--- /dev/null +++ b/.docker/Dockerfile-acala.j2 @@ -0,0 +1,41 @@ +# ===== Rust builder ===== +FROM ubuntu:20.04 as rust-builder +LABEL maintainer="Unique.Network" + +ENV CARGO_HOME="/cargo-home" +ENV PATH="/cargo-home/bin:$PATH" +ENV TZ=UTC +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt-get update && \ + apt-get install -y curl cmake pkg-config libssl-dev git clang llvm libudev-dev && \ + apt-get clean && \ + rm -r /var/lib/apt/lists/* + +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain none + +RUN rustup toolchain uninstall $(rustup toolchain list) && \ + rustup toolchain install {{ RUST_TOOLCHAIN }} && \ + rustup default {{ RUST_TOOLCHAIN }} && \ + rustup target list --installed && \ + rustup show +RUN rustup target add wasm32-unknown-unknown --toolchain {{ RUST_TOOLCHAIN }} + +RUN mkdir /unique_parachain +WORKDIR /unique_parachain + +# ===== BUILD ACALA ===== +FROM rust-builder as builder-acala-bin + +WORKDIR /unique_parachain + +RUN git clone -b {{ ACALA_BUILD_BRANCH }} --depth 1 https://github.com/AcalaNetwork/Acala.git && \ + cd Acala && \ + make init && \ + make build-release + +# ===== BIN ====== + +FROM ubuntu:20.04 as builder-acala + +COPY --from=builder-acala-bin /unique_parachain/Acala/target/production/acala /unique_parachain/Acala/target/production/acala --- a/.docker/Dockerfile-chain-dev +++ b/.docker/Dockerfile-chain-dev @@ -1,17 +1,19 @@ FROM ubuntu:20.04 +ARG RUST_TOOLCHAIN= +ARG FEATURE= + ENV DEBIAN_FRONTEND=noninteractive ENV TZ=Etc/UTC - -RUN apt-get update && apt-get install -y git curl libssl-dev llvm pkg-config libclang-dev clang git make cmake - +ENV FEATURE=$FEATURE ENV CARGO_HOME="/cargo-home" ENV PATH="/cargo-home/bin:$PATH" -RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain none +RUN echo "$FEATURE\n" && echo "$RUST_TOOLCHAIN\n" -ARG RUST_TOOLCHAIN= -ARG FEATURE= +RUN apt-get update && apt-get install -y git curl libssl-dev llvm pkg-config libclang-dev clang git make cmake + +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain none RUN rustup toolchain uninstall $(rustup toolchain list) && \ rustup toolchain install $RUST_TOOLCHAIN && \ @@ -24,5 +26,6 @@ WORKDIR /dev_chain RUN cargo build --release +RUN echo "$FEATURE" CMD cargo run --release --features=$FEATURE -- --dev -linfo --unsafe-ws-external --rpc-cors=all --unsafe-rpc-external --- /dev/null +++ b/.docker/Dockerfile-cumulus.j2 @@ -0,0 +1,40 @@ +# ===== Rust builder ===== +FROM ubuntu:20.04 as rust-builder +LABEL maintainer="Unique.Network" + +ENV CARGO_HOME="/cargo-home" +ENV PATH="/cargo-home/bin:$PATH" +ENV TZ=UTC +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt-get update && \ + apt-get install -y curl cmake pkg-config libssl-dev git clang llvm libudev-dev && \ + apt-get clean && \ + rm -r /var/lib/apt/lists/* + +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain none + +RUN rustup toolchain uninstall $(rustup toolchain list) && \ + rustup toolchain install {{ RUST_TOOLCHAIN }} && \ + rustup default {{ RUST_TOOLCHAIN }} && \ + rustup target list --installed && \ + rustup show +RUN rustup target add wasm32-unknown-unknown --toolchain {{ RUST_TOOLCHAIN }} + +RUN mkdir /unique_parachain +WORKDIR /unique_parachain + +# ===== BUILD CUMULUS ===== +FROM rust-builder as builder-cumulus-bin + +WORKDIR /unique_parachain + +RUN git clone -b {{ CUMULUS_BUILD_BRANCH }} --depth 1 https://github.com/paritytech/cumulus.git && \ + cd cumulus && \ + cargo build --release + +# ===== BIN ====== + +FROM ubuntu:20.04 as builder-cumulus + +COPY --from=builder-cumulus-bin /unique_parachain/cumulus/target/release/polkadot-parachain /unique_parachain/cumulus/target/release/polkadot-parachain --- /dev/null +++ b/.docker/Dockerfile-moonbeam.j2 @@ -0,0 +1,41 @@ +# ===== Rust builder ===== +FROM ubuntu:20.04 as rust-builder +LABEL maintainer="Unique.Network" + +ENV CARGO_HOME="/cargo-home" +ENV PATH="/cargo-home/bin:$PATH" +ENV TZ=UTC +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt-get update && \ + apt-get install -y curl cmake pkg-config libssl-dev git clang llvm libudev-dev && \ + apt-get clean && \ + rm -r /var/lib/apt/lists/* + +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain none + +RUN rustup toolchain uninstall $(rustup toolchain list) && \ + rustup toolchain install {{ RUST_TOOLCHAIN }} && \ + rustup default {{ RUST_TOOLCHAIN }} && \ + rustup target list --installed && \ + rustup show +RUN rustup target add wasm32-unknown-unknown --toolchain {{ RUST_TOOLCHAIN }} + +RUN mkdir /unique_parachain +WORKDIR /unique_parachain + + +# ===== BUILD MOONBEAM ===== +FROM rust-builder as builder-moonbeam-bin + +WORKDIR /unique_parachain + +RUN git clone -b {{ MOONBEAM_BUILD_BRANCH }} --depth 1 https://github.com/PureStake/moonbeam.git && \ + cd moonbeam && \ + cargo build --release + +# ===== BIN ====== + +FROM ubuntu:20.04 as builder-moonbeam + +COPY --from=builder-moonbeam-bin /unique_parachain/moonbeam/target/release/moonbeam /unique_parachain/moonbeam/target/release/moonbeam \ No newline at end of file --- /dev/null +++ b/.docker/Dockerfile-polkadot.j2 @@ -0,0 +1,40 @@ +# ===== Rust builder ===== +FROM ubuntu:20.04 as rust-builder +LABEL maintainer="Unique.Network" + +ENV CARGO_HOME="/cargo-home" +ENV PATH="/cargo-home/bin:$PATH" +ENV TZ=UTC +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt-get update && \ + apt-get install -y curl cmake pkg-config libssl-dev git clang llvm libudev-dev && \ + apt-get clean && \ + rm -r /var/lib/apt/lists/* + +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain none + +RUN rustup toolchain uninstall $(rustup toolchain list) && \ + rustup toolchain install {{ RUST_TOOLCHAIN }} && \ + rustup default {{ RUST_TOOLCHAIN }} && \ + rustup target list --installed && \ + rustup show +RUN rustup target add wasm32-unknown-unknown --toolchain {{ RUST_TOOLCHAIN }} + +RUN mkdir /unique_parachain +WORKDIR /unique_parachain + +# ===== BUILD POLKADOT ===== +FROM rust-builder as builder-polkadot-bin + +WORKDIR /unique_parachain + +RUN git clone -b {{ POLKADOT_BUILD_BRANCH }} --depth 1 https://github.com/paritytech/polkadot.git && \ + cd polkadot && \ + cargo build --release + +# ===== BIN ====== + +FROM ubuntu:20.04 as builder-polkadot + +COPY --from=builder-polkadot-bin /unique_parachain/polkadot/target/release/polkadot /unique_parachain/polkadot/target/release/polkadot --- a/.docker/Dockerfile-try-runtime +++ b/.docker/Dockerfile-try-runtime @@ -47,4 +47,4 @@ cargo build --features=$FEATURE --release -CMD cargo run --features=$FEATURE --release -- try-runtime on-runtime-upgrade live --uri $REPLICA_FROM +CMD cargo run --features=try-runtime,$FEATURE --release -- try-runtime on-runtime-upgrade live --uri $REPLICA_FROM --- /dev/null +++ b/.docker/Dockerfile-xcm.j2 @@ -0,0 +1,85 @@ +# ===== Rust builder ===== +FROM ubuntu:20.04 as rust-builder +LABEL maintainer="Unique.Network" + +ENV CARGO_HOME="/cargo-home" +ENV PATH="/cargo-home/bin:$PATH" +ENV TZ=UTC +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt-get update && \ + apt-get install -y curl cmake pkg-config libssl-dev git clang llvm libudev-dev && \ + apt-get clean && \ + rm -r /var/lib/apt/lists/* + +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain none + +RUN rustup toolchain uninstall $(rustup toolchain list) && \ + rustup toolchain install {{ RUST_TOOLCHAIN }} && \ + rustup default {{ RUST_TOOLCHAIN }} && \ + rustup target list --installed && \ + rustup show +RUN rustup target add wasm32-unknown-unknown --toolchain {{ RUST_TOOLCHAIN }} + +RUN mkdir /unique_parachain +WORKDIR /unique_parachain + +# ===== BUILD ====== +FROM rust-builder as builder-unique + +ARG PROFILE=release + +WORKDIR /unique_parachain + +COPY ./xcm-config/launch-config-xcm-{{ NETWORK }}.json ./launch-config-xcm-{{ NETWORK }}.json +COPY ./xcm-config/5validators.jsonnet ./5validators.jsonnet +COPY ./xcm-config/minBondFix.jsonnet ./minBondFix.jsonnet + +RUN git clone -b {{ BRANCH }} https://github.com/UniqueNetwork/unique-chain.git && \ + cd unique-chain && \ + cargo build --features={{ FEATURE }} --$PROFILE + +# ===== RUN ====== + +FROM ubuntu:20.04 + +RUN apt-get -y update && \ + apt-get -y install curl git && \ + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash && \ + export NVM_DIR="$HOME/.nvm" && \ + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \ + nvm install v16.16.0 && \ + nvm use v16.16.0 + +RUN git clone https://github.com/uniquenetwork/polkadot-launch -b {{ POLKADOT_LAUNCH_BRANCH }} + +RUN export NVM_DIR="$HOME/.nvm" && \ + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \ + cd /polkadot-launch && \ + npm install --global yarn && \ + yarn install + +COPY --from=builder-unique /unique_parachain/launch-config-xcm-{{ NETWORK }}.json /polkadot-launch/ +COPY --from=builder-unique /unique_parachain/5validators.jsonnet /polkadot-launch/5validators.jsonnet +COPY --from=builder-unique /unique_parachain/minBondFix.jsonnet /polkadot-launch/minBondFix.jsonnet + +COPY --from=builder-unique /unique_parachain/unique-chain/target/release/unique-collator /unique-chain/target/release/ + +COPY --from=uniquenetwork/builder-polkadot:{{ POLKADOT_BUILD_BRANCH }} /unique_parachain/polkadot/target/release/polkadot /polkadot/target/release/ +COPY --from=uniquenetwork/builder-moonbeam:{{ MOONBEAM_BUILD_BRANCH }} /unique_parachain/moonbeam/target/release/moonbeam /moonbeam/target/release/ +COPY --from=uniquenetwork/builder-cumulus:{{ CUMULUS_BUILD_BRANCH }} /unique_parachain/cumulus/target/release/polkadot-parachain /cumulus/target/release/cumulus +COPY --from=uniquenetwork/builder-acala:{{ ACALA_BUILD_BRANCH }} /unique_parachain/Acala/target/production/acala /acala/target/release/ +COPY --from=uniquenetwork/builder-chainql:latest /chainql/target/release/chainql /chainql/target/release/ + +EXPOSE 9844 +EXPOSE 9933 +EXPOSE 9944 +EXPOSE 9946 +EXPOSE 9947 +EXPOSE 9948 + +CMD export NVM_DIR="$HOME/.nvm" PATH="$PATH:/chainql/target/release" && \ + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \ + cd /polkadot-launch && \ + yarn start launch-config-xcm-{{ NETWORK }}.json + --- a/.docker/docker-compose.tmp-dev.j2 +++ b/.docker/docker-compose.tmp-dev.j2 @@ -18,4 +18,4 @@ options: max-size: "1m" max-file: "3" - command: cargo run --release --features=$FEATURE -- --dev -linfo --unsafe-ws-external --rpc-cors=all --unsafe-rpc-external + command: cargo run --release --features={{ FEATURE }} -- --dev -linfo --unsafe-ws-external --rpc-cors=all --unsafe-rpc-external --- /dev/null +++ b/.docker/docker-compose.tmp-xcm-tests.j2 @@ -0,0 +1,25 @@ +version: "3.5" + +services: + xcm_nodes: + image: uniquenetwork/xcm-{{ NETWORK }}-testnet-local:latest + container_name: xcm-{{ NETWORK }}-testnet-local + expose: + - 9844 + - 9933 + - 9944 + - 9946 + - 9947 + - 9948 + ports: + - 127.0.0.1:9844:9844 + - 127.0.0.1:9933:9933 + - 127.0.0.1:9944:9944 + - 127.0.0.1:9946:9946 + - 127.0.0.1:9947:9947 + - 127.0.0.1:9948:9948 + logging: + options: + max-size: "1m" + max-file: "3" + --- /dev/null +++ b/.docker/xcm-config/5validators.jsonnet @@ -0,0 +1,50 @@ + +function(spec) + spec { + genesis+: { + runtime+: { + staking+: { + validatorCount: 5, + invulnerables: [ + '5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY', + '5HpG9w8EBLe5XCrbczpwq5TSXvedjrBGCwqxK1iQ7qUsSWFc', + '5Ck5SLSHYac6WFt5UZRSsdJjwmpSZq85fd5TRNAdZQVzEAPT', + '5HKPmK9GYtE1PSLsS1qiYU9xQ9Si1NcEhdeCq9sw5bqu4ns8', + '5FCfAonRZgTFrTd9HREEyeJjDpT397KMzizE6T3DvebLFE7n', + ], + stakers: [ + [ + '5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY', + '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', + 100000000000000, + 'Validator', + ], + [ + '5HpG9w8EBLe5XCrbczpwq5TSXvedjrBGCwqxK1iQ7qUsSWFc', + '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty', + 100000000000000, + 'Validator', + ], + [ + '5Ck5SLSHYac6WFt5UZRSsdJjwmpSZq85fd5TRNAdZQVzEAPT', + '5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y', + 100000000000000, + 'Validator', + ], + [ + '5HKPmK9GYtE1PSLsS1qiYU9xQ9Si1NcEhdeCq9sw5bqu4ns8', + '5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy', + 100000000000000, + 'Validator', + ], + [ + '5FCfAonRZgTFrTd9HREEyeJjDpT397KMzizE6T3DvebLFE7n', + '5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw', + 100000000000000, + 'Validator', + ], + ], + }, + }, + }, + } --- /dev/null +++ b/.docker/xcm-config/launch-config-xcm-opal.json @@ -0,0 +1,134 @@ +{ + "relaychain": { + "bin": "/polkadot/target/release/polkadot", + "chain": "westend-local", + "chainInitializer": [ + "chainql", + "--tla-code=spec=import '${spec}'", + "5validators.jsonnet" + ], + "nodes": [ + { + "name": "alice", + "wsPort": 9844, + "rpcPort": 9843, + "port": 30444, + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lparachain::candidate_validation=debug" + ] + }, + { + "name": "bob", + "wsPort": 9855, + "rpcPort": 9854, + "port": 30555, + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lparachain::candidate_validation=debug" + ] + }, + { + "name": "charlie", + "wsPort": 9866, + "rpcPort": 9865, + "port": 30666, + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lparachain::candidate_validation=debug" + ] + }, + { + "name": "dave", + "wsPort": 9877, + "rpcPort": 9876, + "port": 30777, + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lparachain::candidate_validation=debug" + ] + }, + { + "name": "eve", + "wsPort": 9888, + "rpcPort": 9887, + "port": 30888, + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lparachain::candidate_validation=debug" + ] + } + + ], + "genesis": { + "runtime": { + "runtime_genesis_config": { + "parachainsConfiguration": { + "config": { + "validation_upgrade_frequency": 1, + "validation_upgrade_delay": 1 + } + } + } + } + } + }, + "parachains": [ + { + "bin": "/unique-chain/target/release/unique-collator", + "id": "2095", + "balance": "1000000000000000000000000", + "nodes": [ + { + "port": 31200, + "wsPort": 9944, + "rpcPort": 9933, + "name": "alice", + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external" + ] + } + ] + }, + { + "bin": "/cumulus/target/release/cumulus", + "id": "1000", + "chain": "westmint-local", + "balance": "1000000000000000000000000", + "nodes": [ + { + "wsPort": 9948, + "port": 31204, + "name": "alice", + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external" + ] + } + ] + } + ], + "simpleParachains": [], + "hrmpChannels": [ + { + "sender": 2095, + "recipient": 1000, + "maxCapacity": 8, + "maxMessageSize": 512 + }, + { + "sender": 1000, + "recipient": 2095, + "maxCapacity": 8, + "maxMessageSize": 512 + } + ], + "finalization": false +} + --- /dev/null +++ b/.docker/xcm-config/launch-config-xcm-quartz.json @@ -0,0 +1,199 @@ +{ + "relaychain": { + "bin": "/polkadot/target/release/polkadot", + "chain": "westend-local", + "chainInitializer": [ + "chainql", + "--tla-code=spec=import '${spec}'", + "5validators.jsonnet" + ], + "nodes": [ + { + "name": "alice", + "wsPort": 9844, + "rpcPort": 9843, + "port": 30444, + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lparachain::candidate_validation=debug" + ] + }, + { + "name": "bob", + "wsPort": 9855, + "rpcPort": 9854, + "port": 30555, + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lparachain::candidate_validation=debug" + ] + }, + { + "name": "charlie", + "wsPort": 9866, + "rpcPort": 9865, + "port": 30666, + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lparachain::candidate_validation=debug" + ] + }, + { + "name": "dave", + "wsPort": 9877, + "rpcPort": 9876, + "port": 30777, + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lparachain::candidate_validation=debug" + ] + }, + { + "name": "eve", + "wsPort": 9888, + "rpcPort": 9887, + "port": 30888, + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lparachain::candidate_validation=debug" + ] + } + + ], + "genesis": { + "runtime": { + "runtime_genesis_config": { + "parachainsConfiguration": { + "config": { + "validation_upgrade_frequency": 1, + "validation_upgrade_delay": 1 + } + } + } + } + } + }, + "parachains": [ + { + "bin": "/unique-chain/target/release/unique-collator", + "id": "2095", + "balance": "1000000000000000000000000", + "nodes": [ + { + "port": 31200, + "wsPort": 9944, + "rpcPort": 9933, + "name": "alice", + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external" + ] + } + ] + }, + { + "bin": "/acala/target/release/acala", + "id": "2000", + "chain": "karura-dev", + "balance": "1000000000000000000000", + "nodes": [ + { + "wsPort": 9946, + "port": 31202, + "name": "alice", + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external" + ] + } + ] + }, + { + "bin": "/moonbeam/target/release/moonbeam", + "id": 2023, + "balance": "1000000000000000000000", + "chain": "moonriver-local", + "chainInitializer": [ + "chainql", + "--tla-code=spec=import '${spec}'", + "minBondFix.jsonnet" + ], + "nodes": [ + { + "wsPort": 9947, + "port": 31203, + "name": "alice", + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "--", + "--execution=wasm" + ] + } + ] + }, + { + "bin": "/cumulus/target/release/cumulus", + "id": "1000", + "chain": "statemine-local", + "balance": "1000000000000000000000000", + "nodes": [ + { + "wsPort": 9948, + "port": 31204, + "name": "alice", + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external" + ] + } + ] + } + ], + "simpleParachains": [], + "hrmpChannels": [ + { + "sender": 2095, + "recipient": 2000, + "maxCapacity": 8, + "maxMessageSize": 512 + }, + { + "sender": 2000, + "recipient": 2095, + "maxCapacity": 8, + "maxMessageSize": 512 + }, + { + "sender": 2095, + "recipient": 2023, + "maxCapacity": 8, + "maxMessageSize": 512 + }, + { + "sender": 2023, + "recipient": 2095, + "maxCapacity": 8, + "maxMessageSize": 512 + }, + { + "sender": 2095, + "recipient": 1000, + "maxCapacity": 8, + "maxMessageSize": 512 + }, + { + "sender": 1000, + "recipient": 2095, + "maxCapacity": 8, + "maxMessageSize": 512 + } + ], + "finalization": false +} + --- /dev/null +++ b/.docker/xcm-config/launch-config-xcm-unique-rococo.json @@ -0,0 +1,207 @@ +{ + "relaychain": { + "bin": "/polkadot/target/release/polkadot", + "chain": "rococo-local", + "chainInitializer": [ + "chainql", + "--tla-code=spec=import '${spec}'", + "5validators.jsonnet" + ], + "nodes": [ + { + "name": "alice", + "wsPort": 9844, + "rpcPort": 9843, + "port": 30444, + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lparachain::candidate_validation=debug" + ] + }, + { + "name": "bob", + "wsPort": 9855, + "rpcPort": 9854, + "port": 30555, + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lparachain::candidate_validation=debug" + ] + }, + { + "name": "charlie", + "wsPort": 9866, + "rpcPort": 9865, + "port": 30666, + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lparachain::candidate_validation=debug" + ] + }, + { + "name": "dave", + "wsPort": 9877, + "rpcPort": 9876, + "port": 30777, + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lparachain::candidate_validation=debug" + ] + }, + { + "name": "eve", + "wsPort": 9888, + "rpcPort": 9887, + "port": 30888, + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lparachain::candidate_validation=debug" + ] + } + + ], + "genesis": { + "runtime": { + "runtime_genesis_config": { + "parachainsConfiguration": { + "config": { + "validation_upgrade_frequency": 1, + "validation_upgrade_delay": 1 + } + } + } + } + } + }, + "parachains": [ + { + "bin": "/unique-chain/target/release/unique-collator", + "id": "2037", + "balance": "1000000000000000000000000", + "nodes": [ + { + "port": 31200, + "wsPort": 9944, + "rpcPort": 9933, + "name": "alice", + "flags": [ + "-lruntime=trace", + "-lxcm=trace", + "--unsafe-rpc-external", + "--unsafe-ws-external" + ] + } + ] + }, + { + "bin": "/acala/target/release/acala", + "id": "2000", + "chain": "acala-dev", + "balance": "1000000000000000000000", + "chainInitializer": [ + "chainql", + "-e", + "(import '${spec}') {id+: '-local'}" + ], + "nodes": [ + { + "wsPort": 9946, + "port": 31202, + "name": "alice", + "flags": [ + "-lruntime=trace", + "-lxcm=trace", + "--unsafe-rpc-external", + "--unsafe-ws-external" + ] + } + ] + }, + { + "bin": "/moonbeam/target/release/moonbeam", + "id": "2004", + "balance": "1000000000000000000000", + "chain": "moonbeam-local", + "nodes": [ + { + "wsPort": 9947, + "port": 31203, + "name": "alice", + "flags": [ + "-lruntime=trace", + "-lxcm=trace", + "--unsafe-rpc-external", + "--unsafe-ws-external", + "--", + "--execution=wasm" + ] + } + ] + }, + { + "bin": "/cumulus/target/release/cumulus", + "id": "1000", + "chain": "statemint-local", + "balance": "1000000000000000000000000", + "nodes": [ + { + "wsPort": 9948, + "port": 31204, + "name": "alice", + "flags": [ + "-lruntime=trace", + "-lxcm=trace", + "--unsafe-rpc-external", + "--unsafe-ws-external" + ] + } + ] + } + ], + "simpleParachains": [], + "hrmpChannels": [ + { + "sender": 2037, + "recipient": 2000, + "maxCapacity": 8, + "maxMessageSize": 512 + }, + { + "sender": 2000, + "recipient": 2037, + "maxCapacity": 8, + "maxMessageSize": 512 + }, + { + "sender": 2037, + "recipient": 2004, + "maxCapacity": 8, + "maxMessageSize": 512 + }, + { + "sender": 2004, + "recipient": 2037, + "maxCapacity": 8, + "maxMessageSize": 512 + }, + { + "sender": 2037, + "recipient": 1000, + "maxCapacity": 8, + "maxMessageSize": 512 + }, + { + "sender": 1000, + "recipient": 2037, + "maxCapacity": 8, + "maxMessageSize": 512 + } + ], + "finalization": false +} + --- /dev/null +++ b/.docker/xcm-config/launch-config-xcm-unique.json @@ -0,0 +1,199 @@ +{ + "relaychain": { + "bin": "/polkadot/target/release/polkadot", + "chain": "westend-local", + "chainInitializer": [ + "chainql", + "--tla-code=spec=import '${spec}'", + "5validators.jsonnet" + ], + "nodes": [ + { + "name": "alice", + "wsPort": 9844, + "rpcPort": 9843, + "port": 30444, + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lparachain::candidate_validation=debug" + ] + }, + { + "name": "bob", + "wsPort": 9855, + "rpcPort": 9854, + "port": 30555, + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lparachain::candidate_validation=debug" + ] + }, + { + "name": "charlie", + "wsPort": 9866, + "rpcPort": 9865, + "port": 30666, + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lparachain::candidate_validation=debug" + ] + }, + { + "name": "dave", + "wsPort": 9877, + "rpcPort": 9876, + "port": 30777, + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lparachain::candidate_validation=debug" + ] + }, + { + "name": "eve", + "wsPort": 9888, + "rpcPort": 9887, + "port": 30888, + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lparachain::candidate_validation=debug" + ] + } + + ], + "genesis": { + "runtime": { + "runtime_genesis_config": { + "parachainsConfiguration": { + "config": { + "validation_upgrade_frequency": 1, + "validation_upgrade_delay": 1 + } + } + } + } + } + }, + "parachains": [ + { + "bin": "/unique-chain/target/release/unique-collator", + "id": "2037", + "balance": "1000000000000000000000000", + "nodes": [ + { + "port": 31200, + "wsPort": 9944, + "rpcPort": 9933, + "name": "alice", + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external" + ] + } + ] + }, + { + "bin": "/acala/target/release/acala", + "id": "2000", + "chain": "acala-dev", + "balance": "1000000000000000000000", + "chainInitializer": [ + "chainql", + "-e", + "(import '${spec}') {id+: '-local'}" + ], + "nodes": [ + { + "wsPort": 9946, + "port": 31202, + "name": "alice", + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external" + ] + } + ] + }, + { + "bin": "/moonbeam/target/release/moonbeam", + "id": "2004", + "balance": "1000000000000000000000", + "chain": "moonbeam-local", + "nodes": [ + { + "wsPort": 9947, + "port": 31203, + "name": "alice", + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external", + "--", + "--execution=wasm" + ] + } + ] + }, + { + "bin": "/cumulus/target/release/cumulus", + "id": "1000", + "chain": "statemint-local", + "balance": "1000000000000000000000000", + "nodes": [ + { + "wsPort": 9948, + "port": 31204, + "name": "alice", + "flags": [ + "--unsafe-rpc-external", + "--unsafe-ws-external" + ] + } + ] + } + ], + "simpleParachains": [], + "hrmpChannels": [ + { + "sender": 2037, + "recipient": 2000, + "maxCapacity": 8, + "maxMessageSize": 512 + }, + { + "sender": 2000, + "recipient": 2037, + "maxCapacity": 8, + "maxMessageSize": 512 + }, + { + "sender": 2037, + "recipient": 2004, + "maxCapacity": 8, + "maxMessageSize": 512 + }, + { + "sender": 2004, + "recipient": 2037, + "maxCapacity": 8, + "maxMessageSize": 512 + }, + { + "sender": 2037, + "recipient": 1000, + "maxCapacity": 8, + "maxMessageSize": 512 + }, + { + "sender": 1000, + "recipient": 2037, + "maxCapacity": 8, + "maxMessageSize": 512 + } + ], + "finalization": false +} + --- /dev/null +++ b/.docker/xcm-config/minBondFix.jsonnet @@ -0,0 +1,10 @@ +function(spec) +spec { + genesis+: { + runtime+: { + parachainStaking+: { + candidates: std.map(function(candidate) [candidate[0], candidate[1] * 1000], super.candidates) + }, + }, + }, +} --- a/.env +++ b/.env @@ -12,3 +12,16 @@ OPAL_REPLICA_FROM=wss://eu-ws-opal.unique.network:443 QUARTZ_REPLICA_FROM=wss://eu-ws-quartz.unique.network:443 UNIQUE_REPLICA_FROM=wss://eu-ws.unique.network:443 + +POLKADOT_LAUNCH_BRANCH=feature/rewrite-chain-id-in-spec + +KARURA_BUILD_BRANCH=2.9.1 +ACALA_BUILD_BRANCH=2.9.2 + +MOONRIVER_BUILD_BRANCH=runtime-1701 +MOONBEAM_BUILD_BRANCH=runtime-1701 + +STATEMINE_BUILD_BRANCH=parachains-v9270 +STATEMINT_BUILD_BRANCH=release-parachains-v9230 +WESTMINT_BUILD_BRANCH=parachains-v9270 + --- a/.github/workflows/dev-build-tests_v2.yml +++ b/.github/workflows/dev-build-tests_v2.yml @@ -71,8 +71,8 @@ run: | yarn install yarn add mochawesome + node scripts/readyness.js echo "Ready to start tests" - node scripts/readyness.js yarn polkadot-types NOW=$(date +%s) && yarn test --reporter mochawesome --reporter-options reportFilename=test-${NOW} env: @@ -95,3 +95,10 @@ - name: Stop running containers if: always() # run this step always run: docker-compose -f ".docker/docker-compose-dev.yaml" -f ".docker/docker-compose.${{ matrix.network }}.yml" down + + - name: Remove builder cache + if: always() # run this step always + run: | + docker builder prune -f -a + docker system prune -f + docker image prune -f -a \ No newline at end of file --- a/.github/workflows/market-test_v2.yml +++ b/.github/workflows/market-test_v2.yml @@ -45,7 +45,7 @@ repository: 'UniqueNetwork/market-e2e-tests' ssh-key: ${{ secrets.GH_PAT }} path: 'qa-tests' - ref: 'ci_test_v2' + ref: 'master' - name: Read .env file uses: xom9ikk/dotenv@v1.0.2 @@ -142,7 +142,6 @@ with: path: qa-tests/ - - name: local-market:start run: docker-compose -f "qa-tests/.docker/docker-compose.market.yml" -f "qa-tests/.docker/docker-compose.${{ matrix.network }}.yml" up -d --build @@ -175,7 +174,6 @@ - name: Stop running containers if: always() # run this step always run: docker-compose -f "qa-tests/.docker/docker-compose.market.yml" -f "qa-tests/.docker/docker-compose.${{ matrix.network }}.yml" down --volumes -# run: docker-compose -f "qa-tests/.docker/docker-compose.market.yml" -f "qa-tests/.docker/docker-compose.${{ matrix.network }}.yml" down - name: Remove builder cache if: always() # run this step always --- a/.github/workflows/node-only-update_v2.yml +++ b/.github/workflows/node-only-update_v2.yml @@ -10,7 +10,7 @@ # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: - execution-marix: + nodes-execution-matrix: name: execution matrix @@ -42,8 +42,8 @@ - forkless-update-nodata: - needs: execution-marix + nodes-only-update: + needs: nodes-execution-matrix # The type of runner that the job will run on runs-on: [self-hosted-ci,large] @@ -57,7 +57,7 @@ strategy: matrix: - include: ${{fromJson(needs.prepare-execution-marix.outputs.matrix)}} + include: ${{fromJson(needs.nodes-execution-matrix.outputs.matrix)}} steps: @@ -156,12 +156,18 @@ exit 0 shell: bash + - name: Checkout at '${{ matrix.mainnet_branch }}' branch + uses: actions/checkout@master + with: + ref: ${{ matrix.mainnet_branch }} #Checking out head commit + path: ${{ matrix.mainnet_branch }} + + - name: Run tests before Node Parachain upgrade - working-directory: tests + working-directory: ${{ matrix.mainnet_branch }}/tests run: | yarn install yarn add mochawesome - node scripts/readyness.js echo "Ready to start tests" yarn polkadot-types NOW=$(date +%s) && yarn test --reporter mochawesome --reporter-options reportFilename=test-${NOW} @@ -174,7 +180,7 @@ if: success() || failure() # run this step even if previous step failed with: name: Tests before node upgrade ${{ matrix.network }} # Name of the check run which will be created - path: tests/mochawesome-report/test-*.json # Path to test results + path: ${{ matrix.mainnet_branch }}/tests/mochawesome-report/test-*.json # Path to test results reporter: mochawesome-json fail-on-error: 'false' @@ -263,8 +269,9 @@ - name: Remove builder cache if: always() # run this step always run: | - docker builder prune -f + docker builder prune -f -a docker system prune -f + docker image prune -f -a - name: Clean Workspace if: always() --- a/.github/workflows/try-runtime_v2.yml +++ b/.github/workflows/try-runtime_v2.yml @@ -1,7 +1,6 @@ on: workflow_call: - # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: try-runtime: --- a/.github/workflows/xcm-testnet-build.yml +++ /dev/null @@ -1,151 +0,0 @@ -name: xcm-testnet-build - -# Controls when the action will run. -on: - workflow_call: - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -#Define Workflow variables -env: - REPO_URL: ${{ github.server_url }}/${{ github.repository }} - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - - prepare-execution-marix: - - name: Prepare execution matrix - - runs-on: [XL] - outputs: - matrix: ${{ steps.create_matrix.outputs.matrix }} - - steps: - - - name: Clean Workspace - uses: AutoModality/action-clean@v1.1.0 - - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 - with: - ref: ${{ github.head_ref }} #Checking out head commit - - - name: Read .env file - uses: xom9ikk/dotenv@v1.0.2 - - - name: Create Execution matrix - uses: fabiocaccamo/create-matrix-action@v2 - id: create_matrix - with: - matrix: | - network {opal}, runtime {opal}, features {opal-runtime}, acala_version {${{ env.ACALA_BUILD_BRANCH }}}, moonbeam_version {${{ env.MOONBEAM_BUILD_BRANCH }}}, cumulus_version {${{ env.WESTMINT_BUILD_BRANCH }}} - network {quartz}, runtime {quartz}, features {quartz-runtime}, acala_version {${{ env.KARURA_BUILD_BRANCH }}}, moonbeam_version {${{ env.MOONRIVER_BUILD_BRANCH }}}, cumulus_version {${{ env.STATEMINE_BUILD_BRANCH }}} - network {unique}, runtime {unique}, features {unique-runtime}, acala_version {${{ env.ACALA_BUILD_BRANCH }}}, moonbeam_version {${{ env.MOONBEAM_BUILD_BRANCH }}}, cumulus_version {${{ env.STATEMINT_BUILD_BRANCH }}} - - xcm-build: - - needs: prepare-execution-marix - # The type of runner that the job will run on - runs-on: [XL] - - timeout-minutes: 600 - - name: ${{ matrix.network }} - - continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false. - - strategy: - matrix: - include: ${{fromJson(needs.prepare-execution-marix.outputs.matrix)}} - - steps: - - name: Skip if pull request is in Draft - if: github.event.pull_request.draft == true - run: exit 1 - - - name: Clean Workspace - uses: AutoModality/action-clean@v1.1.0 - - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 - with: - ref: ${{ github.head_ref }} #Checking out head commit - - - name: Read .env file - uses: xom9ikk/dotenv@v1.0.2 - - - name: Generate ENV related extend Dockerfile file - uses: cuchi/jinja2-action@v1.2.0 - with: - template: .docker/Dockerfile-xcm.j2 - output_file: .docker/Dockerfile-xcm.${{ matrix.network }}.yml - variables: | - RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} - NETWORK=${{ matrix.network }} - POLKADOT_BUILD_BRANCH=${{ env.POLKADOT_BUILD_BRANCH }} - POLKADOT_LAUNCH_BRANCH=${{ env.POLKADOT_LAUNCH_BRANCH }} - FEATURE=${{ matrix.features }} - RUNTIME=${{ matrix.runtime }} - BRANCH=${{ github.head_ref }} - ACALA_BUILD_BRANCH=${{ matrix.acala_version }} - MOONBEAM_BUILD_BRANCH=${{ matrix.moonbeam_version }} - CUMULUS_BUILD_BRANCH=${{ matrix.cumulus_version }} - - - name: Show build Dockerfile - run: cat .docker/Dockerfile-xcm.${{ matrix.network }}.yml - - - name: Show launch-config-xcm-${{ matrix.network }} configuration - run: cat .docker/xcm-config/launch-config-xcm-${{ matrix.network }}.json - - - name: Run find-and-replace to remove slashes from branch name - uses: mad9000/actions-find-and-replace-string@2 - id: branchname - with: - source: ${{ github.head_ref }} - find: '/' - replace: '-' - - - name: Log in to Docker Hub - uses: docker/login-action@v2.0.0 - with: - username: ${{ secrets.CORE_DOCKERHUB_USERNAME }} - password: ${{ secrets.CORE_DOCKERHUB_TOKEN }} - - - name: Pull acala docker image - run: docker pull uniquenetwork/builder-acala:${{ matrix.acala_version }} - - - name: Pull moonbeam docker image - run: docker pull uniquenetwork/builder-moonbeam:${{ matrix.moonbeam_version }} - - - name: Pull cumulus docker image - run: docker pull uniquenetwork/builder-cumulus:${{ matrix.cumulus_version }} - - - name: Pull polkadot docker image - run: docker pull uniquenetwork/builder-polkadot:${{ env.POLKADOT_BUILD_BRANCH }} - - - name: Pull chainql docker image - run: docker pull uniquenetwork/builder-chainql:latest - - - name: Build the stack - run: cd .docker/ && docker build --no-cache --file ./Dockerfile-xcm.${{ matrix.network }}.yml --tag uniquenetwork/xcm-${{ matrix.network }}-testnet-local:nightly-${{ steps.branchname.outputs.value }}-${{ github.sha }} --tag uniquenetwork/xcm-${{ matrix.network }}-testnet-local:latest . - - - name: Push docker image version - run: docker push uniquenetwork/xcm-${{ matrix.network }}-testnet-local:nightly-${{ steps.branchname.outputs.value }}-${{ github.sha }} - - - name: Push docker image latest - run: docker push uniquenetwork/xcm-${{ matrix.network }}-testnet-local:latest - - - name: Clean Workspace - if: always() - uses: AutoModality/action-clean@v1.1.0 - - - name: Remove builder cache - if: always() # run this step always - run: | - docker builder prune -f - docker system prune -f - - - --- a/.github/workflows/xcm-tests_v2.yml +++ /dev/null @@ -1,175 +0,0 @@ -name: xcm-tests - -# Controls when the action will run. -on: - # Allows you to run this workflow manually from the Actions tab - workflow_call: - -#Define Workflow variables -env: - REPO_URL: ${{ github.server_url }}/${{ github.repository }} - - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - - prepare-execution-marix: - - name: Prepare execution matrix - -# needs: [xcm-testnet-build] - - runs-on: XL - outputs: - matrix: ${{ steps.create_matrix.outputs.matrix }} - - steps: - - - name: Clean Workspace - uses: AutoModality/action-clean@v1.1.0 - - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 - with: - ref: ${{ github.head_ref }} #Checking out head commit - - - name: Read .env file - uses: xom9ikk/dotenv@v1.0.2 - - - name: Create Execution matrix - uses: fabiocaccamo/create-matrix-action@v2 - id: create_matrix - with: - matrix: | - network {opal}, runtime {opal}, features {opal-runtime}, runtest {testXcmOpal} - network {quartz}, runtime {quartz}, features {quartz-runtime}, runtest {testXcmQuartz} - network {unique}, runtime {unique}, features {unique-runtime}, runtest {testXcmUnique} - - xcm-tests: - needs: prepare-execution-marix - # The type of runner that the job will run on - runs-on: [XL] - - timeout-minutes: 600 - - name: ${{ matrix.network }} - - continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false. - - strategy: - matrix: - include: ${{fromJson(needs.prepare-execution-marix.outputs.matrix)}} - - - steps: - - name: Skip if pull request is in Draft - if: github.event.pull_request.draft == true - run: exit 1 - - - name: Clean Workspace - uses: AutoModality/action-clean@v1.1.0 - - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 - with: - ref: ${{ github.head_ref }} #Checking out head commit - - - name: Read .env file - uses: xom9ikk/dotenv@v1.0.2 - - - name: Generate ENV related extend file for docker-compose - uses: cuchi/jinja2-action@v1.2.0 - with: - template: .docker/docker-compose.tmp-xcm-tests.j2 - output_file: .docker/docker-compose.xcm-tests.${{ matrix.network }}.yml - variables: | - NETWORK=${{ matrix.network }} - - - name: Show build configuration - run: cat .docker/docker-compose.xcm-tests.${{ matrix.network }}.yml - - - uses: actions/setup-node@v3 - with: - node-version: 16 - - - name: Build the stack - run: docker-compose -f ".docker/docker-compose.xcm-tests.${{ matrix.network }}.yml" up -d --remove-orphans --force-recreate --timeout 300 - - # 🚀 POLKADOT LAUNCH COMPLETE 🚀 - - name: Check if docker logs consist messages related to testing of xcm tests - if: success() - run: | - counter=160 - function check_container_status { - docker inspect -f {{.State.Running}} xcm-${{ matrix.network }}-testnet-local - } - function do_docker_logs { - docker logs --details xcm-${{ matrix.network }}-testnet-local 2>&1 - } - function is_started { - if [ "$(check_container_status)" == "true" ]; then - echo "Container: xcm-${{ matrix.network }}-testnet-local RUNNING"; - echo "Check Docker logs" - DOCKER_LOGS=$(do_docker_logs) - if [[ ${DOCKER_LOGS} = *"POLKADOT LAUNCH COMPLETE"* ]];then - echo "🚀 POLKADOT LAUNCH COMPLETE 🚀" - return 0 - else - echo "Message not found in logs output, repeating..." - return 1 - fi - else - echo "Container xcm-${{ matrix.network }}-testnet-local NOT RUNNING" - echo "Halting all future checks" - exit 1 - fi - echo "something goes wrong" - exit 1 - } - while ! is_started; do - echo "Waiting for special message in log files " - sleep 30s - counter=$(( $counter - 1 )) - echo "Counter: $counter" - if [ "$counter" -gt "0" ]; then - continue - else - break - fi - done - echo "Halting script" - exit 0 - shell: bash - - - name: Run XCM tests - working-directory: tests - run: | - yarn install - yarn add mochawesome - node scripts/readyness.js - echo "Ready to start tests" - NOW=$(date +%s) && yarn ${{ matrix.runtest }} --reporter mochawesome --reporter-options reportFilename=test-${NOW} - - - name: XCM Test Report - uses: phoenix-actions/test-reporting@v8 - id: test-report - if: success() || failure() # run this step even if previous step failed - with: - name: XCM Tests ${{ matrix.network }} # Name of the check run which will be created - path: tests/mochawesome-report/test-*.json # Path to test results - reporter: mochawesome-json - fail-on-error: 'false' - - - name: Stop running containers - if: always() # run this step always - run: docker-compose -f ".docker/docker-compose.xcm-tests.${{ matrix.network }}.yml" down - - - name: Clean Workspace - if: always() - uses: AutoModality/action-clean@v1.1.0 - - - name: Remove builder cache - if: always() # run this step always - run: | - docker system prune -a -f - --- a/.github/workflows/xcm.yml +++ b/.github/workflows/xcm.yml @@ -1,19 +1,400 @@ -name: Nesting XCM +name: xcm-testnet-build +# Controls when the action will run. on: workflow_call: + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: +#Define Workflow variables +env: + REPO_URL: ${{ github.server_url }}/${{ github.repository }} +# A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: - xcm-testnet-build: - name: testnet build - uses: ./.github/workflows/xcm-testnet-build.yml - secrets: inherit + prepare-execution-marix: + + name: Prepare execution matrix + + runs-on: [XL] + outputs: + matrix: ${{ steps.create_matrix.outputs.matrix }} + + steps: + + - name: Clean Workspace + uses: AutoModality/action-clean@v1.1.0 + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} #Checking out head commit + + - name: Read .env file + uses: xom9ikk/dotenv@v1.0.2 + + - name: Create Execution matrix + uses: fabiocaccamo/create-matrix-action@v2 + id: create_matrix + with: + matrix: | + network {opal}, runtime {opal}, features {opal-runtime}, acala_version {${{ env.ACALA_BUILD_BRANCH }}}, moonbeam_version {${{ env.MOONBEAM_BUILD_BRANCH }}}, cumulus_version {${{ env.WESTMINT_BUILD_BRANCH }}}, runtest {testXcmOpal} + network {quartz}, runtime {quartz}, features {quartz-runtime}, acala_version {${{ env.KARURA_BUILD_BRANCH }}}, moonbeam_version {${{ env.MOONRIVER_BUILD_BRANCH }}}, cumulus_version {${{ env.STATEMINE_BUILD_BRANCH }}}, runtest {testXcmQuartz} + network {unique}, runtime {unique}, features {unique-runtime}, acala_version {${{ env.ACALA_BUILD_BRANCH }}}, moonbeam_version {${{ env.MOONBEAM_BUILD_BRANCH }}}, cumulus_version {${{ env.STATEMINT_BUILD_BRANCH }}}, runtest {testXcmUnique} + + xcm-build: + + needs: prepare-execution-marix + # The type of runner that the job will run on + runs-on: [XL] + + timeout-minutes: 600 + + name: ${{ matrix.network }} + + continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false. + + strategy: + matrix: + include: ${{fromJson(needs.prepare-execution-marix.outputs.matrix)}} + + steps: + - name: Skip if pull request is in Draft + if: github.event.pull_request.draft == true + run: exit 1 + + - name: Clean Workspace + uses: AutoModality/action-clean@v1.1.0 + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} #Checking out head commit + + - name: Read .env file + uses: xom9ikk/dotenv@v1.0.2 + + - name: Log in to Docker Hub + uses: docker/login-action@v2.0.0 + with: + username: ${{ secrets.CORE_DOCKERHUB_USERNAME }} + password: ${{ secrets.CORE_DOCKERHUB_TOKEN }} + + - name: Install jq + run: sudo apt install jq -y + + # Check POLKADOT version and build it if it doesn't exist in repository + - name: Generate ENV related extend Dockerfile file for POLKADOT + uses: cuchi/jinja2-action@v1.2.0 + with: + template: .docker/Dockerfile-polkadot.j2 + output_file: .docker/Dockerfile-polkadot.${{ env.POLKADOT_BUILD_BRANCH }}.yml + variables: | + RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} + POLKADOT_BUILD_BRANCH=${{ env.POLKADOT_BUILD_BRANCH }} + + - name: Check if the dockerhub repository contains the needed version POLKADOT + run: | + # aquire token + TOKEN=$(curl -s -H "Content-Type: application/json" -X POST -d '{"username": "'${{ secrets.CORE_DOCKERHUB_USERNAME }}'", "password": "'${{ secrets.CORE_DOCKERHUB_TOKEN }}'"}' https://hub.docker.com/v2/users/login/ | jq -r .token) + export TOKEN=$TOKEN + # Get TAGS from DOCKERHUB POLKADOT repository + POLKADOT_TAGS=$(curl -s -H "Authorization: JWT ${TOKEN}" https://hub.docker.com/v2/repositories/uniquenetwork/builder-polkadot/tags/?page_size=100 | jq -r '."results"[]["name"]') + # Show TAGS + echo "POLKADOT TAGS:" + echo $POLKADOT_TAGS + # Check correct version POLKADOT and build it if it doesn't exist in POLKADOT TAGS + if [[ ${POLKADOT_TAGS[*]} =~ (^|[[:space:]])"${{ env.POLKADOT_BUILD_BRANCH }}"($|[[:space:]]) ]]; then + echo "Repository has needed POLKADOT version"; + docker pull uniquenetwork/builder-polkadot:${{ env.POLKADOT_BUILD_BRANCH }} + else + echo "Repository has not needed POLKADOT version, so build it"; + cd .docker/ && docker build --no-cache --file ./Dockerfile-polkadot.${{ env.POLKADOT_BUILD_BRANCH }}.yml --tag uniquenetwork/builder-polkadot:${{ env.POLKADOT_BUILD_BRANCH }} . + echo "Push needed POLKADOT version to the repository"; + docker push uniquenetwork/builder-polkadot:${{ env.POLKADOT_BUILD_BRANCH }} + fi + shell: bash + + # Check ACALA version and build it if it doesn't exist in repository + - name: Generate ENV related extend Dockerfile file for ACALA + uses: cuchi/jinja2-action@v1.2.0 + with: + template: .docker/Dockerfile-acala.j2 + output_file: .docker/Dockerfile-acala.${{ matrix.acala_version }}.yml + variables: | + RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} + ACALA_BUILD_BRANCH=${{ matrix.acala_version }} + + - name: Check if the dockerhub repository contains the needed ACALA version + run: | + # aquire token + TOKEN=$(curl -s -H "Content-Type: application/json" -X POST -d '{"username": "'${{ secrets.CORE_DOCKERHUB_USERNAME }}'", "password": "'${{ secrets.CORE_DOCKERHUB_TOKEN }}'"}' https://hub.docker.com/v2/users/login/ | jq -r .token) + export TOKEN=$TOKEN + + # Get TAGS from DOCKERHUB repository + ACALA_TAGS=$(curl -s -H "Authorization: JWT ${TOKEN}" https://hub.docker.com/v2/repositories/uniquenetwork/builder-acala/tags/?page_size=100 | jq -r '."results"[]["name"]') + # Show TAGS + echo "ACALA TAGS:" + echo $ACALA_TAGS + # Check correct version ACALA and build it if it doesn't exist in ACALA TAGS + if [[ ${ACALA_TAGS[*]} =~ (^|[[:space:]])"${{ matrix.acala_version }}"($|[[:space:]]) ]]; then + echo "Repository has needed ACALA version"; + docker pull uniquenetwork/builder-acala:${{ matrix.acala_version }} + else + echo "Repository has not needed ACALA version, so build it"; + cd .docker/ && docker build --no-cache --file ./Dockerfile-acala.${{ matrix.acala_version }}.yml --tag uniquenetwork/builder-acala:${{ matrix.acala_version }} . + echo "Push needed ACALA version to the repository"; + docker push uniquenetwork/builder-acala:${{ matrix.acala_version }} + fi + shell: bash + + # Check MOONBEAM version and build it if it doesn't exist in repository + - name: Generate ENV related extend Dockerfile file for MOONBEAM + uses: cuchi/jinja2-action@v1.2.0 + with: + template: .docker/Dockerfile-moonbeam.j2 + output_file: .docker/Dockerfile-moonbeam.${{ matrix.moonbeam_version }}.yml + variables: | + RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} + MOONBEAM_BUILD_BRANCH=${{ matrix.moonbeam_version }} + + - name: Check if the dockerhub repository contains the needed MOONBEAM version + run: | + # aquire token + TOKEN=$(curl -s -H "Content-Type: application/json" -X POST -d '{"username": "'${{ secrets.CORE_DOCKERHUB_USERNAME }}'", "password": "'${{ secrets.CORE_DOCKERHUB_TOKEN }}'"}' https://hub.docker.com/v2/users/login/ | jq -r .token) + export TOKEN=$TOKEN + + # Get TAGS from DOCKERHUB repository + MOONBEAM_TAGS=$(curl -s -H "Authorization: JWT ${TOKEN}" https://hub.docker.com/v2/repositories/uniquenetwork/builder-moonbeam/tags/?page_size=100 | jq -r '."results"[]["name"]') + # Show TAGS + echo "MOONBEAM TAGS:" + echo $MOONBEAM_TAGS + # Check correct version MOONBEAM and build it if it doesn't exist in MOONBEAM TAGS + if [[ ${MOONBEAM_TAGS[*]} =~ (^|[[:space:]])"${{ matrix.moonbeam_version }}"($|[[:space:]]) ]]; then + echo "Repository has needed MOONBEAM version"; + docker pull uniquenetwork/builder-moonbeam:${{ matrix.moonbeam_version }} + else + echo "Repository has not needed MOONBEAM version, so build it"; + cd .docker/ && docker build --no-cache --file ./Dockerfile-moonbeam.${{ matrix.moonbeam_version }}.yml --tag uniquenetwork/builder-moonbeam:${{ matrix.moonbeam_version }} . + echo "Push needed MOONBEAM version to the repository"; + docker push uniquenetwork/builder-moonbeam:${{ matrix.moonbeam_version }} + fi + shell: bash + + + # Check CUMULUS version and build it if it doesn't exist in repository + - name: Generate ENV related extend Dockerfile file for CUMULUS + uses: cuchi/jinja2-action@v1.2.0 + with: + template: .docker/Dockerfile-cumulus.j2 + output_file: .docker/Dockerfile-cumulus.${{ matrix.cumulus_version }}.yml + variables: | + RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} + CUMULUS_BUILD_BRANCH=${{ matrix.cumulus_version }} + + - name: Check if the dockerhub repository contains the needed CUMULUS version + run: | + # aquire token + TOKEN=$(curl -s -H "Content-Type: application/json" -X POST -d '{"username": "'${{ secrets.CORE_DOCKERHUB_USERNAME }}'", "password": "'${{ secrets.CORE_DOCKERHUB_TOKEN }}'"}' https://hub.docker.com/v2/users/login/ | jq -r .token) + export TOKEN=$TOKEN + + # Get TAGS from DOCKERHUB repository + CUMULUS_TAGS=$(curl -s -H "Authorization: JWT ${TOKEN}" https://hub.docker.com/v2/repositories/uniquenetwork/builder-cumulus/tags/?page_size=100 | jq -r '."results"[]["name"]') + # Show TAGS + echo "CUMULUS TAGS:" + echo $CUMULUS_TAGS + # Check correct version CUMULUS and build it if it doesn't exist in CUMULUS TAGS + if [[ ${CUMULUS_TAGS[*]} =~ (^|[[:space:]])"${{ matrix.cumulus_version }}"($|[[:space:]]) ]]; then + echo "Repository has needed CUMULUS version"; + docker pull uniquenetwork/builder-cumulus:${{ matrix.cumulus_version }} + else + echo "Repository has not needed CUMULUS version, so build it"; + cd .docker/ && docker build --no-cache --file ./Dockerfile-cumulus.${{ matrix.cumulus_version }}.yml --tag uniquenetwork/builder-cumulus:${{ matrix.cumulus_version }} . + echo "Push needed CUMULUS version to the repository"; + docker push uniquenetwork/builder-cumulus:${{ matrix.cumulus_version }} + fi + shell: bash + + + # Build main image for XCM + - name: Generate ENV related extend Dockerfile file + uses: cuchi/jinja2-action@v1.2.0 + with: + template: .docker/Dockerfile-xcm.j2 + output_file: .docker/Dockerfile-xcm.${{ matrix.network }}.yml + variables: | + RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} + NETWORK=${{ matrix.network }} + POLKADOT_BUILD_BRANCH=${{ env.POLKADOT_BUILD_BRANCH }} + POLKADOT_LAUNCH_BRANCH=${{ env.POLKADOT_LAUNCH_BRANCH }} + FEATURE=${{ matrix.features }} + RUNTIME=${{ matrix.runtime }} + BRANCH=${{ github.head_ref }} + ACALA_BUILD_BRANCH=${{ matrix.acala_version }} + MOONBEAM_BUILD_BRANCH=${{ matrix.moonbeam_version }} + CUMULUS_BUILD_BRANCH=${{ matrix.cumulus_version }} + + - name: Show build Dockerfile + run: cat .docker/Dockerfile-xcm.${{ matrix.network }}.yml + + - name: Show launch-config-xcm-${{ matrix.network }} configuration + run: cat .docker/xcm-config/launch-config-xcm-${{ matrix.network }}.json + + - name: Run find-and-replace to remove slashes from branch name + uses: mad9000/actions-find-and-replace-string@2 + id: branchname + with: + source: ${{ github.head_ref }} + find: '/' + replace: '-' + + - name: Pull chainql docker image + run: docker pull uniquenetwork/builder-chainql:latest + + - name: Build the stack + run: cd .docker/ && docker build --no-cache --file ./Dockerfile-xcm.${{ matrix.network }}.yml --tag uniquenetwork/xcm-${{ matrix.network }}-testnet-local:nightly-${{ steps.branchname.outputs.value }}-${{ github.sha }} --tag uniquenetwork/xcm-${{ matrix.network }}-testnet-local:latest . + + - name: Push docker image version + run: docker push uniquenetwork/xcm-${{ matrix.network }}-testnet-local:nightly-${{ steps.branchname.outputs.value }}-${{ github.sha }} + + - name: Push docker image latest + run: docker push uniquenetwork/xcm-${{ matrix.network }}-testnet-local:latest + + - name: Remove builder cache + if: always() # run this step always + run: | + docker builder prune -f + docker system prune -f + xcm-tests: - name: tests - needs: [ xcm-testnet-build ] - uses: ./.github/workflows/xcm-tests_v2.yml + needs: [prepare-execution-marix, xcm-build] + # The type of runner that the job will run on + runs-on: [XL] + + timeout-minutes: 600 + + name: ${{ matrix.network }} + + continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false. + + strategy: + matrix: + include: ${{fromJson(needs.prepare-execution-marix.outputs.matrix)}} + + steps: + - name: Skip if pull request is in Draft + if: github.event.pull_request.draft == true + run: exit 1 + + - name: Clean Workspace + uses: AutoModality/action-clean@v1.1.0 + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} #Checking out head commit + + - name: Read .env file + uses: xom9ikk/dotenv@v1.0.2 + + - name: Generate ENV related extend file for docker-compose + uses: cuchi/jinja2-action@v1.2.0 + with: + template: .docker/docker-compose.tmp-xcm-tests.j2 + output_file: .docker/docker-compose.xcm-tests.${{ matrix.network }}.yml + variables: | + NETWORK=${{ matrix.network }} + - name: Show build configuration + run: cat .docker/docker-compose.xcm-tests.${{ matrix.network }}.yml + - uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Build the stack + run: docker-compose -f ".docker/docker-compose.xcm-tests.${{ matrix.network }}.yml" up -d --remove-orphans --force-recreate --timeout 300 + + # 🚀 POLKADOT LAUNCH COMPLETE 🚀 + - name: Check if docker logs consist messages related to testing of xcm tests + if: success() + run: | + counter=160 + function check_container_status { + docker inspect -f {{.State.Running}} xcm-${{ matrix.network }}-testnet-local + } + function do_docker_logs { + docker logs --details xcm-${{ matrix.network }}-testnet-local 2>&1 + } + function is_started { + if [ "$(check_container_status)" == "true" ]; then + echo "Container: xcm-${{ matrix.network }}-testnet-local RUNNING"; + echo "Check Docker logs" + DOCKER_LOGS=$(do_docker_logs) + if [[ ${DOCKER_LOGS} = *"POLKADOT LAUNCH COMPLETE"* ]];then + echo "🚀 POLKADOT LAUNCH COMPLETE 🚀" + return 0 + else + echo "Message not found in logs output, repeating..." + return 1 + fi + else + echo "Container xcm-${{ matrix.network }}-testnet-local NOT RUNNING" + echo "Halting all future checks" + exit 1 + fi + echo "something goes wrong" + exit 1 + } + while ! is_started; do + echo "Waiting for special message in log files " + sleep 30s + counter=$(( $counter - 1 )) + echo "Counter: $counter" + if [ "$counter" -gt "0" ]; then + continue + else + break + fi + done + echo "Halting script" + exit 0 + shell: bash + + - name: Run XCM tests + working-directory: tests + run: | + yarn install + yarn add mochawesome + node scripts/readyness.js + echo "Ready to start tests" + yarn polkadot-types + NOW=$(date +%s) && yarn ${{ matrix.runtest }} --reporter mochawesome --reporter-options reportFilename=test-${NOW} + env: + RPC_URL: http://127.0.0.1:9933/ + + - name: XCM Test Report + uses: phoenix-actions/test-reporting@v8 + id: test-report + if: success() || failure() # run this step even if previous step failed + with: + name: XCM Tests ${{ matrix.network }} # Name of the check run which will be created + path: tests/mochawesome-report/test-*.json # Path to test results + reporter: mochawesome-json + fail-on-error: 'false' + + - name: Stop running containers + if: always() # run this step always + run: docker-compose -f ".docker/docker-compose.xcm-tests.${{ matrix.network }}.yml" down + + - name: Clean Workspace + if: always() + uses: AutoModality/action-clean@v1.1.0 + + - name: Remove builder cache + if: always() # run this step always + run: | + docker system prune -a -f --- a/Cargo.lock +++ b/Cargo.lock @@ -697,6 +697,26 @@ ] [[package]] +name = "bondrewd" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1660fac8d3acced44dac64453fafedf5aab2de196b932c727e63e4ae42d1cc" +dependencies = [ + "bondrewd-derive", +] + +[[package]] +name = "bondrewd-derive" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723da0dee1eef38edc021b0793f892bdc024500c6a5b0727a2efe16f0e0a6977" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "bounded-vec" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4586,6 +4606,16 @@ ] [[package]] +name = "logtest" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3e43a8657c1d64516dcc9db8ca03826a4aceaf89d5ce1b37b59f6ff0e43026" +dependencies = [ + "lazy_static", + "log", +] + +[[package]] name = "lru" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5163,8 +5193,13 @@ "frame-system-rpc-runtime-api", "frame-try-runtime", "hex-literal", + "impl-trait-for-tuples", "log", + "logtest", + "orml-tokens", + "orml-traits", "orml-vesting", + "orml-xtokens", "pallet-app-promotion", "pallet-aura", "pallet-balances", @@ -5177,6 +5212,7 @@ "pallet-evm-contract-helpers", "pallet-evm-migration", "pallet-evm-transaction-payment", + "pallet-foreign-assets", "pallet-fungible", "pallet-inflation", "pallet-nonfungible", @@ -5282,6 +5318,53 @@ ] [[package]] +name = "orml-tokens" +version = "0.4.1-dev" +source = "git+https://github.com/open-web3-stack/open-runtime-module-library?branch=polkadot-v0.9.27#377213f750755cc48e80a3131eaae63b5eda8362" +dependencies = [ + "frame-support", + "frame-system", + "orml-traits", + "parity-scale-codec 3.1.5", + "scale-info", + "serde", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "orml-traits" +version = "0.4.1-dev" +source = "git+https://github.com/open-web3-stack/open-runtime-module-library?branch=polkadot-v0.9.27#377213f750755cc48e80a3131eaae63b5eda8362" +dependencies = [ + "frame-support", + "impl-trait-for-tuples", + "num-traits", + "orml-utilities", + "parity-scale-codec 3.1.5", + "scale-info", + "serde", + "sp-io", + "sp-runtime", + "sp-std", + "xcm", +] + +[[package]] +name = "orml-utilities" +version = "0.4.1-dev" +source = "git+https://github.com/open-web3-stack/open-runtime-module-library?branch=polkadot-v0.9.27#377213f750755cc48e80a3131eaae63b5eda8362" +dependencies = [ + "frame-support", + "parity-scale-codec 3.1.5", + "scale-info", + "serde", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] name = "orml-vesting" version = "0.4.1-dev" source = "git+https://github.com/open-web3-stack/open-runtime-module-library?branch=polkadot-v0.9.27#377213f750755cc48e80a3131eaae63b5eda8362" @@ -5297,6 +5380,41 @@ ] [[package]] +name = "orml-xcm-support" +version = "0.4.1-dev" +source = "git+https://github.com/open-web3-stack/open-runtime-module-library?branch=polkadot-v0.9.27#377213f750755cc48e80a3131eaae63b5eda8362" +dependencies = [ + "frame-support", + "orml-traits", + "parity-scale-codec 3.1.5", + "sp-runtime", + "sp-std", + "xcm", + "xcm-executor", +] + +[[package]] +name = "orml-xtokens" +version = "0.4.1-dev" +source = "git+https://github.com/open-web3-stack/open-runtime-module-library?branch=polkadot-v0.9.27#377213f750755cc48e80a3131eaae63b5eda8362" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "frame-system", + "orml-traits", + "orml-xcm-support", + "pallet-xcm", + "parity-scale-codec 3.1.5", + "scale-info", + "serde", + "sp-io", + "sp-runtime", + "sp-std", + "xcm", + "xcm-executor", +] + +[[package]] name = "os_str_bytes" version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5791,6 +5909,34 @@ ] [[package]] +name = "pallet-foreign-assets" +version = "0.1.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "hex", + "log", + "orml-tokens", + "pallet-balances", + "pallet-common", + "pallet-fungible", + "pallet-timestamp", + "parity-scale-codec 3.1.5", + "scale-info", + "serde", + "serde_json", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "up-data-structs", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] name = "pallet-fungible" version = "0.1.5" dependencies = [ @@ -6475,7 +6621,7 @@ [[package]] name = "pallet-unique" -version = "0.1.4" +version = "0.2.0" dependencies = [ "ethereum", "evm-coder", @@ -8397,8 +8543,13 @@ "frame-system-rpc-runtime-api", "frame-try-runtime", "hex-literal", + "impl-trait-for-tuples", "log", + "logtest", + "orml-tokens", + "orml-traits", "orml-vesting", + "orml-xtokens", "pallet-app-promotion", "pallet-aura", "pallet-balances", @@ -8411,6 +8562,7 @@ "pallet-evm-contract-helpers", "pallet-evm-migration", "pallet-evm-transaction-payment", + "pallet-foreign-assets", "pallet-fungible", "pallet-inflation", "pallet-nonfungible", @@ -12134,7 +12286,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "digest 0.10.3", "rand 0.8.5", "static_assertions", @@ -12391,8 +12543,13 @@ "frame-system-rpc-runtime-api", "frame-try-runtime", "hex-literal", + "impl-trait-for-tuples", "log", + "logtest", + "orml-tokens", + "orml-traits", "orml-vesting", + "orml-xtokens", "pallet-app-promotion", "pallet-aura", "pallet-balances", @@ -12405,6 +12562,7 @@ "pallet-evm-contract-helpers", "pallet-evm-migration", "pallet-evm-transaction-payment", + "pallet-foreign-assets", "pallet-fungible", "pallet-inflation", "pallet-nonfungible", @@ -12497,6 +12655,7 @@ name = "up-data-structs" version = "0.2.2" dependencies = [ + "bondrewd", "derivative", "frame-support", "frame-system", --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,12 @@ 'client/*', 'primitives/*', 'crates/*', + 'runtime/opal', + 'runtime/quartz', + 'runtime/unique', 'runtime/tests', ] -exclude = ["runtime/unique", "runtime/quartz"] +default-members = ['node/*', 'runtime/opal'] [profile.release] panic = 'unwind' --- a/Makefile +++ b/Makefile @@ -128,9 +128,13 @@ bench-rmrk-equip: make _bench PALLET=proxy-rmrk-equip +.PHONY: bench-foreign-assets +bench-foreign-assets: + make _bench PALLET=foreign-assets + .PHONY: bench-app-promotion bench-app-promotion: make _bench PALLET=app-promotion PALLET_DIR=app-promotion .PHONY: bench -bench: bench-evm-migration bench-unique bench-structure bench-fungible bench-refungible bench-nonfungible bench-scheduler bench-rmrk-core bench-rmrk-equip +bench: bench-evm-migration bench-unique bench-structure bench-fungible bench-refungible bench-nonfungible bench-scheduler bench-rmrk-core bench-rmrk-equip bench-foreign-assets --- a/README.md +++ b/README.md @@ -195,7 +195,7 @@ xtokens -> transfer currencyId: - ForeingAsset + ForeignAsset amount: --- a/crates/evm-coder/procedural/src/solidity_interface.rs +++ b/crates/evm-coder/procedural/src/solidity_interface.rs @@ -42,6 +42,7 @@ pascal_call_name: Ident, snake_call_name: Ident, via: Option<(Type, Ident)>, + condition: Option, } impl Is { fn expand_call_def(&self, gen_ref: &proc_macro2::TokenStream) -> proc_macro2::TokenStream { @@ -64,8 +65,13 @@ generics: &proc_macro2::TokenStream, ) -> proc_macro2::TokenStream { let pascal_call_name = &self.pascal_call_name; + let condition = self.condition.as_ref().map(|condition| { + quote! { + (#condition) && + } + }); quote! { - <#pascal_call_name #generics>::supports_interface(interface_id) + #condition <#pascal_call_name #generics>::supports_interface(this, interface_id) } } @@ -93,8 +99,13 @@ .as_ref() .map(|(_, i)| quote! {.#i()}) .unwrap_or_default(); + let condition = self.condition.as_ref().map(|condition| { + quote! { + if ({let this = &self; (#condition)}) + } + }); quote! { - #call_name::#name(call) => return <#via_typ as ::evm_coder::Callable<#pascal_call_name #generics>>::call(self #via_map, Msg { + #call_name::#name(call) #condition => return <#via_typ as ::evm_coder::Callable<#pascal_call_name #generics>>::call(self #via_map, Msg { call, caller: c.caller, value: c.value, @@ -138,17 +149,50 @@ } let name = input.parse::()?; let lookahead = input.lookahead1(); - let via = if lookahead.peek(syn::token::Paren) { + + let mut condition: Option = None; + let mut via: Option<(Type, Ident)> = None; + + if lookahead.peek(syn::token::Paren) { let contents; parenthesized!(contents in input); - let method = contents.parse::()?; - contents.parse::()?; - let ty = contents.parse::()?; - Some((ty, method)) - } else if lookahead.peek(Token![,]) { - None - } else if input.is_empty() { - None + let input = contents; + + while !input.is_empty() { + let lookahead = input.lookahead1(); + if lookahead.peek(Token![if]) { + input.parse::()?; + let contents; + parenthesized!(contents in input); + let contents = contents.parse::()?; + + if condition.replace(contents).is_some() { + return Err(syn::Error::new(input.span(), "condition is already set")); + } + } else if lookahead.peek(kw::via) { + input.parse::()?; + let contents; + parenthesized!(contents in input); + + let method = contents.parse::()?; + contents.parse::()?; + let ty = contents.parse::()?; + + if via.replace((ty, method)).is_some() { + return Err(syn::Error::new(input.span(), "via is already set")); + } + } else { + return Err(lookahead.error()); + } + + if input.peek(Token![,]) { + input.parse::()?; + } else if !input.is_empty() { + return Err(syn::Error::new(input.span(), "expected end")); + } + } + } else if lookahead.peek(Token![,]) || input.is_empty() { + // Pass } else { return Err(lookahead.error()); }; @@ -157,6 +201,7 @@ snake_call_name: pascal_ident_to_snake_call(&name), name, via, + condition, }); if input.peek(Token![,]) { input.parse::()?; @@ -495,6 +540,7 @@ syn::custom_keyword!(weight); syn::custom_keyword!(via); + syn::custom_keyword!(returns); syn::custom_keyword!(name); syn::custom_keyword!(is); syn::custom_keyword!(inline_is); @@ -996,16 +1042,6 @@ #(#inline_interface_id)* u32::to_be_bytes(interface_id) } - /// Is this contract implements specified ERC165 selector - pub fn supports_interface(interface_id: ::evm_coder::types::bytes4) -> bool { - interface_id != u32::to_be_bytes(0xffffff) && ( - interface_id == ::evm_coder::ERC165Call::INTERFACE_ID || - interface_id == Self::interface_id() - #( - || #supports_interface - )* - ) - } /// Generate solidity definitions for methods described in this interface pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) { use evm_coder::solidity::*; @@ -1024,7 +1060,7 @@ )*), }; - let mut out = string::new(); + let mut out = ::evm_coder::types::string::new(); if #solidity_name.starts_with("Inline") { out.push_str("/// @dev inlined interface\n"); } @@ -1062,6 +1098,20 @@ return Ok(None); } } + impl #generics #call_name #gen_ref + #gen_where + { + /// Is this contract implements specified ERC165 selector + pub fn supports_interface(this: &#name, interface_id: ::evm_coder::types::bytes4) -> bool { + interface_id != u32::to_be_bytes(0xffffff) && ( + interface_id == ::evm_coder::ERC165Call::INTERFACE_ID || + interface_id == Self::interface_id() + #( + || #supports_interface + )* + ) + } + } impl #generics ::evm_coder::Weighted for #call_name #gen_ref #gen_where { @@ -1091,7 +1141,7 @@ )* #call_name::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {interface_id}, _) => { let mut writer = ::evm_coder::abi::AbiWriter::default(); - writer.bool(&<#call_name #gen_ref>::supports_interface(interface_id)); + writer.bool(&<#call_name #gen_ref>::supports_interface(self, interface_id)); return Ok(writer.into()); } _ => {}, @@ -1101,7 +1151,7 @@ #( #call_variants_this, )* - _ => unreachable!() + _ => Err(::evm_coder::execution::Error::from("method is not available").into()), } } } --- a/crates/evm-coder/src/abi.rs +++ b/crates/evm-coder/src/abi.rs @@ -313,7 +313,7 @@ /// Finish writer, concatenating all internal buffers pub fn finish(mut self) -> Vec { for (static_offset, part) in self.dynamic_part { - let part_offset = self.static_part.len() - self.had_call.then(|| 4).unwrap_or(0); + let part_offset = self.static_part.len() - if self.had_call { 4 } else { 0 }; let encoded_dynamic_offset = usize::to_be_bytes(part_offset); self.static_part[static_offset + ABI_ALIGNMENT - encoded_dynamic_offset.len() --- a/crates/evm-coder/src/lib.rs +++ b/crates/evm-coder/src/lib.rs @@ -74,10 +74,10 @@ /// #[solidity_interface(name = MyContract, is(SuperContract), inline_is(InlineContract))] /// impl Contract { /// /// Multiply two numbers -/// /// @param a First number -/// /// @param b Second number -/// /// @return uint32 Product of two passed numbers -/// /// @dev This function returns error in case of overflow +/// /// @param a First number +/// /// @param b Second number +/// /// @return uint32 Product of two passed numbers +/// /// @dev This function returns error in case of overflow /// #[weight(200 + a + b)] /// #[solidity_interface(rename_selector = "mul")] /// fn mul(&mut self, a: uint32, b: uint32) -> Result { --- /dev/null +++ b/crates/evm-coder/tests/conditional_is.rs @@ -0,0 +1,44 @@ +use evm_coder::{types::*, solidity_interface, execution::Result, Call}; + +pub struct Contract(bool); + +#[solidity_interface(name = A)] +impl Contract { + fn method_a() -> Result { + Ok(()) + } +} + +#[solidity_interface(name = B)] +impl Contract { + fn method_b() -> Result { + Ok(()) + } +} + +#[solidity_interface(name = Contract, is( + A(if(this.0)), + B(if(!this.0)), +))] +impl Contract {} + +#[test] +fn conditional_erc165() { + assert!(ContractCall::supports_interface( + &Contract(true), + ACall::METHOD_A + )); + assert!(!ContractCall::supports_interface( + &Contract(false), + ACall::METHOD_A + )); + + assert!(ContractCall::supports_interface( + &Contract(false), + BCall::METHOD_B + )); + assert!(!ContractCall::supports_interface( + &Contract(true), + BCall::METHOD_B + )); +} --- a/crates/evm-coder/tests/generics.rs +++ b/crates/evm-coder/tests/generics.rs @@ -17,7 +17,7 @@ use std::marker::PhantomData; use evm_coder::{execution::Result, generate_stubgen, solidity_interface, types::*}; -struct Generic(PhantomData); +pub struct Generic(PhantomData); #[solidity_interface(name = GenericIs)] impl Generic { --- a/crates/evm-coder/tests/random.rs +++ b/crates/evm-coder/tests/random.rs @@ -18,7 +18,7 @@ use evm_coder::{ToLog, execution::Result, solidity_interface, types::*, solidity, weight}; -struct Impls; +pub struct Impls; #[solidity_interface(name = OurInterface)] impl Impls { --- a/crates/evm-coder/tests/solidity_generation.rs +++ b/crates/evm-coder/tests/solidity_generation.rs @@ -16,7 +16,7 @@ use evm_coder::{execution::Result, generate_stubgen, solidity_interface, types::*}; -struct ERC20; +pub struct ERC20; #[solidity_interface(name = ERC20)] impl ERC20 { --- a/node/cli/Cargo.toml +++ b/node/cli/Cargo.toml @@ -302,7 +302,7 @@ [dependencies] futures = '0.3.17' -log = '0.4.14' +log = '0.4.16' flexi_logger = "0.22.5" parking_lot = '0.12.1' clap = "3.1.2" --- a/node/cli/src/chain_spec.rs +++ b/node/cli/src/chain_spec.rs @@ -65,6 +65,14 @@ Unknown(String), } +#[cfg(not(feature = "unique-runtime"))] +/// PARA_ID for Opal/Quartz +const PARA_ID: u32 = 2095; + +#[cfg(feature = "unique-runtime")] +/// PARA_ID for Unique +const PARA_ID: u32 = 2037; + pub trait RuntimeIdentification { fn runtime_id(&self) -> RuntimeId; } @@ -167,6 +175,7 @@ .collect(), }, treasury: Default::default(), + tokens: TokensConfig { balances: vec![] }, sudo: SudoConfig { key: Some($root_key), }, @@ -235,7 +244,7 @@ get_account_id_from_seed::("Eve//stash"), get_account_id_from_seed::("Ferdie//stash"), ], - 1000 + PARA_ID ) }, // Bootnodes @@ -250,7 +259,7 @@ // Extensions Extensions { relay_chain: "rococo-dev".into(), - para_id: 1000, + para_id: PARA_ID, }, ) } @@ -303,7 +312,7 @@ get_account_id_from_seed::("Eve//stash"), get_account_id_from_seed::("Ferdie//stash"), ], - 1000 + PARA_ID ) }, // Bootnodes @@ -318,7 +327,7 @@ // Extensions Extensions { relay_chain: "westend-local".into(), - para_id: 1000, + para_id: PARA_ID, }, ) } --- a/pallets/app-promotion/Cargo.toml +++ b/pallets/app-promotion/Cargo.toml @@ -66,8 +66,8 @@ pallet-common ={ default-features = false, path = "../common" } pallet-unique ={ default-features = false, path = "../unique" } pallet-evm-contract-helpers ={ default-features = false, path = "../evm-contract-helpers" } +pallet-evm-migration ={ default-features = false, path = "../evm-migration" } -[dev-dependencies] -pallet-evm-migration ={ default-features = false, path = "../evm-migration" } +# [dev-dependencies] ################################################################################ --- a/pallets/app-promotion/src/benchmarking.rs +++ b/pallets/app-promotion/src/benchmarking.rs @@ -20,11 +20,10 @@ use crate::Pallet as PromototionPallet; use sp_runtime::traits::Bounded; -use sp_std::vec; use frame_benchmarking::{benchmarks, account}; use frame_support::traits::OnInitialize; -use frame_system::{Origin, RawOrigin}; +use frame_system::RawOrigin; use pallet_unique::benchmarking::create_nft_collection; use pallet_evm_migration::Pallet as EvmMigrationPallet; --- a/pallets/app-promotion/src/lib.rs +++ b/pallets/app-promotion/src/lib.rs @@ -594,14 +594,14 @@ last_id, *income_acc.borrow(), ExistenceRequirement::KeepAlive, - ) - .and_then(|_| { - Self::add_lock_balance(last_id, *income_acc.borrow())?; - >::try_mutate(|staked| { - staked - .checked_add(&*income_acc.borrow()) - .ok_or(ArithmeticError::Overflow.into()) - }) + )?; + + Self::add_lock_balance(last_id, *income_acc.borrow())?; + >::try_mutate(|staked| -> DispatchResult { + *staked = staked + .checked_add(&*income_acc.borrow()) + .ok_or(ArithmeticError::Overflow)?; + Ok(()) })?; Self::deposit_event(Event::StakingRecalculation( --- a/pallets/common/src/benchmarking.rs +++ b/pallets/common/src/benchmarking.rs @@ -19,8 +19,8 @@ use pallet_evm::account::CrossAccountId; use frame_benchmarking::{benchmarks, account}; use up_data_structs::{ - CollectionMode, CreateCollectionData, CollectionId, Property, PropertyKey, PropertyValue, - CollectionPermissions, NestingPermissions, MAX_COLLECTION_NAME_LENGTH, + CollectionMode, CollectionFlags, CreateCollectionData, CollectionId, Property, PropertyKey, + PropertyValue, CollectionPermissions, NestingPermissions, MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH, MAX_PROPERTIES_PER_ITEM, }; use frame_support::{ @@ -116,7 +116,7 @@ create_collection_raw( owner, CollectionMode::NFT, - |owner, data| >::init_collection(owner, data, true), + |owner, data| >::init_collection(owner, data, CollectionFlags::default()), |h| h, ) } --- a/pallets/common/src/erc.rs +++ b/pallets/common/src/erc.rs @@ -194,7 +194,7 @@ /// Get current sponsor. /// /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. - fn get_collection_sponsor(&self) -> Result<(address, uint256)> { + fn collection_sponsor(&self) -> Result<(address, uint256)> { let sponsor = match self.collection.sponsorship.sponsor() { Some(sponsor) => sponsor, None => return Ok(Default::default()), @@ -406,9 +406,9 @@ true => { let mut bv = OwnerRestrictedSet::new(); for i in collections { - bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or(Error::Revert( - "Can't convert address into collection id".into(), - ))?) + bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or_else(|| { + Error::Revert("Can't convert address into collection id".into()) + })?) .map_err(|_| "too many collections")?; } let mut nesting = permissions.nesting().clone(); --- a/pallets/common/src/lib.rs +++ b/pallets/common/src/lib.rs @@ -70,6 +70,7 @@ COLLECTION_NUMBER_LIMIT, Collection, RpcCollection, + CollectionFlags, CollectionId, CreateItemData, MAX_TOKEN_PREFIX_LENGTH, @@ -252,9 +253,9 @@ } /// Checks that the collection was created with, and must be operated upon through **Unique API**. - /// Now check only the `external_collection` flag and if it's **true**, then return [`Error::CollectionIsExternal`] error. + /// Now check only the `external` flag and if it's **true**, then return [`Error::CollectionIsExternal`] error. pub fn check_is_internal(&self) -> DispatchResult { - if self.external_collection { + if self.flags.external { return Err(>::CollectionIsExternal)?; } @@ -262,9 +263,9 @@ } /// Checks that the collection was created with, and must be operated upon through an **assimilated API**. - /// Now check only the `external_collection` flag and if it's **false**, then return [`Error::CollectionIsInternal`] error. + /// Now check only the `external` flag and if it's **false**, then return [`Error::CollectionIsInternal`] error. pub fn check_is_external(&self) -> DispatchResult { - if !self.external_collection { + if !self.flags.external { return Err(>::CollectionIsInternal)?; } @@ -792,7 +793,7 @@ sponsorship, limits, permissions, - external_collection, + flags, } = >::get(collection)?; let token_property_permissions = >::get(collection) @@ -822,7 +823,8 @@ permissions, token_property_permissions, properties, - read_only: external_collection, + read_only: flags.external, + foreign: flags.foreign, }) } } @@ -861,11 +863,11 @@ /// /// * `owner` - The owner of the collection. /// * `data` - Description of the created collection. - /// * `is_external` - Marks that collection managet by not "Unique network". + /// * `flags` - Extra flags to store. pub fn init_collection( owner: T::CrossAccountId, data: CreateCollectionData, - is_external: bool, + flags: CollectionFlags, ) -> Result { { ensure!( @@ -909,7 +911,7 @@ Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions) }) .unwrap_or_else(|| Ok(CollectionPermissions::default()))?, - external_collection: is_external, + flags, }; let mut collection_properties = up_data_structs::CollectionProperties::get(); --- a/pallets/evm-contract-helpers/src/eth.rs +++ b/pallets/evm-contract-helpers/src/eth.rs @@ -169,7 +169,7 @@ /// /// @param contractAddress The contract for which a sponsor is requested. /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. - fn get_sponsor(&self, contract_address: address) -> Result<(address, uint256)> { + fn sponsor(&self, contract_address: address) -> Result<(address, uint256)> { let sponsor = Pallet::::get_sponsor(contract_address).ok_or("Contract has no sponsor")?; Ok(pallet_common::eth::convert_cross_account_to_tuple::( @@ -220,7 +220,7 @@ /// Get current contract sponsoring rate limit /// @param contractAddress Contract to get sponsoring rate limit of /// @return uint32 Amount of blocks between two sponsored transactions - fn get_sponsoring_rate_limit(&self, contract_address: address) -> Result { + fn sponsoring_rate_limit(&self, contract_address: address) -> Result { self.recorder().consume_sload()?; Ok(>::get(contract_address) @@ -273,7 +273,7 @@ /// @param contractAddress Contract to get sponsoring fee limit of /// @return uint256 Maximum amount of fee that could be spent by single /// transaction - fn get_sponsoring_fee_limit(&self, contract_address: address) -> Result { + fn sponsoring_fee_limit(&self, contract_address: address) -> Result { self.recorder().consume_sload()?; Ok(get_sponsoring_fee_limit::(contract_address)) --- a/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol +++ b/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol @@ -10,11 +10,7 @@ } contract ERC165 is Dummy { - function supportsInterface(bytes4 interfaceID) - external - view - returns (bool) - { + function supportsInterface(bytes4 interfaceID) external view returns (bool) { require(false, stub_error); interfaceID; return true; @@ -24,15 +20,12 @@ /// @dev inlined interface contract ContractHelpersEvents { event ContractSponsorSet(address indexed contractAddress, address sponsor); - event ContractSponsorshipConfirmed( - address indexed contractAddress, - address sponsor - ); + event ContractSponsorshipConfirmed(address indexed contractAddress, address sponsor); event ContractSponsorRemoved(address indexed contractAddress); } /// @title Magic contract, which allows users to reconfigure other contracts -/// @dev the ERC-165 identifier for this interface is 0x172cb4fb +/// @dev the ERC-165 identifier for this interface is 0x30afad04 contract ContractHelpers is Dummy, ERC165, ContractHelpersEvents { /// Get user, which deployed specified contract /// @dev May return zero address in case if contract is deployed @@ -43,11 +36,7 @@ /// @return address Owner of contract /// @dev EVM selector for this function is: 0x5152b14c, /// or in textual repr: contractOwner(address) - function contractOwner(address contractAddress) - public - view - returns (address) - { + function contractOwner(address contractAddress) public view returns (address) { require(false, stub_error); contractAddress; dummy; @@ -105,13 +94,9 @@ /// /// @param contractAddress The contract for which a sponsor is requested. /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. - /// @dev EVM selector for this function is: 0x743fc745, - /// or in textual repr: getSponsor(address) - function getSponsor(address contractAddress) - public - view - returns (Tuple0 memory) - { + /// @dev EVM selector for this function is: 0x766c4f37, + /// or in textual repr: sponsor(address) + function sponsor(address contractAddress) public view returns (Tuple0 memory) { require(false, stub_error); contractAddress; dummy; @@ -137,11 +122,7 @@ /// @return **true** if contract has pending sponsor. /// @dev EVM selector for this function is: 0x39b9b242, /// or in textual repr: hasPendingSponsor(address) - function hasPendingSponsor(address contractAddress) - public - view - returns (bool) - { + function hasPendingSponsor(address contractAddress) public view returns (bool) { require(false, stub_error); contractAddress; dummy; @@ -150,11 +131,7 @@ /// @dev EVM selector for this function is: 0x6027dc61, /// or in textual repr: sponsoringEnabled(address) - function sponsoringEnabled(address contractAddress) - public - view - returns (bool) - { + function sponsoringEnabled(address contractAddress) public view returns (bool) { require(false, stub_error); contractAddress; dummy; @@ -173,13 +150,9 @@ /// Get current contract sponsoring rate limit /// @param contractAddress Contract to get sponsoring rate limit of /// @return uint32 Amount of blocks between two sponsored transactions - /// @dev EVM selector for this function is: 0x610cfabd, - /// or in textual repr: getSponsoringRateLimit(address) - function getSponsoringRateLimit(address contractAddress) - public - view - returns (uint32) - { + /// @dev EVM selector for this function is: 0xf29694d8, + /// or in textual repr: sponsoringRateLimit(address) + function sponsoringRateLimit(address contractAddress) public view returns (uint32) { require(false, stub_error); contractAddress; dummy; @@ -194,9 +167,7 @@ /// @dev Only contract owner can change this setting /// @dev EVM selector for this function is: 0x77b6c908, /// or in textual repr: setSponsoringRateLimit(address,uint32) - function setSponsoringRateLimit(address contractAddress, uint32 rateLimit) - public - { + function setSponsoringRateLimit(address contractAddress, uint32 rateLimit) public { require(false, stub_error); contractAddress; rateLimit; @@ -211,9 +182,7 @@ /// @dev Only contract owner can change this setting /// @dev EVM selector for this function is: 0x03aed665, /// or in textual repr: setSponsoringFeeLimit(address,uint256) - function setSponsoringFeeLimit(address contractAddress, uint256 feeLimit) - public - { + function setSponsoringFeeLimit(address contractAddress, uint256 feeLimit) public { require(false, stub_error); contractAddress; feeLimit; @@ -224,13 +193,9 @@ /// @param contractAddress Contract to get sponsoring fee limit of /// @return uint256 Maximum amount of fee that could be spent by single /// transaction - /// @dev EVM selector for this function is: 0xc3fdc9ee, - /// or in textual repr: getSponsoringFeeLimit(address) - function getSponsoringFeeLimit(address contractAddress) - public - view - returns (uint256) - { + /// @dev EVM selector for this function is: 0x75b73606, + /// or in textual repr: sponsoringFeeLimit(address) + function sponsoringFeeLimit(address contractAddress) public view returns (uint256) { require(false, stub_error); contractAddress; dummy; @@ -244,11 +209,7 @@ /// @return bool Is specified users exists in contract allowlist /// @dev EVM selector for this function is: 0x5c658165, /// or in textual repr: allowed(address,address) - function allowed(address contractAddress, address user) - public - view - returns (bool) - { + function allowed(address contractAddress, address user) public view returns (bool) { require(false, stub_error); contractAddress; user; @@ -284,11 +245,7 @@ /// @return bool Is specified contract has allowlist access enabled /// @dev EVM selector for this function is: 0xc772ef6c, /// or in textual repr: allowlistEnabled(address) - function allowlistEnabled(address contractAddress) - public - view - returns (bool) - { + function allowlistEnabled(address contractAddress) public view returns (bool) { require(false, stub_error); contractAddress; dummy; --- /dev/null +++ b/pallets/foreign-assets/Cargo.toml @@ -0,0 +1,53 @@ +[package] +name = "pallet-foreign-assets" +version = "0.1.0" +license = "GPLv3" +edition = "2021" + +[dependencies] +log = { version = "0.4.16", default-features = false } +serde = { version = "1.0.136", optional = true } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27", default-features = false } +up-data-structs = { default-features = false, path = '../../primitives/data-structs' } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27", default-features = false } +pallet-common = { default-features = false, path = '../common' } +pallet-fungible = { default-features = false, path = '../fungible' } +xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.27", default-features = false } +xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.27", default-features = false } +xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.27", default-features = false } +#orml-tokens = { git = 'https://github.com/UniqueNetwork/open-runtime-module-library', branch = 'unique-polkadot-v0.9.24', version = "0.4.1-dev", default-features = false } +orml-tokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch = "polkadot-v0.9.27", version = "0.4.1-dev", default-features = false } +frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } + +[dev-dependencies] +serde_json = "1.0.68" +hex = { version = "0.4" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } +pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } + +[features] +default = ["std"] +std = [ + "serde", + "log/std", + "codec/std", + "scale-info/std", + "sp-runtime/std", + "sp-std/std", + "frame-support/std", + "frame-system/std", + "up-data-structs/std", + "pallet-common/std", + "pallet-balances/std", + "pallet-fungible/std", + "orml-tokens/std" +] +try-runtime = ["frame-support/try-runtime"] +runtime-benchmarks = ['frame-benchmarking', 'pallet-common/runtime-benchmarks'] \ No newline at end of file --- /dev/null +++ b/pallets/foreign-assets/src/benchmarking.rs @@ -0,0 +1,68 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +#![allow(missing_docs)] + +use super::{Config, Pallet, Call}; +use frame_benchmarking::{benchmarks, account}; +use frame_system::RawOrigin; +use crate::AssetMetadata; +use xcm::opaque::latest::Junction::Parachain; +use xcm::VersionedMultiLocation; +use frame_support::{ + traits::{Currency}, +}; +use sp_std::boxed::Box; + +benchmarks! { + register_foreign_asset { + let owner: T::AccountId = account("user", 0, 1); + let location: VersionedMultiLocation = VersionedMultiLocation::from(Parachain(1000).into()); + let metadata: AssetMetadata<<::Currency as Currency<::AccountId>>::Balance> = AssetMetadata{ + name: "name".into(), + symbol: "symbol".into(), + decimals: 18, + minimal_balance: 1u32.into() + }; + let mut balance: <::Currency as Currency<::AccountId>>::Balance = + 4_000_000_000u32.into(); + balance = balance * balance; + ::Currency::make_free_balance_be(&owner, + balance); + }: _(RawOrigin::Root, owner, Box::new(location), Box::new(metadata)) + + update_foreign_asset { + let owner: T::AccountId = account("user", 0, 1); + let location: VersionedMultiLocation = VersionedMultiLocation::from(Parachain(2000).into()); + let metadata: AssetMetadata<<::Currency as Currency<::AccountId>>::Balance> = AssetMetadata{ + name: "name".into(), + symbol: "symbol".into(), + decimals: 18, + minimal_balance: 1u32.into() + }; + let metadata2: AssetMetadata<<::Currency as Currency<::AccountId>>::Balance> = AssetMetadata{ + name: "name2".into(), + symbol: "symbol2".into(), + decimals: 18, + minimal_balance: 1u32.into() + }; + let mut balance: <::Currency as Currency<::AccountId>>::Balance = + 4_000_000_000u32.into(); + balance = balance * balance; + ::Currency::make_free_balance_be(&owner, balance); + Pallet::::register_foreign_asset(RawOrigin::Root.into(), owner, Box::new(location.clone()), Box::new(metadata))?; + }: _(RawOrigin::Root, 0, Box::new(location), Box::new(metadata2)) +} --- /dev/null +++ b/pallets/foreign-assets/src/impl_fungibles.rs @@ -0,0 +1,454 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +//! Implementations for fungibles trait. + +use super::*; +use frame_system::Config as SystemConfig; + +use frame_support::traits::tokens::{DepositConsequence, WithdrawConsequence}; +use pallet_common::CollectionHandle; +use pallet_fungible::FungibleHandle; +use pallet_common::CommonCollectionOperations; +use up_data_structs::budget::Value; +use sp_runtime::traits::{CheckedAdd, CheckedSub}; + +impl fungibles::Inspect<::AccountId> for Pallet +where + T: orml_tokens::Config, + BalanceOf: From<::Balance>, + BalanceOf: From<::Balance>, + ::Balance: From>, + ::Balance: From>, +{ + type AssetId = AssetIds; + type Balance = BalanceOf; + + fn total_issuance(asset: Self::AssetId) -> Self::Balance { + log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible total_issuance"); + + match asset { + AssetIds::NativeAssetId(NativeCurrency::Here) => { + as fungible::Inspect>::total_issuance() + .into() + } + AssetIds::NativeAssetId(NativeCurrency::Parent) => { + as fungibles::Inspect>::total_issuance( + AssetIds::NativeAssetId(NativeCurrency::Parent), + ) + .into() + } + AssetIds::ForeignAssetId(fid) => { + let target_collection_id = match >::get(fid) { + Some(v) => v, + None => return Zero::zero(), + }; + let collection_handle = match >::try_get(target_collection_id) { + Ok(v) => v, + Err(_) => return Zero::zero(), + }; + let collection = FungibleHandle::cast(collection_handle); + Self::Balance::try_from(collection.total_supply()).unwrap_or(Zero::zero()) + } + } + } + + fn minimum_balance(asset: Self::AssetId) -> Self::Balance { + log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible minimum_balance"); + match asset { + AssetIds::NativeAssetId(NativeCurrency::Here) => { + as fungible::Inspect>::minimum_balance() + .into() + } + AssetIds::NativeAssetId(NativeCurrency::Parent) => { + as fungibles::Inspect>::minimum_balance( + AssetIds::NativeAssetId(NativeCurrency::Parent), + ) + .into() + } + AssetIds::ForeignAssetId(fid) => { + AssetMetadatas::::get(AssetIds::ForeignAssetId(fid)) + .map(|x| x.minimal_balance) + .unwrap_or_else(Zero::zero) + } + } + } + + fn balance(asset: Self::AssetId, who: &::AccountId) -> Self::Balance { + log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible balance"); + match asset { + AssetIds::NativeAssetId(NativeCurrency::Here) => { + as fungible::Inspect>::balance(who).into() + } + AssetIds::NativeAssetId(NativeCurrency::Parent) => { + as fungibles::Inspect>::balance( + AssetIds::NativeAssetId(NativeCurrency::Parent), + who, + ) + .into() + } + AssetIds::ForeignAssetId(fid) => { + let target_collection_id = match >::get(fid) { + Some(v) => v, + None => return Zero::zero(), + }; + let collection_handle = match >::try_get(target_collection_id) { + Ok(v) => v, + Err(_) => return Zero::zero(), + }; + let collection = FungibleHandle::cast(collection_handle); + Self::Balance::try_from( + collection.balance(T::CrossAccountId::from_sub(who.clone()), TokenId(0)), + ) + .unwrap_or(Zero::zero()) + } + } + } + + fn reducible_balance( + asset: Self::AssetId, + who: &::AccountId, + keep_alive: bool, + ) -> Self::Balance { + log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible reducible_balance"); + + match asset { + AssetIds::NativeAssetId(NativeCurrency::Here) => { + as fungible::Inspect>::reducible_balance( + who, keep_alive, + ) + .into() + } + AssetIds::NativeAssetId(NativeCurrency::Parent) => { + as fungibles::Inspect>::reducible_balance( + AssetIds::NativeAssetId(NativeCurrency::Parent), + who, + keep_alive, + ) + .into() + } + _ => Self::balance(asset, who), + } + } + + fn can_deposit( + asset: Self::AssetId, + who: &::AccountId, + amount: Self::Balance, + mint: bool, + ) -> DepositConsequence { + log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible can_deposit"); + + match asset { + AssetIds::NativeAssetId(NativeCurrency::Here) => { + as fungible::Inspect>::can_deposit( + who, + amount.into(), + mint, + ) + } + AssetIds::NativeAssetId(NativeCurrency::Parent) => { + as fungibles::Inspect>::can_deposit( + AssetIds::NativeAssetId(NativeCurrency::Parent), + who, + amount.into(), + mint, + ) + } + _ => { + if amount.is_zero() { + return DepositConsequence::Success; + } + + let extential_deposit_value = T::ExistentialDeposit::get(); + let ed_value: u128 = match extential_deposit_value.try_into() { + Ok(val) => val, + Err(_) => return DepositConsequence::CannotCreate, + }; + let extential_deposit: Self::Balance = match ed_value.try_into() { + Ok(val) => val, + Err(_) => return DepositConsequence::CannotCreate, + }; + + let new_total_balance = match Self::balance(asset, who).checked_add(&amount) { + Some(x) => x, + None => return DepositConsequence::Overflow, + }; + + if new_total_balance < extential_deposit { + return DepositConsequence::BelowMinimum; + } + + DepositConsequence::Success + } + } + } + + fn can_withdraw( + asset: Self::AssetId, + who: &::AccountId, + amount: Self::Balance, + ) -> WithdrawConsequence { + log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible can_withdraw"); + let value: u128 = match amount.try_into() { + Ok(val) => val, + Err(_) => return WithdrawConsequence::UnknownAsset, + }; + + match asset { + AssetIds::NativeAssetId(NativeCurrency::Here) => { + let this_amount: ::Balance = match value.try_into() { + Ok(val) => val, + Err(_) => { + return WithdrawConsequence::UnknownAsset; + } + }; + match as fungible::Inspect>::can_withdraw( + who, + this_amount, + ) { + WithdrawConsequence::NoFunds => WithdrawConsequence::NoFunds, + WithdrawConsequence::WouldDie => WithdrawConsequence::WouldDie, + WithdrawConsequence::UnknownAsset => WithdrawConsequence::UnknownAsset, + WithdrawConsequence::Underflow => WithdrawConsequence::Underflow, + WithdrawConsequence::Overflow => WithdrawConsequence::Overflow, + WithdrawConsequence::Frozen => WithdrawConsequence::Frozen, + WithdrawConsequence::Success => WithdrawConsequence::Success, + _ => WithdrawConsequence::NoFunds, + } + } + AssetIds::NativeAssetId(NativeCurrency::Parent) => { + let parent_amount: ::Balance = match value.try_into() { + Ok(val) => val, + Err(_) => { + return WithdrawConsequence::UnknownAsset; + } + }; + match as fungibles::Inspect>::can_withdraw( + AssetIds::NativeAssetId(NativeCurrency::Parent), + who, + parent_amount, + ) { + WithdrawConsequence::NoFunds => WithdrawConsequence::NoFunds, + WithdrawConsequence::WouldDie => WithdrawConsequence::WouldDie, + WithdrawConsequence::UnknownAsset => WithdrawConsequence::UnknownAsset, + WithdrawConsequence::Underflow => WithdrawConsequence::Underflow, + WithdrawConsequence::Overflow => WithdrawConsequence::Overflow, + WithdrawConsequence::Frozen => WithdrawConsequence::Frozen, + WithdrawConsequence::Success => WithdrawConsequence::Success, + _ => WithdrawConsequence::NoFunds, + } + } + _ => match Self::balance(asset, who).checked_sub(&amount) { + Some(_) => WithdrawConsequence::Success, + None => WithdrawConsequence::NoFunds, + }, + } + } +} + +impl fungibles::Mutate<::AccountId> for Pallet +where + T: orml_tokens::Config, + BalanceOf: From<::Balance>, + BalanceOf: From<::Balance>, + ::Balance: From>, + ::Balance: From>, + u128: From>, +{ + fn mint_into( + asset: Self::AssetId, + who: &::AccountId, + amount: Self::Balance, + ) -> DispatchResult { + //Self::do_mint(asset, who, amount, None) + log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible mint_into {:?}", asset); + + match asset { + AssetIds::NativeAssetId(NativeCurrency::Here) => { + as fungible::Mutate>::mint_into( + who, + amount.into(), + ) + .into() + } + AssetIds::NativeAssetId(NativeCurrency::Parent) => { + as fungibles::Mutate>::mint_into( + AssetIds::NativeAssetId(NativeCurrency::Parent), + who, + amount.into(), + ) + .into() + } + AssetIds::ForeignAssetId(fid) => { + let target_collection_id = match >::get(fid) { + Some(v) => v, + None => { + return Err(DispatchError::Other( + "Associated collection not found for asset", + )) + } + }; + let collection = + FungibleHandle::cast(>::try_get(target_collection_id)?); + let account = T::CrossAccountId::from_sub(who.clone()); + + let amount_data: pallet_fungible::CreateItemData = + (account.clone(), amount.into()); + + pallet_fungible::Pallet::::create_item_foreign( + &collection, + &account, + amount_data, + &Value::new(0), + )?; + + Ok(()) + } + } + } + + fn burn_from( + asset: Self::AssetId, + who: &::AccountId, + amount: Self::Balance, + ) -> Result { + // let f = DebitFlags { keep_alive: false, best_effort: false }; + log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible burn_from"); + + match asset { + AssetIds::NativeAssetId(NativeCurrency::Here) => { + match as fungible::Mutate>::burn_from( + who, + amount.into(), + ) { + Ok(v) => Ok(v.into()), + Err(e) => Err(e), + } + } + AssetIds::NativeAssetId(NativeCurrency::Parent) => { + match as fungibles::Mutate>::burn_from( + AssetIds::NativeAssetId(NativeCurrency::Parent), + who, + amount.into(), + ) { + Ok(v) => Ok(v.into()), + Err(e) => Err(e), + } + } + AssetIds::ForeignAssetId(fid) => { + let target_collection_id = match >::get(fid) { + Some(v) => v, + None => { + return Err(DispatchError::Other( + "Associated collection not found for asset", + )) + } + }; + let collection = + FungibleHandle::cast(>::try_get(target_collection_id)?); + pallet_fungible::Pallet::::burn_foreign( + &collection, + &T::CrossAccountId::from_sub(who.clone()), + amount.into(), + )?; + + Ok(amount) + } + } + } + + fn slash( + asset: Self::AssetId, + who: &::AccountId, + amount: Self::Balance, + ) -> Result { + // let f = DebitFlags { keep_alive: false, best_effort: true }; + log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible slash"); + Ok(Self::burn_from(asset, who, amount)?) + } +} + +impl fungibles::Transfer for Pallet +where + T: orml_tokens::Config, + BalanceOf: From<::Balance>, + BalanceOf: From<::Balance>, + ::Balance: From>, + ::Balance: From>, + u128: From>, +{ + fn transfer( + asset: Self::AssetId, + source: &::AccountId, + dest: &::AccountId, + amount: Self::Balance, + keep_alive: bool, + ) -> Result { + // let f = TransferFlags { keep_alive, best_effort: false, burn_dust: false }; + log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible transfer"); + + match asset { + AssetIds::NativeAssetId(NativeCurrency::Here) => { + match as fungible::Transfer>::transfer( + source, + dest, + amount.into(), + keep_alive, + ) { + Ok(_) => Ok(amount), + Err(_) => Err(DispatchError::Other( + "Bad amount to relay chain value conversion", + )), + } + } + AssetIds::NativeAssetId(NativeCurrency::Parent) => { + match as fungibles::Transfer>::transfer( + AssetIds::NativeAssetId(NativeCurrency::Parent), + source, + dest, + amount.into(), + keep_alive, + ) { + Ok(_) => Ok(amount), + Err(e) => Err(e), + } + } + AssetIds::ForeignAssetId(fid) => { + let target_collection_id = match >::get(fid) { + Some(v) => v, + None => { + return Err(DispatchError::Other( + "Associated collection not found for asset", + )) + } + }; + let collection = + FungibleHandle::cast(>::try_get(target_collection_id)?); + + pallet_fungible::Pallet::::transfer( + &collection, + &T::CrossAccountId::from_sub(source.clone()), + &T::CrossAccountId::from_sub(dest.clone()), + amount.into(), + &Value::new(0), + )?; + + Ok(amount) + } + } + } +} --- /dev/null +++ b/pallets/foreign-assets/src/lib.rs @@ -0,0 +1,498 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +//! # Foreign assets +//! +//! - [`Config`] +//! - [`Call`] +//! - [`Pallet`] +//! +//! ## Overview +//! +//! The foreign assests pallet provides functions for: +//! +//! - Local and foreign assets management. The foreign assets can be updated without runtime upgrade. +//! - Bounds between asset and target collection for cross chain transfer and inner transfers. +//! +//! ## Overview +//! +//! Under construction + +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::unused_unit)] + +use frame_support::{ + dispatch::DispatchResult, + ensure, + pallet_prelude::*, + traits::{fungible, fungibles, Currency, EnsureOrigin}, + RuntimeDebug, +}; +use frame_system::pallet_prelude::*; +use up_data_structs::{CollectionMode}; +use pallet_fungible::{Pallet as PalletFungible}; +use scale_info::{TypeInfo}; +use sp_runtime::{ + traits::{One, Zero}, + ArithmeticError, +}; +use sp_std::{boxed::Box, vec::Vec}; +use up_data_structs::{CollectionId, TokenId, CreateCollectionData}; + +// NOTE:v1::MultiLocation is used in storages, we would need to do migration if upgrade the +// MultiLocation in the future. +use xcm::opaque::latest::prelude::XcmError; +use xcm::{v1::MultiLocation, VersionedMultiLocation}; +use xcm_executor::{traits::WeightTrader, Assets}; + +use pallet_common::erc::CrossAccountId; + +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; + +// TODO: Move to primitives +// Id of native currency. +// 0 - QTZ\UNQ +// 1 - KSM\DOT +#[derive( + Clone, + Copy, + Eq, + PartialEq, + PartialOrd, + Ord, + MaxEncodedLen, + RuntimeDebug, + Encode, + Decode, + TypeInfo, +)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub enum NativeCurrency { + Here = 0, + Parent = 1, +} + +#[derive( + Clone, + Copy, + Eq, + PartialEq, + PartialOrd, + Ord, + MaxEncodedLen, + RuntimeDebug, + Encode, + Decode, + TypeInfo, +)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub enum AssetIds { + ForeignAssetId(ForeignAssetId), + NativeAssetId(NativeCurrency), +} + +pub trait TryAsForeign { + fn try_as_foreign(asset: T) -> Option; +} + +impl TryAsForeign for AssetIds { + fn try_as_foreign(asset: AssetIds) -> Option { + match asset { + AssetIds::ForeignAssetId(id) => Some(id), + _ => None, + } + } +} + +pub type ForeignAssetId = u32; +pub type CurrencyId = AssetIds; + +mod impl_fungibles; +pub mod weights; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +pub use module::*; +pub use weights::WeightInfo; + +/// Type alias for currency balance. +pub type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; + +/// A mapping between ForeignAssetId and AssetMetadata. +pub trait AssetIdMapping { + /// Returns the AssetMetadata associated with a given ForeignAssetId. + fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option; + /// Returns the MultiLocation associated with a given ForeignAssetId. + fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option; + /// Returns the CurrencyId associated with a given MultiLocation. + fn get_currency_id(multi_location: MultiLocation) -> Option; +} + +pub struct XcmForeignAssetIdMapping(sp_std::marker::PhantomData); + +impl AssetIdMapping>> + for XcmForeignAssetIdMapping +{ + fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option>> { + log::trace!(target: "fassets::asset_metadatas", "call"); + Pallet::::asset_metadatas(AssetIds::ForeignAssetId(foreign_asset_id)) + } + + fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option { + log::trace!(target: "fassets::get_multi_location", "call"); + Pallet::::foreign_asset_locations(foreign_asset_id) + } + + fn get_currency_id(multi_location: MultiLocation) -> Option { + log::trace!(target: "fassets::get_currency_id", "call"); + Some(AssetIds::ForeignAssetId( + Pallet::::location_to_currency_ids(multi_location).unwrap_or(0), + )) + } +} + +#[frame_support::pallet] +pub mod module { + use super::*; + + #[pallet::config] + pub trait Config: + frame_system::Config + + pallet_common::Config + + pallet_fungible::Config + + orml_tokens::Config + + pallet_balances::Config + { + /// The overarching event type. + type Event: From> + IsType<::Event>; + + /// Currency type for withdraw and balance storage. + type Currency: Currency; + + /// Required origin for registering asset. + type RegisterOrigin: EnsureOrigin; + + /// Weight information for the extrinsics in this module. + type WeightInfo: WeightInfo; + } + + #[derive(Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, TypeInfo)] + pub struct AssetMetadata { + pub name: Vec, + pub symbol: Vec, + pub decimals: u8, + pub minimal_balance: Balance, + } + + #[pallet::error] + pub enum Error { + /// The given location could not be used (e.g. because it cannot be expressed in the + /// desired version of XCM). + BadLocation, + /// MultiLocation existed + MultiLocationExisted, + /// AssetId not exists + AssetIdNotExists, + /// AssetId exists + AssetIdExisted, + } + + #[pallet::event] + #[pallet::generate_deposit(fn deposit_event)] + pub enum Event { + /// The foreign asset registered. + ForeignAssetRegistered { + asset_id: ForeignAssetId, + asset_address: MultiLocation, + metadata: AssetMetadata>, + }, + /// The foreign asset updated. + ForeignAssetUpdated { + asset_id: ForeignAssetId, + asset_address: MultiLocation, + metadata: AssetMetadata>, + }, + /// The asset registered. + AssetRegistered { + asset_id: AssetIds, + metadata: AssetMetadata>, + }, + /// The asset updated. + AssetUpdated { + asset_id: AssetIds, + metadata: AssetMetadata>, + }, + } + + /// Next available Foreign AssetId ID. + /// + /// NextForeignAssetId: ForeignAssetId + #[pallet::storage] + #[pallet::getter(fn next_foreign_asset_id)] + pub type NextForeignAssetId = StorageValue<_, ForeignAssetId, ValueQuery>; + /// The storages for MultiLocations. + /// + /// ForeignAssetLocations: map ForeignAssetId => Option + #[pallet::storage] + #[pallet::getter(fn foreign_asset_locations)] + pub type ForeignAssetLocations = + StorageMap<_, Twox64Concat, ForeignAssetId, MultiLocation, OptionQuery>; + + /// The storages for CurrencyIds. + /// + /// LocationToCurrencyIds: map MultiLocation => Option + #[pallet::storage] + #[pallet::getter(fn location_to_currency_ids)] + pub type LocationToCurrencyIds = + StorageMap<_, Twox64Concat, MultiLocation, ForeignAssetId, OptionQuery>; + + /// The storages for AssetMetadatas. + /// + /// AssetMetadatas: map AssetIds => Option + #[pallet::storage] + #[pallet::getter(fn asset_metadatas)] + pub type AssetMetadatas = + StorageMap<_, Twox64Concat, AssetIds, AssetMetadata>, OptionQuery>; + + /// The storages for assets to fungible collection binding + /// + #[pallet::storage] + #[pallet::getter(fn asset_binding)] + pub type AssetBinding = + StorageMap<_, Twox64Concat, ForeignAssetId, CollectionId, OptionQuery>; + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::call] + impl Pallet { + #[pallet::weight(::WeightInfo::register_foreign_asset())] + pub fn register_foreign_asset( + origin: OriginFor, + owner: T::AccountId, + location: Box, + metadata: Box>>, + ) -> DispatchResult { + T::RegisterOrigin::ensure_origin(origin.clone())?; + + let location: MultiLocation = (*location) + .try_into() + .map_err(|()| Error::::BadLocation)?; + + let md = metadata.clone(); + let name: Vec = md.name.into_iter().map(|x| x as u16).collect::>(); + let mut description: Vec = "Foreign assets collection for " + .encode_utf16() + .collect::>(); + description.append(&mut name.clone()); + + let data: CreateCollectionData = CreateCollectionData { + name: name.try_into().unwrap(), + description: description.try_into().unwrap(), + mode: CollectionMode::Fungible(md.decimals), + ..Default::default() + }; + + let bounded_collection_id = >::init_foreign_collection( + CrossAccountId::from_sub(owner), + data, + )?; + let foreign_asset_id = + Self::do_register_foreign_asset(&location, &metadata, bounded_collection_id)?; + + Self::deposit_event(Event::::ForeignAssetRegistered { + asset_id: foreign_asset_id, + asset_address: location, + metadata: *metadata, + }); + Ok(()) + } + + #[pallet::weight(::WeightInfo::update_foreign_asset())] + pub fn update_foreign_asset( + origin: OriginFor, + foreign_asset_id: ForeignAssetId, + location: Box, + metadata: Box>>, + ) -> DispatchResult { + T::RegisterOrigin::ensure_origin(origin)?; + + let location: MultiLocation = (*location) + .try_into() + .map_err(|()| Error::::BadLocation)?; + Self::do_update_foreign_asset(foreign_asset_id, &location, &metadata)?; + + Self::deposit_event(Event::::ForeignAssetUpdated { + asset_id: foreign_asset_id, + asset_address: location, + metadata: *metadata, + }); + Ok(()) + } + } +} + +impl Pallet { + fn get_next_foreign_asset_id() -> Result { + NextForeignAssetId::::try_mutate(|current| -> Result { + let id = *current; + *current = current + .checked_add(One::one()) + .ok_or(ArithmeticError::Overflow)?; + Ok(id) + }) + } + + fn do_register_foreign_asset( + location: &MultiLocation, + metadata: &AssetMetadata>, + bounded_collection_id: CollectionId, + ) -> Result { + let foreign_asset_id = Self::get_next_foreign_asset_id()?; + LocationToCurrencyIds::::try_mutate(location, |maybe_currency_ids| -> DispatchResult { + ensure!( + maybe_currency_ids.is_none(), + Error::::MultiLocationExisted + ); + *maybe_currency_ids = Some(foreign_asset_id); + // *maybe_currency_ids = Some(CurrencyId::ForeignAsset(foreign_asset_id)); + + ForeignAssetLocations::::try_mutate( + foreign_asset_id, + |maybe_location| -> DispatchResult { + ensure!(maybe_location.is_none(), Error::::MultiLocationExisted); + *maybe_location = Some(location.clone()); + + AssetMetadatas::::try_mutate( + AssetIds::ForeignAssetId(foreign_asset_id), + |maybe_asset_metadatas| -> DispatchResult { + ensure!(maybe_asset_metadatas.is_none(), Error::::AssetIdExisted); + *maybe_asset_metadatas = Some(metadata.clone()); + Ok(()) + }, + ) + }, + )?; + + AssetBinding::::try_mutate(foreign_asset_id, |collection_id| -> DispatchResult { + *collection_id = Some(bounded_collection_id); + Ok(()) + }) + })?; + + Ok(foreign_asset_id) + } + + fn do_update_foreign_asset( + foreign_asset_id: ForeignAssetId, + location: &MultiLocation, + metadata: &AssetMetadata>, + ) -> DispatchResult { + ForeignAssetLocations::::try_mutate( + foreign_asset_id, + |maybe_multi_locations| -> DispatchResult { + let old_multi_locations = maybe_multi_locations + .as_mut() + .ok_or(Error::::AssetIdNotExists)?; + + AssetMetadatas::::try_mutate( + AssetIds::ForeignAssetId(foreign_asset_id), + |maybe_asset_metadatas| -> DispatchResult { + ensure!( + maybe_asset_metadatas.is_some(), + Error::::AssetIdNotExists + ); + + // modify location + if location != old_multi_locations { + LocationToCurrencyIds::::remove(old_multi_locations.clone()); + LocationToCurrencyIds::::try_mutate( + location, + |maybe_currency_ids| -> DispatchResult { + ensure!( + maybe_currency_ids.is_none(), + Error::::MultiLocationExisted + ); + // *maybe_currency_ids = Some(CurrencyId::ForeignAsset(foreign_asset_id)); + *maybe_currency_ids = Some(foreign_asset_id); + Ok(()) + }, + )?; + } + *maybe_asset_metadatas = Some(metadata.clone()); + *old_multi_locations = location.clone(); + Ok(()) + }, + ) + }, + ) + } +} + +pub use frame_support::{ + traits::{ + fungibles::{Balanced, CreditOf}, + tokens::currency::Currency as CurrencyT, + OnUnbalanced as OnUnbalancedT, + }, + weights::{WeightToFeePolynomial, WeightToFee}, +}; + +pub struct FreeForAll< + WeightToFee: WeightToFeePolynomial, + AssetId: Get, + AccountId, + Currency: CurrencyT, + OnUnbalanced: OnUnbalancedT, +>( + Weight, + Currency::Balance, + PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>, +); + +impl< + WeightToFee: WeightToFeePolynomial, + AssetId: Get, + AccountId, + Currency: CurrencyT, + OnUnbalanced: OnUnbalancedT, + > WeightTrader for FreeForAll +{ + fn new() -> Self { + Self(0, Zero::zero(), PhantomData) + } + + fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result { + log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment); + Ok(payment) + } +} +impl Drop + for FreeForAll +where + WeightToFee: WeightToFeePolynomial, + AssetId: Get, + Currency: CurrencyT, + OnUnbalanced: OnUnbalancedT, +{ + fn drop(&mut self) { + OnUnbalanced::on_unbalanced(Currency::issue(self.1)); + } +} --- /dev/null +++ b/pallets/foreign-assets/src/weights.rs @@ -0,0 +1,94 @@ +// Template adopted from https://github.com/paritytech/substrate/blob/master/.maintain/frame-weight-template.hbs + +//! Autogenerated weights for pallet_foreign_assets +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-09-16, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// target/release/unique-collator +// benchmark +// pallet +// --pallet +// pallet-foreign-assets +// --wasm-execution +// compiled +// --extrinsic +// * +// --template +// .maintain/frame-weight-template.hbs +// --steps=50 +// --repeat=80 +// --heap-pages=4096 +// --output=./pallets/foreign-assets/src/weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] +#![allow(clippy::unnecessary_cast)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_foreign_assets. +pub trait WeightInfo { + fn register_foreign_asset() -> Weight; + fn update_foreign_asset() -> Weight; +} + +/// Weights for pallet_foreign_assets using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + // Storage: Common CreatedCollectionCount (r:1 w:1) + // Storage: Common DestroyedCollectionCount (r:1 w:0) + // Storage: System Account (r:2 w:2) + // Storage: ForeignAssets NextForeignAssetId (r:1 w:1) + // Storage: ForeignAssets LocationToCurrencyIds (r:1 w:1) + // Storage: ForeignAssets ForeignAssetLocations (r:1 w:1) + // Storage: ForeignAssets AssetMetadatas (r:1 w:1) + // Storage: ForeignAssets AssetBinding (r:1 w:1) + // Storage: Common CollectionPropertyPermissions (r:0 w:1) + // Storage: Common CollectionProperties (r:0 w:1) + // Storage: Common CollectionById (r:0 w:1) + fn register_foreign_asset() -> Weight { + (52_161_000 as Weight) + .saturating_add(T::DbWeight::get().reads(9 as Weight)) + .saturating_add(T::DbWeight::get().writes(11 as Weight)) + } + // Storage: ForeignAssets ForeignAssetLocations (r:1 w:1) + // Storage: ForeignAssets AssetMetadatas (r:1 w:1) + fn update_foreign_asset() -> Weight { + (19_111_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + // Storage: Common CreatedCollectionCount (r:1 w:1) + // Storage: Common DestroyedCollectionCount (r:1 w:0) + // Storage: System Account (r:2 w:2) + // Storage: ForeignAssets NextForeignAssetId (r:1 w:1) + // Storage: ForeignAssets LocationToCurrencyIds (r:1 w:1) + // Storage: ForeignAssets ForeignAssetLocations (r:1 w:1) + // Storage: ForeignAssets AssetMetadatas (r:1 w:1) + // Storage: ForeignAssets AssetBinding (r:1 w:1) + // Storage: Common CollectionPropertyPermissions (r:0 w:1) + // Storage: Common CollectionProperties (r:0 w:1) + // Storage: Common CollectionById (r:0 w:1) + fn register_foreign_asset() -> Weight { + (52_161_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(9 as Weight)) + .saturating_add(RocksDbWeight::get().writes(11 as Weight)) + } + // Storage: ForeignAssets ForeignAssetLocations (r:1 w:1) + // Storage: ForeignAssets AssetMetadatas (r:1 w:1) + fn update_foreign_asset() -> Weight { + (19_111_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + } +} --- a/pallets/fungible/src/erc.rs +++ b/pallets/fungible/src/erc.rs @@ -199,7 +199,7 @@ ERC20, ERC20Mintable, ERC20UniqueExtensions, - Collection(common_mut, CollectionHandle), + Collection(via(common_mut returns CollectionHandle)), ) )] impl FungibleHandle where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {} --- a/pallets/fungible/src/lib.rs +++ b/pallets/fungible/src/lib.rs @@ -83,8 +83,8 @@ use frame_support::{ensure}; use pallet_evm::account::CrossAccountId; use up_data_structs::{ - AccessMode, CollectionId, TokenId, CreateCollectionData, mapping::TokenAddressMapping, - budget::Budget, + AccessMode, CollectionId, CollectionFlags, TokenId, CreateCollectionData, + mapping::TokenAddressMapping, budget::Budget, }; use pallet_common::{ Error as CommonError, Event as CommonEvent, Pallet as PalletCommon, @@ -212,9 +212,25 @@ owner: T::CrossAccountId, data: CreateCollectionData, ) -> Result { - >::init_collection(owner, data, false) + >::init_collection(owner, data, CollectionFlags::default()) } + /// Initializes the collection with ForeignCollection flag. Returns [CollectionId] on success, [DispatchError] otherwise. + pub fn init_foreign_collection( + owner: T::CrossAccountId, + data: CreateCollectionData, + ) -> Result { + let id = >::init_collection( + owner, + data, + CollectionFlags { + foreign: true, + ..Default::default() + }, + )?; + Ok(id) + } + /// Destroys a collection. pub fn destroy_collection( collection: FungibleHandle, @@ -257,6 +273,9 @@ .checked_sub(amount) .ok_or(>::TokenValueTooLow)?; + // Foreign collection check + ensure!(!collection.flags.foreign, >::NoPermission); + if collection.permissions.access() == AccessMode::AllowList { collection.check_allowlist(owner)?; } @@ -288,6 +307,46 @@ Ok(()) } + /// Burns the specified amount of the token. + pub fn burn_foreign( + collection: &FungibleHandle, + owner: &T::CrossAccountId, + amount: u128, + ) -> DispatchResult { + let total_supply = >::get(collection.id) + .checked_sub(amount) + .ok_or(>::TokenValueTooLow)?; + + let balance = >::get((collection.id, owner)) + .checked_sub(amount) + .ok_or(>::TokenValueTooLow)?; + // ========= + + if balance == 0 { + >::remove((collection.id, owner)); + >::unnest_if_nested(owner, collection.id, TokenId::default()); + } else { + >::insert((collection.id, owner), balance); + } + >::insert(collection.id, total_supply); + + >::deposit_log( + ERC20Events::Transfer { + from: *owner.as_eth(), + to: H160::default(), + value: amount.into(), + } + .to_log(collection_id_to_address(collection.id)), + ); + >::deposit_event(CommonEvent::ItemDestroyed( + collection.id, + TokenId::default(), + owner.clone(), + amount, + )); + Ok(()) + } + /// Transfers the specified amount of tokens. Will check that /// the transfer is allowed for the token. /// @@ -366,25 +425,14 @@ } /// Minting tokens for multiple IDs. - /// See [`create_item`][`Pallet::create_item`] for more details. - pub fn create_multiple_items( + /// It is a utility function used in [`create_multiple_items`][`Pallet::create_multiple_items`] + /// and [`create_multiple_items_foreign`][`Pallet::create_multiple_items_foreign`] + pub fn create_multiple_items_common( collection: &FungibleHandle, sender: &T::CrossAccountId, data: BTreeMap, nesting_budget: &dyn Budget, ) -> DispatchResult { - if !collection.is_owner_or_admin(sender) { - ensure!( - collection.permissions.mint_mode(), - >::PublicMintingNotAllowed - ); - collection.check_allowlist(sender)?; - - for (owner, _) in data.iter() { - collection.check_allowlist(owner)?; - } - } - let total_supply = data .iter() .map(|(_, v)| *v) @@ -442,6 +490,43 @@ Ok(()) } + /// Minting tokens for multiple IDs. + /// See [`create_item`][`Pallet::create_item`] for more details. + pub fn create_multiple_items( + collection: &FungibleHandle, + sender: &T::CrossAccountId, + data: BTreeMap, + nesting_budget: &dyn Budget, + ) -> DispatchResult { + // Foreign collection check + ensure!(!collection.flags.foreign, >::NoPermission); + + if !collection.is_owner_or_admin(sender) { + ensure!( + collection.permissions.mint_mode(), + >::PublicMintingNotAllowed + ); + collection.check_allowlist(sender)?; + + for (owner, _) in data.iter() { + collection.check_allowlist(owner)?; + } + } + + Self::create_multiple_items_common(collection, sender, data, nesting_budget) + } + + /// Minting tokens for multiple IDs. + /// See [`create_item_foreign`][`Pallet::create_item_foreign`] for more details. + pub fn create_multiple_items_foreign( + collection: &FungibleHandle, + sender: &T::CrossAccountId, + data: BTreeMap, + nesting_budget: &dyn Budget, + ) -> DispatchResult { + Self::create_multiple_items_common(collection, sender, data, nesting_budget) + } + fn set_allowance_unchecked( collection: &FungibleHandle, owner: &T::CrossAccountId, @@ -615,6 +700,24 @@ ) } + /// Creates fungible token. + /// + /// - `data`: Contains user who will become the owners of the tokens and amount + /// of tokens he will receive. + pub fn create_item_foreign( + collection: &FungibleHandle, + sender: &T::CrossAccountId, + data: CreateItemData, + nesting_budget: &dyn Budget, + ) -> DispatchResult { + Self::create_multiple_items_foreign( + collection, + sender, + [(data.0, data.1)].into_iter().collect(), + nesting_budget, + ) + } + /// Returns 10 tokens owners in no particular order /// /// There is no direct way to get token holders in ascending order, --- a/pallets/fungible/src/stubs/UniqueFungible.sol +++ b/pallets/fungible/src/stubs/UniqueFungible.sol @@ -10,11 +10,7 @@ } contract ERC165 is Dummy { - function supportsInterface(bytes4 interfaceID) - external - view - returns (bool) - { + function supportsInterface(bytes4 interfaceID) external view returns (bool) { require(false, stub_error); interfaceID; return true; @@ -22,7 +18,7 @@ } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0x9f70d4e0 +/// @dev the ERC-165 identifier for this interface is 0x47dbc105 contract Collection is Dummy, ERC165 { /// Set collection property. /// @@ -30,9 +26,7 @@ /// @param value Propery value. /// @dev EVM selector for this function is: 0x2f073f66, /// or in textual repr: setCollectionProperty(string,bytes) - function setCollectionProperty(string memory key, bytes memory value) - public - { + function setCollectionProperty(string memory key, bytes memory value) public { require(false, stub_error); key; value; @@ -58,11 +52,7 @@ /// @return bytes The property corresponding to the key. /// @dev EVM selector for this function is: 0xcf24fd6d, /// or in textual repr: collectionProperty(string) - function collectionProperty(string memory key) - public - view - returns (bytes memory) - { + function collectionProperty(string memory key) public view returns (bytes memory) { require(false, stub_error); key; dummy; @@ -95,6 +85,7 @@ dummy = 0; } + /// Whether there is a pending sponsor. /// @dev EVM selector for this function is: 0x058ac185, /// or in textual repr: hasCollectionPendingSponsor() function hasCollectionPendingSponsor() public view returns (bool) { @@ -124,9 +115,9 @@ /// Get current sponsor. /// /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. - /// @dev EVM selector for this function is: 0xb66bbc14, - /// or in textual repr: getCollectionSponsor() - function getCollectionSponsor() public view returns (Tuple6 memory) { + /// @dev EVM selector for this function is: 0x6ec0a9f1, + /// or in textual repr: collectionSponsor() + function collectionSponsor() public view returns (Tuple6 memory) { require(false, stub_error); dummy; return Tuple6(0x0000000000000000000000000000000000000000, 0); @@ -234,9 +225,7 @@ /// @param collections Addresses of collections that will be available for nesting. /// @dev EVM selector for this function is: 0x64872396, /// or in textual repr: setCollectionNesting(bool,address[]) - function setCollectionNesting(bool enable, address[] memory collections) - public - { + function setCollectionNesting(bool enable, address[] memory collections) public { require(false, stub_error); enable; collections; @@ -353,9 +342,9 @@ /// @return `Fungible` or `NFT` or `ReFungible` /// @dev EVM selector for this function is: 0xd34b55b8, /// or in textual repr: uniqueCollectionType() - function uniqueCollectionType() public returns (string memory) { + function uniqueCollectionType() public view returns (string memory) { require(false, stub_error); - dummy = 0; + dummy; return ""; } @@ -450,11 +439,7 @@ /// @dev inlined interface contract ERC20Events { event Transfer(address indexed from, address indexed to, uint256 value); - event Approval( - address indexed owner, - address indexed spender, - uint256 value - ); + event Approval(address indexed owner, address indexed spender, uint256 value); } /// @dev the ERC-165 identifier for this interface is 0x942e8b22 @@ -537,11 +522,7 @@ /// @dev EVM selector for this function is: 0xdd62ed3e, /// or in textual repr: allowance(address,address) - function allowance(address owner, address spender) - public - view - returns (uint256) - { + function allowance(address owner, address spender) public view returns (uint256) { require(false, stub_error); owner; spender; @@ -550,11 +531,4 @@ } } -contract UniqueFungible is - Dummy, - ERC165, - ERC20, - ERC20Mintable, - ERC20UniqueExtensions, - Collection -{} +contract UniqueFungible is Dummy, ERC165, ERC20, ERC20Mintable, ERC20UniqueExtensions, Collection {} --- a/pallets/nonfungible/src/erc.rs +++ b/pallets/nonfungible/src/erc.rs @@ -736,7 +736,7 @@ ERC721UniqueExtensions, ERC721Mintable, ERC721Burnable, - Collection(common_mut, CollectionHandle), + Collection(via(common_mut returns CollectionHandle)), TokenProperties, ) )] --- a/pallets/nonfungible/src/lib.rs +++ b/pallets/nonfungible/src/lib.rs @@ -100,10 +100,10 @@ weights::{PostDispatchInfo, Pays}, }; use up_data_structs::{ - AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData, - mapping::TokenAddressMapping, budget::Budget, Property, PropertyPermission, PropertyKey, - PropertyValue, PropertyKeyPermission, Properties, PropertyScope, TrySetProperty, TokenChild, - AuxPropertyValue, + AccessMode, CollectionId, CollectionFlags, CustomDataLimit, TokenId, CreateCollectionData, + CreateNftExData, mapping::TokenAddressMapping, budget::Budget, Property, PropertyPermission, + PropertyKey, PropertyValue, PropertyKeyPermission, Properties, PropertyScope, TrySetProperty, + TokenChild, AuxPropertyValue, }; use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm}; use pallet_common::{ @@ -408,7 +408,14 @@ data: CreateCollectionData, is_external: bool, ) -> Result { - >::init_collection(owner, data, is_external) + >::init_collection( + owner, + data, + CollectionFlags { + external: is_external, + ..Default::default() + }, + ) } /// Destroy NFT collection --- a/pallets/nonfungible/src/stubs/UniqueNFT.sol +++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol @@ -10,11 +10,7 @@ } contract ERC165 is Dummy { - function supportsInterface(bytes4 interfaceID) - external - view - returns (bool) - { + function supportsInterface(bytes4 interfaceID) external view returns (bool) { require(false, stub_error); interfaceID; return true; @@ -85,11 +81,7 @@ /// @return Property value bytes /// @dev EVM selector for this function is: 0x7228c327, /// or in textual repr: property(uint256,string) - function property(uint256 tokenId, string memory key) - public - view - returns (bytes memory) - { + function property(uint256 tokenId, string memory key) public view returns (bytes memory) { require(false, stub_error); tokenId; key; @@ -99,7 +91,7 @@ } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0x9f70d4e0 +/// @dev the ERC-165 identifier for this interface is 0x47dbc105 contract Collection is Dummy, ERC165 { /// Set collection property. /// @@ -107,9 +99,7 @@ /// @param value Propery value. /// @dev EVM selector for this function is: 0x2f073f66, /// or in textual repr: setCollectionProperty(string,bytes) - function setCollectionProperty(string memory key, bytes memory value) - public - { + function setCollectionProperty(string memory key, bytes memory value) public { require(false, stub_error); key; value; @@ -135,11 +125,7 @@ /// @return bytes The property corresponding to the key. /// @dev EVM selector for this function is: 0xcf24fd6d, /// or in textual repr: collectionProperty(string) - function collectionProperty(string memory key) - public - view - returns (bytes memory) - { + function collectionProperty(string memory key) public view returns (bytes memory) { require(false, stub_error); key; dummy; @@ -172,6 +158,7 @@ dummy = 0; } + /// Whether there is a pending sponsor. /// @dev EVM selector for this function is: 0x058ac185, /// or in textual repr: hasCollectionPendingSponsor() function hasCollectionPendingSponsor() public view returns (bool) { @@ -201,9 +188,9 @@ /// Get current sponsor. /// /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. - /// @dev EVM selector for this function is: 0xb66bbc14, - /// or in textual repr: getCollectionSponsor() - function getCollectionSponsor() public view returns (Tuple17 memory) { + /// @dev EVM selector for this function is: 0x6ec0a9f1, + /// or in textual repr: collectionSponsor() + function collectionSponsor() public view returns (Tuple17 memory) { require(false, stub_error); dummy; return Tuple17(0x0000000000000000000000000000000000000000, 0); @@ -311,9 +298,7 @@ /// @param collections Addresses of collections that will be available for nesting. /// @dev EVM selector for this function is: 0x64872396, /// or in textual repr: setCollectionNesting(bool,address[]) - function setCollectionNesting(bool enable, address[] memory collections) - public - { + function setCollectionNesting(bool enable, address[] memory collections) public { require(false, stub_error); enable; collections; @@ -430,9 +415,9 @@ /// @return `Fungible` or `NFT` or `ReFungible` /// @dev EVM selector for this function is: 0xd34b55b8, /// or in textual repr: uniqueCollectionType() - function uniqueCollectionType() public returns (string memory) { + function uniqueCollectionType() public view returns (string memory) { require(false, stub_error); - dummy = 0; + dummy; return ""; } @@ -605,10 +590,7 @@ /// @param tokenIds IDs of the minted NFTs /// @dev EVM selector for this function is: 0x44a9945e, /// or in textual repr: mintBulk(address,uint256[]) - function mintBulk(address to, uint256[] memory tokenIds) - public - returns (bool) - { + function mintBulk(address to, uint256[] memory tokenIds) public returns (bool) { require(false, stub_error); to; tokenIds; @@ -623,10 +605,7 @@ /// @param tokens array of pairs of token ID and token URI for minted tokens /// @dev EVM selector for this function is: 0x36543006, /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[]) - function mintBulkWithTokenURI(address to, Tuple8[] memory tokens) - public - returns (bool) - { + function mintBulkWithTokenURI(address to, Tuple8[] memory tokens) public returns (bool) { require(false, stub_error); to; tokens; @@ -661,11 +640,7 @@ /// @dev Not implemented /// @dev EVM selector for this function is: 0x2f745c59, /// or in textual repr: tokenOfOwnerByIndex(address,uint256) - function tokenOfOwnerByIndex(address owner, uint256 index) - public - view - returns (uint256) - { + function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256) { require(false, stub_error); owner; index; @@ -728,21 +703,9 @@ /// @dev inlined interface contract ERC721Events { - event Transfer( - address indexed from, - address indexed to, - uint256 indexed tokenId - ); - event Approval( - address indexed owner, - address indexed approved, - uint256 indexed tokenId - ); - event ApprovalForAll( - address indexed owner, - address indexed operator, - bool approved - ); + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); } /// @title ERC-721 Non-Fungible Token Standard @@ -870,11 +833,7 @@ /// @dev Not implemented /// @dev EVM selector for this function is: 0xe985e9c5, /// or in textual repr: isApprovedForAll(address,address) - function isApprovedForAll(address owner, address operator) - public - view - returns (address) - { + function isApprovedForAll(address owner, address operator) public view returns (address) { require(false, stub_error); owner; operator; --- a/pallets/refungible/src/erc.rs +++ b/pallets/refungible/src/erc.rs @@ -785,7 +785,7 @@ ERC721UniqueExtensions, ERC721Mintable, ERC721Burnable, - Collection(common_mut, CollectionHandle), + Collection(via(common_mut returns CollectionHandle)), TokenProperties, ) )] --- a/pallets/refungible/src/lib.rs +++ b/pallets/refungible/src/lib.rs @@ -112,10 +112,10 @@ use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome}; use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap}; use up_data_structs::{ - AccessMode, budget::Budget, CollectionId, CollectionMode, CollectionPropertiesVec, - CreateCollectionData, CustomDataLimit, mapping::TokenAddressMapping, MAX_ITEMS_PER_BATCH, - MAX_REFUNGIBLE_PIECES, Property, PropertyKey, PropertyKeyPermission, PropertyPermission, - PropertyScope, PropertyValue, TokenId, TrySetProperty, + AccessMode, budget::Budget, CollectionId, CollectionMode, CollectionFlags, + CollectionPropertiesVec, CreateCollectionData, CustomDataLimit, mapping::TokenAddressMapping, + MAX_ITEMS_PER_BATCH, MAX_REFUNGIBLE_PIECES, Property, PropertyKey, PropertyKeyPermission, + PropertyPermission, PropertyScope, PropertyValue, TokenId, TrySetProperty, }; use frame_support::BoundedBTreeMap; use derivative::Derivative; @@ -375,7 +375,7 @@ owner: T::CrossAccountId, data: CreateCollectionData, ) -> Result { - >::init_collection(owner, data, false) + >::init_collection(owner, data, CollectionFlags::default()) } /// Destroy RFT collection --- a/pallets/refungible/src/stubs/UniqueRefungible.sol +++ b/pallets/refungible/src/stubs/UniqueRefungible.sol @@ -10,11 +10,7 @@ } contract ERC165 is Dummy { - function supportsInterface(bytes4 interfaceID) - external - view - returns (bool) - { + function supportsInterface(bytes4 interfaceID) external view returns (bool) { require(false, stub_error); interfaceID; return true; @@ -85,11 +81,7 @@ /// @return Property value bytes /// @dev EVM selector for this function is: 0x7228c327, /// or in textual repr: property(uint256,string) - function property(uint256 tokenId, string memory key) - public - view - returns (bytes memory) - { + function property(uint256 tokenId, string memory key) public view returns (bytes memory) { require(false, stub_error); tokenId; key; @@ -99,7 +91,7 @@ } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0x9f70d4e0 +/// @dev the ERC-165 identifier for this interface is 0x47dbc105 contract Collection is Dummy, ERC165 { /// Set collection property. /// @@ -107,9 +99,7 @@ /// @param value Propery value. /// @dev EVM selector for this function is: 0x2f073f66, /// or in textual repr: setCollectionProperty(string,bytes) - function setCollectionProperty(string memory key, bytes memory value) - public - { + function setCollectionProperty(string memory key, bytes memory value) public { require(false, stub_error); key; value; @@ -135,11 +125,7 @@ /// @return bytes The property corresponding to the key. /// @dev EVM selector for this function is: 0xcf24fd6d, /// or in textual repr: collectionProperty(string) - function collectionProperty(string memory key) - public - view - returns (bytes memory) - { + function collectionProperty(string memory key) public view returns (bytes memory) { require(false, stub_error); key; dummy; @@ -172,6 +158,7 @@ dummy = 0; } + /// Whether there is a pending sponsor. /// @dev EVM selector for this function is: 0x058ac185, /// or in textual repr: hasCollectionPendingSponsor() function hasCollectionPendingSponsor() public view returns (bool) { @@ -201,9 +188,9 @@ /// Get current sponsor. /// /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. - /// @dev EVM selector for this function is: 0xb66bbc14, - /// or in textual repr: getCollectionSponsor() - function getCollectionSponsor() public view returns (Tuple17 memory) { + /// @dev EVM selector for this function is: 0x6ec0a9f1, + /// or in textual repr: collectionSponsor() + function collectionSponsor() public view returns (Tuple17 memory) { require(false, stub_error); dummy; return Tuple17(0x0000000000000000000000000000000000000000, 0); @@ -311,9 +298,7 @@ /// @param collections Addresses of collections that will be available for nesting. /// @dev EVM selector for this function is: 0x64872396, /// or in textual repr: setCollectionNesting(bool,address[]) - function setCollectionNesting(bool enable, address[] memory collections) - public - { + function setCollectionNesting(bool enable, address[] memory collections) public { require(false, stub_error); enable; collections; @@ -430,9 +415,9 @@ /// @return `Fungible` or `NFT` or `ReFungible` /// @dev EVM selector for this function is: 0xd34b55b8, /// or in textual repr: uniqueCollectionType() - function uniqueCollectionType() public returns (string memory) { + function uniqueCollectionType() public view returns (string memory) { require(false, stub_error); - dummy = 0; + dummy; return ""; } @@ -607,10 +592,7 @@ /// @param tokenIds IDs of the minted RFTs /// @dev EVM selector for this function is: 0x44a9945e, /// or in textual repr: mintBulk(address,uint256[]) - function mintBulk(address to, uint256[] memory tokenIds) - public - returns (bool) - { + function mintBulk(address to, uint256[] memory tokenIds) public returns (bool) { require(false, stub_error); to; tokenIds; @@ -625,10 +607,7 @@ /// @param tokens array of pairs of token ID and token URI for minted tokens /// @dev EVM selector for this function is: 0x36543006, /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[]) - function mintBulkWithTokenURI(address to, Tuple8[] memory tokens) - public - returns (bool) - { + function mintBulkWithTokenURI(address to, Tuple8[] memory tokens) public returns (bool) { require(false, stub_error); to; tokens; @@ -675,11 +654,7 @@ /// Not implemented /// @dev EVM selector for this function is: 0x2f745c59, /// or in textual repr: tokenOfOwnerByIndex(address,uint256) - function tokenOfOwnerByIndex(address owner, uint256 index) - public - view - returns (uint256) - { + function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256) { require(false, stub_error); owner; index; @@ -740,21 +715,9 @@ /// @dev inlined interface contract ERC721Events { - event Transfer( - address indexed from, - address indexed to, - uint256 indexed tokenId - ); - event Approval( - address indexed owner, - address indexed approved, - uint256 indexed tokenId - ); - event ApprovalForAll( - address indexed owner, - address indexed operator, - bool approved - ); + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); } /// @title ERC-721 Non-Fungible Token Standard @@ -880,11 +843,7 @@ /// @dev Not implemented /// @dev EVM selector for this function is: 0xe985e9c5, /// or in textual repr: isApprovedForAll(address,address) - function isApprovedForAll(address owner, address operator) - public - view - returns (address) - { + function isApprovedForAll(address owner, address operator) public view returns (address) { require(false, stub_error); owner; operator; --- a/pallets/refungible/src/stubs/UniqueRefungibleToken.sol +++ b/pallets/refungible/src/stubs/UniqueRefungibleToken.sol @@ -10,11 +10,7 @@ } contract ERC165 is Dummy { - function supportsInterface(bytes4 interfaceID) - external - view - returns (bool) - { + function supportsInterface(bytes4 interfaceID) external view returns (bool) { require(false, stub_error); interfaceID; return true; @@ -72,11 +68,7 @@ /// @dev inlined interface contract ERC20Events { event Transfer(address indexed from, address indexed to, uint256 value); - event Approval( - address indexed owner, - address indexed spender, - uint256 value - ); + event Approval(address indexed owner, address indexed spender, uint256 value); } /// @title Standard ERC20 token @@ -188,11 +180,7 @@ /// @return A uint256 specifying the amount of tokens still available for the spender. /// @dev EVM selector for this function is: 0xdd62ed3e, /// or in textual repr: allowance(address,address) - function allowance(address owner, address spender) - public - view - returns (uint256) - { + function allowance(address owner, address spender) public view returns (uint256) { require(false, stub_error); owner; spender; @@ -201,10 +189,4 @@ } } -contract UniqueRefungibleToken is - Dummy, - ERC165, - ERC20, - ERC20UniqueExtensions, - ERC1633 -{} +contract UniqueRefungibleToken is Dummy, ERC165, ERC20, ERC20UniqueExtensions, ERC1633 {} --- a/pallets/scheduler/Cargo.toml +++ b/pallets/scheduler/Cargo.toml @@ -25,7 +25,7 @@ frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.27" } -log = { version = "0.4.14", default-features = false } +log = { version = "0.4.16", default-features = false } [dev-dependencies] sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } --- a/primitives/data-structs/Cargo.toml +++ b/primitives/data-structs/Cargo.toml @@ -27,6 +27,7 @@ struct-versioning = { path = "../../crates/struct-versioning" } pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } rmrk-traits = { default-features = false, path = "../rmrk-traits" } +bondrewd = { version = "0.1.14", features = ["derive"], default-features = false } [features] default = ["std"] --- /dev/null +++ b/primitives/data-structs/src/bondrewd_codec.rs @@ -0,0 +1,31 @@ +//! Integration between bondrewd and parity scale codec +//! Maybe we can move it to scale-codec itself in future? + +#[macro_export] +macro_rules! bondrewd_codec { + ($T:ty) => { + impl Encode for $T { + fn encode_to(&self, dest: &mut O) { + dest.write(&self.into_bytes()) + } + } + impl codec::Decode for $T { + fn decode(from: &mut I) -> Result { + let mut bytes = [0; Self::BYTE_SIZE]; + from.read(&mut bytes)?; + Ok(Self::from_bytes(bytes)) + } + } + impl MaxEncodedLen for $T { + fn max_encoded_len() -> usize { + Self::BYTE_SIZE + } + } + impl TypeInfo for $T { + type Identity = [u8; Self::BYTE_SIZE]; + fn type_info() -> scale_info::Type { + <[u8; Self::BYTE_SIZE] as TypeInfo>::type_info() + } + } + }; +} --- a/primitives/data-structs/src/lib.rs +++ b/primitives/data-structs/src/lib.rs @@ -36,6 +36,7 @@ use sp_core::U256; use sp_runtime::{ArithmeticError, sp_std::prelude::Vec, Permill}; use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; +use bondrewd::Bitfields; use frame_support::{BoundedVec, traits::ConstU32}; use derivative::Derivative; use scale_info::TypeInfo; @@ -54,6 +55,7 @@ FixedPart as RmrkFixedPart, SlotPart as RmrkSlotPart, }; +mod bondrewd_codec; mod bounded; pub mod budget; pub mod mapping; @@ -357,6 +359,21 @@ pub type CollectionDescription = BoundedVec>; pub type CollectionTokenPrefix = BoundedVec>; +#[derive(Bitfields, Clone, Copy, PartialEq, Eq, Debug, Default)] +#[bondrewd(enforce_bytes = 1)] +pub struct CollectionFlags { + /// Tokens in foreign collections can be transferred, but not burnt + #[bondrewd(bits = "0..1")] + pub foreign: bool, + /// External collections can't be managed using `unique` api + #[bondrewd(bits = "7..8")] + pub external: bool, + + #[bondrewd(reserve, bits = "1..7")] + pub reserved: u8, +} +bondrewd_codec!(CollectionFlags); + /// Base structure for represent collection. /// /// Used to provide basic functionality for all types of collections. @@ -404,9 +421,8 @@ #[version(2.., upper(Default::default()))] pub permissions: CollectionPermissions, - /// Marks that this collection is not "unique", and managed from external. - #[version(2.., upper(false))] - pub external_collection: bool, + #[version(2.., upper(Default::default()))] + pub flags: CollectionFlags, #[version(..2)] pub variable_on_chain_schema: BoundedVec>, @@ -454,6 +470,9 @@ /// Is collection read only. pub read_only: bool, + + /// Is collection is foreign. + pub foreign: bool, } /// Data used for create collection. --- a/primitives/data-structs/src/migration.rs +++ b/primitives/data-structs/src/migration.rs @@ -38,3 +38,26 @@ test_to_option(SponsoringRateLimit::SponsoringDisabled); test_to_option(SponsoringRateLimit::Blocks(10)); } + +#[test] +fn collection_flags_have_same_encoding_as_bool() { + use crate::CollectionFlags; + use codec::Encode; + + assert_eq!( + true.encode(), + CollectionFlags { + external: true, + ..Default::default() + } + .encode() + ); + assert_eq!( + false.encode(), + CollectionFlags { + external: false, + ..Default::default() + } + .encode() + ); +} --- a/runtime/common/config/orml.rs +++ b/runtime/common/config/orml.rs @@ -14,19 +14,87 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -use frame_support::parameter_types; +use frame_support::{ + parameter_types, + traits::{Contains, Everything}, + weights::Weight, +}; use frame_system::EnsureSigned; -use crate::{Runtime, Event, RelayChainBlockNumberProvider}; +use orml_traits::{location::AbsoluteReserveProvider, parameter_type_with_key}; +use sp_runtime::traits::Convert; +use xcm::v1::{Junction::*, Junctions::*, MultiLocation, NetworkId}; +use xcm_builder::LocationInverter; +use xcm_executor::XcmExecutor; +use sp_std::{vec, vec::Vec}; +use pallet_foreign_assets::{CurrencyId, NativeCurrency}; +use crate::{ + Runtime, Event, RelayChainBlockNumberProvider, + runtime_common::config::{ + xcm::{ + SelfLocation, Weigher, XcmConfig, Ancestry, + xcm_assets::{CurrencyIdConvert}, + }, + pallets::TreasuryAccountId, + substrate::{MaxLocks, MaxReserves}, + }, +}; + use up_common::{ types::{AccountId, Balance}, constants::*, }; +// Signed version of balance +pub type Amount = i128; + parameter_types! { pub const MinVestedTransfer: Balance = 10 * UNIQUE; pub const MaxVestingSchedules: u32 = 28; + + pub const BaseXcmWeight: Weight = 100_000_000; // TODO: recheck this + pub const MaxAssetsForTransfer: usize = 2; +} + +parameter_type_with_key! { + pub ParachainMinFee: |_location: MultiLocation| -> Option { + Some(100_000_000_000) + }; +} + +parameter_type_with_key! { + pub ExistentialDeposits: |currency_id: CurrencyId| -> Balance { + match currency_id { + CurrencyId::NativeAssetId(symbol) => match symbol { + NativeCurrency::Here => 0, + NativeCurrency::Parent=> 0, + }, + _ => 100_000 + } + }; +} + +pub fn get_all_module_accounts() -> Vec { + vec![TreasuryAccountId::get()] } +pub struct DustRemovalWhitelist; +impl Contains for DustRemovalWhitelist { + fn contains(a: &AccountId) -> bool { + get_all_module_accounts().contains(a) + } +} + +pub struct AccountIdToMultiLocation; +impl Convert for AccountIdToMultiLocation { + fn convert(account: AccountId) -> MultiLocation { + X1(AccountId32 { + network: NetworkId::Any, + id: account.into(), + }) + .into() + } +} + impl orml_vesting::Config for Runtime { type Event = Event; type Currency = pallet_balances::Pallet; @@ -36,3 +104,38 @@ type MaxVestingSchedules = MaxVestingSchedules; type BlockNumberProvider = RelayChainBlockNumberProvider; } + +impl orml_tokens::Config for Runtime { + type Event = Event; + type Balance = Balance; + type Amount = Amount; + type CurrencyId = CurrencyId; + type WeightInfo = (); + type ExistentialDeposits = ExistentialDeposits; + type OnDust = orml_tokens::TransferDust; + type MaxLocks = MaxLocks; + type MaxReserves = MaxReserves; + // TODO: Add all module accounts + type DustRemovalWhitelist = DustRemovalWhitelist; + /// The id type for named reserves. + type ReserveIdentifier = (); + type OnNewTokenAccount = (); + type OnKilledTokenAccount = (); +} + +impl orml_xtokens::Config for Runtime { + type Event = Event; + type Balance = Balance; + type CurrencyId = CurrencyId; + type CurrencyIdConvert = CurrencyIdConvert; + type AccountIdToMultiLocation = AccountIdToMultiLocation; + type SelfLocation = SelfLocation; + type XcmExecutor = XcmExecutor>; + type Weigher = Weigher; + type BaseXcmWeight = BaseXcmWeight; + type LocationInverter = LocationInverter; + type MaxAssetsForTransfer = MaxAssetsForTransfer; + type MinXcmFee = ParachainMinFee; + type MultiLocationsFilter = Everything; + type ReserveProvider = AbsoluteReserveProvider; +} --- /dev/null +++ b/runtime/common/config/pallets/foreign_asset.rs @@ -0,0 +1,9 @@ +use crate::{Runtime, Event, Balances}; +use up_common::types::AccountId; + +impl pallet_foreign_assets::Config for Runtime { + type Event = Event; + type Currency = Balances; + type RegisterOrigin = frame_system::EnsureRoot; + type WeightInfo = pallet_foreign_assets::weights::SubstrateWeight; +} --- a/runtime/common/config/pallets/mod.rs +++ b/runtime/common/config/pallets/mod.rs @@ -40,6 +40,9 @@ #[cfg(feature = "scheduler")] pub mod scheduler; +#[cfg(feature = "foreign-assets")] +pub mod foreign_asset; + #[cfg(feature = "app-promotion")] pub mod app_promotion; --- a/runtime/common/config/xcm.rs +++ /dev/null @@ -1,302 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -use frame_support::{ - traits::{ - tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT, Get, Everything, - }, - weights::{Weight, WeightToFeePolynomial, WeightToFee}, - parameter_types, match_types, -}; -use frame_system::EnsureRoot; -use sp_runtime::{ - traits::{Saturating, CheckedConversion, Zero}, - SaturatedConversion, -}; -use pallet_xcm::XcmPassthrough; -use polkadot_parachain::primitives::Sibling; -use xcm::v1::{BodyId, Junction::*, MultiLocation, NetworkId, Junctions::*}; -use xcm::latest::{ - AssetId::{Concrete}, - Fungibility::Fungible as XcmFungible, - MultiAsset, Error as XcmError, -}; -use xcm_executor::traits::{MatchesFungible, WeightTrader}; -use xcm_builder::{ - AccountId32Aliases, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, - FixedWeightBounds, LocationInverter, NativeAsset, ParentAsSuperuser, RelayChainAsNative, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, ParentIsPreset, -}; -use xcm_executor::{Config, XcmExecutor, Assets}; -use sp_std::marker::PhantomData; -use crate::{ - Runtime, Call, Event, Origin, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, XcmpQueue, -}; -use up_common::{ - types::{AccountId, Balance}, - constants::*, -}; - -parameter_types! { - pub const RelayLocation: MultiLocation = MultiLocation::parent(); - pub const RelayNetwork: NetworkId = NetworkId::Polkadot; - pub RelayOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); - pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); -} - -/// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used -/// when determining ownership of accounts for asset transacting and when attempting to use XCM -/// `Transact` in order to determine the dispatch Origin. -pub type LocationToAccountId = ( - // The parent (Relay-chain) origin converts to the default `AccountId`. - ParentIsPreset, - // Sibling parachain origins convert to AccountId via the `ParaId::into`. - SiblingParachainConvertsVia, - // Straight up local `AccountId32` origins just alias directly to `AccountId`. - AccountId32Aliases, -); - -pub struct OnlySelfCurrency; -impl> MatchesFungible for OnlySelfCurrency { - fn matches_fungible(a: &MultiAsset) -> Option { - match (&a.id, &a.fun) { - (Concrete(_), XcmFungible(ref amount)) => CheckedConversion::checked_from(*amount), - _ => None, - } - } -} - -/// Means for transacting assets on this chain. -pub type LocalAssetTransactor = CurrencyAdapter< - // Use this currency: - Balances, - // Use this currency when it is a fungible asset matching the given location or name: - OnlySelfCurrency, - // Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID: - LocationToAccountId, - // Our chain's account ID type (we can't get away without mentioning it explicitly): - AccountId, - // We don't track any teleports. - (), ->; - -/// No local origins on this chain are allowed to dispatch XCM sends/executions. -pub type LocalOriginToLocation = (SignedToAccountId32,); - -/// The means for routing XCM messages which are not for local execution into the right message -/// queues. -pub type XcmRouter = ( - // Two routers - use UMP to communicate with the relay chain: - cumulus_primitives_utility::ParentAsUmp, - // ..and XCMP to communicate with the sibling chains. - XcmpQueue, -); - -/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, -/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can -/// biases the kind of local `Origin` it will become. -pub type XcmOriginToTransactDispatchOrigin = ( - // Sovereign account converter; this attempts to derive an `AccountId` from the origin location - // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for - // foreign chains who want to have a local sovereign account on this chain which they control. - SovereignSignedViaLocation, - // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when - // recognised. - RelayChainAsNative, - // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when - // recognised. - SiblingParachainAsNative, - // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a - // transaction from the Root origin. - ParentAsSuperuser, - // Native signed account converter; this just converts an `AccountId32` origin into a normal - // `Origin::Signed` origin of the same 32-byte value. - SignedAccountId32AsNative, - // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. - XcmPassthrough, -); - -parameter_types! { - // One XCM operation is 1_000_000 weight - almost certainly a conservative estimate. - pub UnitWeightCost: Weight = 1_000_000; - // 1200 UNIQUEs buy 1 second of weight. - pub const WeightPrice: (MultiLocation, u128) = (MultiLocation::parent(), 1_200 * UNIQUE); - pub const MaxInstructions: u32 = 100; -} - -match_types! { - pub type ParentOrParentsUnitPlurality: impl Contains = { - MultiLocation { parents: 1, interior: Here } | - MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Unit, .. }) } - }; -} - -pub type Barrier = ( - TakeWeightCredit, - AllowTopLevelPaidExecutionFrom, - // ^^^ Parent & its unit plurality gets free execution -); - -pub struct UsingOnlySelfCurrencyComponents< - WeightToFee: WeightToFeePolynomial, - AssetId: Get, - AccountId, - Currency: CurrencyT, - OnUnbalanced: OnUnbalancedT, ->( - Weight, - Currency::Balance, - PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>, -); -impl< - WeightToFee: WeightToFeePolynomial, - AssetId: Get, - AccountId, - Currency: CurrencyT, - OnUnbalanced: OnUnbalancedT, - > WeightTrader - for UsingOnlySelfCurrencyComponents -{ - fn new() -> Self { - Self(0, Zero::zero(), PhantomData) - } - - fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result { - let amount = WeightToFee::weight_to_fee(&weight); - let u128_amount: u128 = amount.try_into().map_err(|_| XcmError::Overflow)?; - - // location to this parachain through relay chain - let option1: xcm::v1::AssetId = Concrete(MultiLocation { - parents: 1, - interior: X1(Parachain(ParachainInfo::parachain_id().into())), - }); - // direct location - let option2: xcm::v1::AssetId = Concrete(MultiLocation { - parents: 0, - interior: Here, - }); - - let required = if payment.fungible.contains_key(&option1) { - (option1, u128_amount).into() - } else if payment.fungible.contains_key(&option2) { - (option2, u128_amount).into() - } else { - (Concrete(MultiLocation::default()), u128_amount).into() - }; - - let unused = payment - .checked_sub(required) - .map_err(|_| XcmError::TooExpensive)?; - self.0 = self.0.saturating_add(weight); - self.1 = self.1.saturating_add(amount); - Ok(unused) - } - - fn refund_weight(&mut self, weight: Weight) -> Option { - let weight = weight.min(self.0); - let amount = WeightToFee::weight_to_fee(&weight); - self.0 -= weight; - self.1 = self.1.saturating_sub(amount); - let amount: u128 = amount.saturated_into(); - if amount > 0 { - Some((AssetId::get(), amount).into()) - } else { - None - } - } -} -impl< - WeightToFee: WeightToFeePolynomial, - AssetId: Get, - AccountId, - Currency: CurrencyT, - OnUnbalanced: OnUnbalancedT, - > Drop - for UsingOnlySelfCurrencyComponents -{ - fn drop(&mut self) { - OnUnbalanced::on_unbalanced(Currency::issue(self.1)); - } -} - -pub struct XcmConfig(PhantomData); -impl Config for XcmConfig -where - T: pallet_configuration::Config, -{ - type Call = Call; - type XcmSender = XcmRouter; - // How to withdraw and deposit an asset. - type AssetTransactor = LocalAssetTransactor; - type OriginConverter = XcmOriginToTransactDispatchOrigin; - type IsReserve = NativeAsset; - type IsTeleporter = (); // Teleportation is disabled - type LocationInverter = LocationInverter; - type Barrier = Barrier; - type Weigher = FixedWeightBounds; - type Trader = UsingOnlySelfCurrencyComponents< - pallet_configuration::WeightToFee, - RelayLocation, - AccountId, - Balances, - (), - >; - type ResponseHandler = (); // Don't handle responses for now. - type SubscriptionService = PolkadotXcm; - - type AssetTrap = PolkadotXcm; - type AssetClaims = PolkadotXcm; -} - -impl pallet_xcm::Config for Runtime { - type Event = Event; - type SendXcmOrigin = EnsureXcmOrigin; - type XcmRouter = XcmRouter; - type ExecuteXcmOrigin = EnsureXcmOrigin; - type XcmExecuteFilter = Everything; - type XcmExecutor = XcmExecutor>; - type XcmTeleportFilter = Everything; - type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; - type Origin = Origin; - type Call = Call; - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; -} - -impl cumulus_pallet_xcm::Config for Runtime { - type Event = Event; - type XcmExecutor = XcmExecutor>; -} - -impl cumulus_pallet_xcmp_queue::Config for Runtime { - type WeightInfo = (); - type Event = Event; - type XcmExecutor = XcmExecutor>; - type ChannelInfo = ParachainSystem; - type VersionWrapper = (); - type ExecuteOverweightOrigin = frame_system::EnsureRoot; - type ControllerOrigin = EnsureRoot; - type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; -} - -impl cumulus_pallet_dmp_queue::Config for Runtime { - type Event = Event; - type XcmExecutor = XcmExecutor>; - type ExecuteOverweightOrigin = frame_system::EnsureRoot; -} --- /dev/null +++ b/runtime/common/config/xcm/foreignassets.rs @@ -0,0 +1,198 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +use frame_support::{ + traits::{Contains, Get, fungibles}, + parameter_types, +}; +use sp_runtime::traits::{Zero, Convert}; +use xcm::v1::{Junction::*, MultiLocation, Junctions::*}; +use xcm::latest::MultiAsset; +use xcm_builder::{FungiblesAdapter, ConvertedConcreteAssetId}; +use xcm_executor::traits::{Convert as ConvertXcm, JustTry, FilterAssetLocation}; +use pallet_foreign_assets::{ + AssetIds, AssetIdMapping, XcmForeignAssetIdMapping, NativeCurrency, FreeForAll, TryAsForeign, + ForeignAssetId, CurrencyId, +}; +use sp_std::{borrow::Borrow, marker::PhantomData}; +use crate::{Runtime, Balances, ParachainInfo, PolkadotXcm, ForeignAssets}; + +use super::{LocationToAccountId, RelayLocation}; + +use up_common::types::{AccountId, Balance}; + +parameter_types! { + pub CheckingAccount: AccountId = PolkadotXcm::check_account(); +} + +/// Allow checking in assets that have issuance > 0. +pub struct NonZeroIssuance(PhantomData<(AccountId, ForeignAssets)>); + +impl Contains<>::AssetId> + for NonZeroIssuance +where + ForeignAssets: fungibles::Inspect, +{ + fn contains(id: &>::AssetId) -> bool { + !ForeignAssets::total_issuance(*id).is_zero() + } +} + +pub struct AsInnerId(PhantomData<(AssetId, ConvertAssetId)>); +impl> + ConvertXcm for AsInnerId +where + AssetId: Borrow, + AssetId: TryAsForeign, + AssetIds: Borrow, +{ + fn convert_ref(id: impl Borrow) -> Result { + let id = id.borrow(); + + log::trace!( + target: "xcm::AsInnerId::Convert", + "AsInnerId {:?}", + id + ); + + let parent = MultiLocation::parent(); + let here = MultiLocation::here(); + let self_location = MultiLocation::new(1, X1(Parachain(ParachainInfo::get().into()))); + + if *id == parent { + return ConvertAssetId::convert_ref(AssetIds::NativeAssetId(NativeCurrency::Parent)); + } + + if *id == here || *id == self_location { + return ConvertAssetId::convert_ref(AssetIds::NativeAssetId(NativeCurrency::Here)); + } + + match XcmForeignAssetIdMapping::::get_currency_id(id.clone()) { + Some(AssetIds::ForeignAssetId(foreign_asset_id)) => { + ConvertAssetId::convert_ref(AssetIds::ForeignAssetId(foreign_asset_id)) + } + _ => ConvertAssetId::convert_ref(AssetIds::ForeignAssetId(0)), + } + } + + fn reverse_ref(what: impl Borrow) -> Result { + log::trace!( + target: "xcm::AsInnerId::Reverse", + "AsInnerId", + ); + + let asset_id = what.borrow(); + + let parent_id = + ConvertAssetId::convert_ref(AssetIds::NativeAssetId(NativeCurrency::Parent)).unwrap(); + let here_id = + ConvertAssetId::convert_ref(AssetIds::NativeAssetId(NativeCurrency::Here)).unwrap(); + + if asset_id.clone() == parent_id { + return Ok(MultiLocation::parent()); + } + + if asset_id.clone() == here_id { + return Ok(MultiLocation::new( + 1, + X1(Parachain(ParachainInfo::get().into())), + )); + } + + match >::try_as_foreign(asset_id.clone()) { + Some(fid) => match XcmForeignAssetIdMapping::::get_multi_location(fid) { + Some(location) => Ok(location), + None => Err(()), + }, + None => Err(()), + } + } +} + +/// Means for transacting assets besides the native currency on this chain. +pub type FungiblesTransactor = FungiblesAdapter< + // Use this fungibles implementation: + ForeignAssets, + // Use this currency when it is a fungible asset matching the given location or name: + ConvertedConcreteAssetId, JustTry>, + // Convert an XCM MultiLocation into a local account id: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We only want to allow teleports of known assets. We use non-zero issuance as an indication + // that this asset is known. + NonZeroIssuance, + // The account to use for tracking teleports. + CheckingAccount, +>; + +/// Means for transacting assets on this chain. +pub type AssetTransactors = FungiblesTransactor; + +pub struct AllAsset; +impl FilterAssetLocation for AllAsset { + fn filter_asset_location(_asset: &MultiAsset, _origin: &MultiLocation) -> bool { + true + } +} + +pub type IsReserve = AllAsset; + +pub type Trader = FreeForAll< + pallet_configuration::WeightToFee, + RelayLocation, + AccountId, + Balances, + (), +>; + +pub struct CurrencyIdConvert; +impl Convert> for CurrencyIdConvert { + fn convert(id: AssetIds) -> Option { + match id { + AssetIds::NativeAssetId(NativeCurrency::Here) => Some(MultiLocation::new( + 1, + X1(Parachain(ParachainInfo::get().into())), + )), + AssetIds::NativeAssetId(NativeCurrency::Parent) => Some(MultiLocation::parent()), + AssetIds::ForeignAssetId(foreign_asset_id) => { + XcmForeignAssetIdMapping::::get_multi_location(foreign_asset_id) + } + } + } +} + +impl Convert> for CurrencyIdConvert { + fn convert(location: MultiLocation) -> Option { + if location == MultiLocation::here() + || location == MultiLocation::new(1, X1(Parachain(ParachainInfo::get().into()))) + { + return Some(AssetIds::NativeAssetId(NativeCurrency::Here)); + } + + if location == MultiLocation::parent() { + return Some(AssetIds::NativeAssetId(NativeCurrency::Parent)); + } + + if let Some(currency_id) = + XcmForeignAssetIdMapping::::get_currency_id(location.clone()) + { + return Some(currency_id); + } + + None + } +} --- /dev/null +++ b/runtime/common/config/xcm/mod.rs @@ -0,0 +1,268 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +use frame_support::{ + traits::{Everything, Get}, + weights::Weight, + parameter_types, +}; +use frame_system::EnsureRoot; +use pallet_xcm::XcmPassthrough; +use polkadot_parachain::primitives::Sibling; +use xcm::v1::{Junction::*, MultiLocation, NetworkId}; +use xcm::latest::prelude::*; +use xcm_builder::{ + AccountId32Aliases, EnsureXcmOrigin, FixedWeightBounds, LocationInverter, ParentAsSuperuser, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, ParentIsPreset, +}; +use xcm_executor::{Config, XcmExecutor, traits::ShouldExecute}; +use sp_std::{marker::PhantomData, vec::Vec}; +use crate::{ + Runtime, Call, Event, Origin, ParachainInfo, ParachainSystem, PolkadotXcm, XcmpQueue, + xcm_barrier::Barrier, +}; + +use up_common::types::AccountId; + +#[cfg(feature = "foreign-assets")] +pub mod foreignassets; + +#[cfg(not(feature = "foreign-assets"))] +pub mod nativeassets; + +#[cfg(feature = "foreign-assets")] +pub use foreignassets as xcm_assets; + +#[cfg(not(feature = "foreign-assets"))] +pub use nativeassets as xcm_assets; + +use xcm_assets::{AssetTransactors, IsReserve, Trader}; + +parameter_types! { + pub const RelayLocation: MultiLocation = MultiLocation::parent(); + pub const RelayNetwork: NetworkId = NetworkId::Polkadot; + pub RelayOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); + pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); + pub SelfLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(ParachainInfo::get().into()))); + + // One XCM operation is 1_000_000 weight - almost certainly a conservative estimate. + pub UnitWeightCost: Weight = 1_000_000; + pub const MaxInstructions: u32 = 100; +} + +/// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // Straight up local `AccountId32` origins just alias directly to `AccountId`. + AccountId32Aliases, +); + +/// No local origins on this chain are allowed to dispatch XCM sends/executions. +pub type LocalOriginToLocation = (SignedToAccountId32,); + +/// The means for routing XCM messages which are not for local execution into the right message +/// queues. +pub type XcmRouter = ( + // Two routers - use UMP to communicate with the relay chain: + cumulus_primitives_utility::ParentAsUmp, + // ..and XCMP to communicate with the sibling chains. + XcmpQueue, +); + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when + // recognised. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognised. + SiblingParachainAsNative, + // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a + // transaction from the Root origin. + ParentAsSuperuser, + // Native signed account converter; this just converts an `AccountId32` origin into a normal + // `Origin::Signed` origin of the same 32-byte value. + SignedAccountId32AsNative, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + XcmPassthrough, +); + +pub trait TryPass { + fn try_pass(origin: &MultiLocation, message: &mut Xcm) -> Result<(), ()>; +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl TryPass for Tuple { + fn try_pass(origin: &MultiLocation, message: &mut Xcm) -> Result<(), ()> { + for_tuples!( #( + Tuple::try_pass(origin, message)?; + )* ); + + Ok(()) + } +} + +pub struct DenyTransact; +impl TryPass for DenyTransact { + fn try_pass(_origin: &MultiLocation, message: &mut Xcm) -> Result<(), ()> { + let transact_inst = message + .0 + .iter() + .find(|inst| matches![inst, Instruction::Transact { .. }]); + + if transact_inst.is_some() { + log::warn!( + target: "xcm::barrier", + "transact XCM rejected" + ); + + Err(()) + } else { + Ok(()) + } + } +} + +/// Deny executing the XCM if it matches any of the Deny filter regardless of anything else. +/// If it passes the Deny, and matches one of the Allow cases then it is let through. +pub struct DenyThenTry(PhantomData, PhantomData) +where + Deny: TryPass, + Allow: ShouldExecute; + +impl ShouldExecute for DenyThenTry +where + Deny: TryPass, + Allow: ShouldExecute, +{ + fn should_execute( + origin: &MultiLocation, + message: &mut Xcm, + max_weight: Weight, + weight_credit: &mut Weight, + ) -> Result<(), ()> { + Deny::try_pass(origin, message)?; + Allow::should_execute(origin, message, max_weight, weight_credit) + } +} + +// Allow xcm exchange only with locations in list +pub struct DenyExchangeWithUnknownLocation(PhantomData); +impl>> TryPass for DenyExchangeWithUnknownLocation { + fn try_pass(origin: &MultiLocation, message: &mut Xcm) -> Result<(), ()> { + let allowed_locations = T::get(); + + // Check if deposit or transfer belongs to allowed parachains + let mut allowed = allowed_locations.contains(origin); + + message.0.iter().for_each(|inst| match inst { + DepositReserveAsset { dest: dst, .. } => { + allowed |= allowed_locations.contains(dst); + } + TransferReserveAsset { dest: dst, .. } => { + allowed |= allowed_locations.contains(dst); + } + _ => {} + }); + + if allowed { + return Ok(()); + } + + log::warn!( + target: "xcm::barrier", + "Unexpected deposit or transfer location" + ); + // Deny + Err(()) + } +} + +pub type Weigher = FixedWeightBounds; + +pub struct XcmConfig(PhantomData); +impl Config for XcmConfig +where + T: pallet_configuration::Config, +{ + type Call = Call; + type XcmSender = XcmRouter; + // How to withdraw and deposit an asset. + type AssetTransactor = AssetTransactors; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + type IsReserve = IsReserve; + type IsTeleporter = (); // Teleportation is disabled + type LocationInverter = LocationInverter; + type Barrier = Barrier; + type Weigher = Weigher; + type Trader = Trader; + type ResponseHandler = (); // Don't handle responses for now. + type SubscriptionService = PolkadotXcm; + + type AssetTrap = PolkadotXcm; + type AssetClaims = PolkadotXcm; +} + +impl pallet_xcm::Config for Runtime { + type Event = Event; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + type XcmExecutor = XcmExecutor>; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type LocationInverter = LocationInverter; + type Origin = Origin; + type Call = Call; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor>; +} + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type WeightInfo = (); + type Event = Event; + type XcmExecutor = XcmExecutor>; + type ChannelInfo = ParachainSystem; + type VersionWrapper = (); + type ExecuteOverweightOrigin = frame_system::EnsureRoot; + type ControllerOrigin = EnsureRoot; + type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; +} + +impl cumulus_pallet_dmp_queue::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor>; + type ExecuteOverweightOrigin = frame_system::EnsureRoot; +} --- /dev/null +++ b/runtime/common/config/xcm/nativeassets.rs @@ -0,0 +1,143 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +use frame_support::{ + traits::{tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT, Get}, + weights::{Weight, WeightToFeePolynomial}, +}; +use sp_runtime::traits::{CheckedConversion, Zero, Convert}; +use xcm::v1::{Junction::*, MultiLocation, Junctions::*}; +use xcm::latest::{ + AssetId::{Concrete}, + Fungibility::Fungible as XcmFungible, + MultiAsset, Error as XcmError, +}; +use xcm_builder::{CurrencyAdapter, NativeAsset}; +use xcm_executor::{ + Assets, + traits::{MatchesFungible, WeightTrader}, +}; +use pallet_foreign_assets::{AssetIds, NativeCurrency}; +use sp_std::marker::PhantomData; +use crate::{Balances, ParachainInfo}; +use super::{LocationToAccountId, RelayLocation}; + +use up_common::types::{AccountId, Balance}; + +pub struct OnlySelfCurrency; +impl> MatchesFungible for OnlySelfCurrency { + fn matches_fungible(a: &MultiAsset) -> Option { + let paraid = Parachain(ParachainInfo::parachain_id().into()); + match (&a.id, &a.fun) { + ( + Concrete(MultiLocation { + parents: 1, + interior: X1(loc), + }), + XcmFungible(ref amount), + ) if paraid == *loc => CheckedConversion::checked_from(*amount), + ( + Concrete(MultiLocation { + parents: 0, + interior: Here, + }), + XcmFungible(ref amount), + ) => CheckedConversion::checked_from(*amount), + _ => None, + } + } +} + +/// Means for transacting assets on this chain. +pub type LocalAssetTransactor = CurrencyAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching the given location or name: + OnlySelfCurrency, + // Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We don't track any teleports. + (), +>; + +pub type AssetTransactors = LocalAssetTransactor; + +pub type IsReserve = NativeAsset; + +pub struct UsingOnlySelfCurrencyComponents< + WeightToFee: WeightToFeePolynomial, + AssetId: Get, + AccountId, + Currency: CurrencyT, + OnUnbalanced: OnUnbalancedT, +>( + Weight, + Currency::Balance, + PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>, +); +impl< + WeightToFee: WeightToFeePolynomial, + AssetId: Get, + AccountId, + Currency: CurrencyT, + OnUnbalanced: OnUnbalancedT, + > WeightTrader + for UsingOnlySelfCurrencyComponents +{ + fn new() -> Self { + Self(0, Zero::zero(), PhantomData) + } + + fn buy_weight(&mut self, _weight: Weight, payment: Assets) -> Result { + Ok(payment) + } +} +impl< + WeightToFee: WeightToFeePolynomial, + AssetId: Get, + AccountId, + Currency: CurrencyT, + OnUnbalanced: OnUnbalancedT, + > Drop + for UsingOnlySelfCurrencyComponents +{ + fn drop(&mut self) { + OnUnbalanced::on_unbalanced(Currency::issue(self.1)); + } +} + +pub type Trader = UsingOnlySelfCurrencyComponents< + pallet_configuration::WeightToFee, + RelayLocation, + AccountId, + Balances, + (), +>; + +pub struct CurrencyIdConvert; +impl Convert> for CurrencyIdConvert { + fn convert(id: AssetIds) -> Option { + match id { + AssetIds::NativeAssetId(NativeCurrency::Here) => Some(MultiLocation::new( + 1, + X1(Parachain(ParachainInfo::get().into())), + )), + _ => None, + } + } +} --- a/runtime/common/construct_runtime/mod.rs +++ b/runtime/common/construct_runtime/mod.rs @@ -42,7 +42,9 @@ Treasury: pallet_treasury::{Pallet, Call, Storage, Config, Event} = 34, Sudo: pallet_sudo::{Pallet, Call, Storage, Config, Event} = 35, Vesting: orml_vesting::{Pallet, Storage, Call, Event, Config} = 37, - // Vesting: pallet_vesting::{Pallet, Call, Config, Storage, Event} = 37, + + XTokens: orml_xtokens = 38, + Tokens: orml_tokens = 39, // Contracts: pallet_contracts::{Pallet, Call, Storage, Event} = 38, // XCM helpers. @@ -65,7 +67,7 @@ Common: pallet_common::{Pallet, Storage, Event} = 66, Fungible: pallet_fungible::{Pallet, Storage} = 67, - #[runtimes(opal)] + #[runtimes(opal, quartz)] Refungible: pallet_refungible::{Pallet, Storage} = 68, Nonfungible: pallet_nonfungible::{Pallet, Storage} = 69, @@ -80,6 +82,9 @@ #[runtimes(opal)] AppPromotion: pallet_app_promotion::{Pallet, Call, Storage, Event} = 73, + #[runtimes(opal)] + ForeignAssets: pallet_foreign_assets::{Pallet, Call, Storage, Event} = 80, + // Frontier EVM: pallet_evm::{Pallet, Config, Call, Storage, Event} = 100, Ethereum: pallet_ethereum::{Pallet, Config, Call, Storage, Event, Origin} = 101, --- a/runtime/common/mod.rs +++ b/runtime/common/mod.rs @@ -24,6 +24,9 @@ pub mod sponsoring; pub mod weights; +#[cfg(test)] +pub mod tests; + use sp_core::H160; use frame_support::traits::{Currency, OnUnbalanced, Imbalance}; use sp_runtime::{ @@ -79,7 +82,7 @@ pub type SignedExtra = ( frame_system::CheckSpecVersion, - // system::CheckTxVersion, + frame_system::CheckTxVersion, frame_system::CheckGenesis, frame_system::CheckEra, frame_system::CheckNonce, --- a/runtime/common/runtime_apis.rs +++ b/runtime/common/runtime_apis.rs @@ -688,6 +688,10 @@ #[cfg(not(any(feature = "unique-runtime", feature = "quartz-runtime")))] list_benchmark!(list, extra, pallet_proxy_rmrk_equip, RmrkEquip); + #[cfg(not(any(feature = "unique-runtime", feature = "quartz-runtime")))] + list_benchmark!(list, extra, pallet_foreign_assets, ForeignAssets); + + // list_benchmark!(list, extra, pallet_evm_coder_substrate, EvmCoderSubstrate); let storage_info = AllPalletsReversedWithSystemFirst::storage_info(); @@ -744,6 +748,9 @@ #[cfg(not(any(feature = "unique-runtime", feature = "quartz-runtime")))] add_benchmark!(params, batches, pallet_proxy_rmrk_equip, RmrkEquip); + #[cfg(not(any(feature = "unique-runtime", feature = "quartz-runtime")))] + add_benchmark!(params, batches, pallet_foreign_assets, ForeignAssets); + // add_benchmark!(params, batches, pallet_evm_coder_substrate, EvmCoderSubstrate); if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } --- /dev/null +++ b/runtime/common/tests/mod.rs @@ -0,0 +1,46 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +use sp_runtime::BuildStorage; +use sp_core::{Public, Pair}; +use sp_std::vec; +use up_common::types::AuraId; +use crate::{GenesisConfig, ParachainInfoConfig, AuraConfig}; + +pub mod xcm; + +fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{}", seed), None) + .expect("static values are valid; qed") + .public() +} + +fn new_test_ext(para_id: u32) -> sp_io::TestExternalities { + let cfg = GenesisConfig { + aura: AuraConfig { + authorities: vec![ + get_from_seed::("Alice"), + get_from_seed::("Bob"), + ], + }, + parachain_info: ParachainInfoConfig { + parachain_id: para_id.into(), + }, + ..GenesisConfig::default() + }; + + cfg.build_storage().unwrap().into() +} --- /dev/null +++ b/runtime/common/tests/xcm.rs @@ -0,0 +1,162 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +use xcm_executor::traits::ShouldExecute; +use xcm::latest::prelude::*; +use logtest::Logger; +use crate::Call; +use super::new_test_ext; + +fn catch_xcm_barrier_log(logger: &mut Logger, expected_msg: &str) -> Result<(), String> { + for record in logger { + if record.target() == "xcm::barrier" && record.args() == expected_msg { + return Ok(()); + } + } + + Err(format!( + "the expected XCM barrier log `{}` is not found", + expected_msg + )) +} + +/// WARNING: Uses log capturing +/// See https://docs.rs/logtest/latest/logtest/index.html#constraints +pub fn barrier_denies_transact(logger: &mut Logger) { + let location = MultiLocation { + parents: 0, + interior: Junctions::Here, + }; + + // We will never decode this "call", + // so it is irrelevant what we are passing to the `transact` cmd. + let fake_encoded_call = vec![0u8]; + + let transact_inst = Transact { + origin_type: OriginKind::Superuser, + require_weight_at_most: 0, + call: fake_encoded_call.into(), + }; + + let mut xcm_program = Xcm::(vec![transact_inst]); + + let max_weight = 100_000; + let mut weight_credit = 100_000_000; + + let result = B::should_execute(&location, &mut xcm_program, max_weight, &mut weight_credit); + + assert!( + result.is_err(), + "the barrier should disallow the XCM transact cmd" + ); + + catch_xcm_barrier_log(logger, "transact XCM rejected").unwrap(); +} + +fn xcm_execute( + self_para_id: u32, + location: &MultiLocation, + xcm: &mut Xcm, +) -> Result<(), ()> { + new_test_ext(self_para_id).execute_with(|| { + let max_weight = 100_000; + let mut weight_credit = 100_000_000; + + B::should_execute(&location, xcm, max_weight, &mut weight_credit) + }) +} + +fn make_multiassets(location: &MultiLocation) -> MultiAssets { + let id = AssetId::Concrete(location.clone()); + let fun = Fungibility::Fungible(42); + let multiasset = MultiAsset { id, fun }; + + multiasset.into() +} + +fn make_transfer_reserve_asset(location: &MultiLocation) -> Xcm { + let assets = make_multiassets(location); + let inst = TransferReserveAsset { + assets, + dest: location.clone(), + xcm: Xcm(vec![]), + }; + + Xcm::(vec![inst]) +} + +fn make_deposit_reserve_asset(location: &MultiLocation) -> Xcm { + let assets = make_multiassets(location); + let inst = DepositReserveAsset { + assets: assets.into(), + max_assets: 42, + dest: location.clone(), + xcm: Xcm(vec![]), + }; + + Xcm::(vec![inst]) +} + +fn expect_transfer_location_denied( + logger: &mut Logger, + self_para_id: u32, + location: &MultiLocation, + xcm: &mut Xcm, +) -> Result<(), String> { + let result = xcm_execute::(self_para_id, location, xcm); + + if result.is_ok() { + return Err("the barrier should deny the unknown location".into()); + } + + catch_xcm_barrier_log(logger, "Unexpected deposit or transfer location") +} + +/// WARNING: Uses log capturing +/// See https://docs.rs/logtest/latest/logtest/index.html#constraints +pub fn barrier_denies_transfer_from_unknown_location( + logger: &mut Logger, + self_para_id: u32, +) -> Result<(), String> +where + B: ShouldExecute, +{ + const UNKNOWN_PARACHAIN_ID: u32 = 4057; + + let unknown_location = MultiLocation { + parents: 1, + interior: X1(Parachain(UNKNOWN_PARACHAIN_ID)), + }; + + let mut transfer_reserve_asset = make_transfer_reserve_asset(&unknown_location); + let mut deposit_reserve_asset = make_deposit_reserve_asset(&unknown_location); + + expect_transfer_location_denied::( + logger, + self_para_id, + &unknown_location, + &mut transfer_reserve_asset, + )?; + + expect_transfer_location_denied::( + logger, + self_para_id, + &unknown_location, + &mut deposit_reserve_asset, + )?; + + Ok(()) +} --- a/runtime/opal/Cargo.toml +++ b/runtime/opal/Cargo.toml @@ -35,6 +35,7 @@ 'pallet-nonfungible/runtime-benchmarks', 'pallet-proxy-rmrk-core/runtime-benchmarks', 'pallet-proxy-rmrk-equip/runtime-benchmarks', + 'pallet-foreign-assets/runtime-benchmarks', 'pallet-unique/runtime-benchmarks', 'pallet-inflation/runtime-benchmarks', 'pallet-app-promotion/runtime-benchmarks', @@ -123,13 +124,18 @@ 'up-sponsorship/std', "orml-vesting/std", + "orml-tokens/std", + "orml-xtokens/std", + "orml-traits/std", + "pallet-foreign-assets/std" ] limit-testing = ['pallet-unique/limit-testing', 'up-data-structs/limit-testing'] -opal-runtime = ['refungible', 'scheduler', 'rmrk', 'app-promotion'] +opal-runtime = ['refungible', 'scheduler', 'rmrk', 'app-promotion', 'foreign-assets'] refungible = [] scheduler = [] rmrk = [] +foreign-assets = [] app-promotion = [] ################################################################################ @@ -403,6 +409,24 @@ version = "0.4.1-dev" default-features = false +[dependencies.orml-xtokens] +git = "https://github.com/open-web3-stack/open-runtime-module-library" +branch = "polkadot-v0.9.27" +version = "0.4.1-dev" +default-features = false + +[dependencies.orml-tokens] +git = "https://github.com/open-web3-stack/open-runtime-module-library" +branch = "polkadot-v0.9.27" +version = "0.4.1-dev" +default-features = false + +[dependencies.orml-traits] +git = "https://github.com/open-web3-stack/open-runtime-module-library" +branch = "polkadot-v0.9.27" +version = "0.4.1-dev" +default-features = false + ################################################################################ # local dependencies @@ -442,7 +466,19 @@ fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } fp-self-contained = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } evm-coder = { default-features = false, path = '../../crates/evm-coder' } -up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.27" } +up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = 'polkadot-v0.9.27' } +pallet-foreign-assets = { default-features = false, path = "../../pallets/foreign-assets" } + +################################################################################ +# Other Dependencies + +impl-trait-for-tuples = "0.2.2" + +################################################################################ +# Dev Dependencies + +[dev-dependencies.logtest] +version = "2.0.0" ################################################################################ # Build Dependencies --- a/runtime/opal/src/lib.rs +++ b/runtime/opal/src/lib.rs @@ -35,6 +35,11 @@ #[path = "../../common/mod.rs"] mod runtime_common; +pub mod xcm_barrier; + +#[cfg(test)] +mod tests; + pub use runtime_common::*; pub const RUNTIME_NAME: &str = "opal"; @@ -45,7 +50,7 @@ spec_name: create_runtime_str!(RUNTIME_NAME), impl_name: create_runtime_str!(RUNTIME_NAME), authoring_version: 1, - spec_version: 927020, + spec_version: 927030, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, --- /dev/null +++ b/runtime/opal/src/tests/logcapture.rs @@ -0,0 +1,25 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +use logtest::Logger; +use super::xcm::opal_xcm_tests; + +#[test] +fn opal_log_capture_tests() { + let mut logger = Logger::start(); + + opal_xcm_tests(&mut logger); +} --- /dev/null +++ b/runtime/opal/src/tests/mod.rs @@ -0,0 +1,18 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +mod logcapture; +mod xcm; --- /dev/null +++ b/runtime/opal/src/tests/xcm.rs @@ -0,0 +1,27 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +use logtest::Logger; +use crate::{runtime_common::tests::xcm::*, xcm_barrier::Barrier}; + +const OPAL_PARA_ID: u32 = 2095; // Same as Quartz + +pub fn opal_xcm_tests(logger: &mut Logger) { + barrier_denies_transact::(logger); + + barrier_denies_transfer_from_unknown_location::(logger, OPAL_PARA_ID) + .expect_err("opal runtime allows any location"); +} --- /dev/null +++ b/runtime/opal/src/xcm_barrier.rs @@ -0,0 +1,48 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +use frame_support::{weights::Weight, traits::Everything}; +use xcm::{latest::Xcm, v1::MultiLocation}; +use xcm_builder::{AllowTopLevelPaidExecutionFrom, TakeWeightCredit}; +use xcm_executor::traits::ShouldExecute; + +use crate::runtime_common::config::xcm::{DenyThenTry, DenyTransact}; + +/// Execution barrier that just takes `max_weight` from `weight_credit`. +/// +/// Useful to allow XCM execution by local chain users via extrinsics. +/// E.g. `pallet_xcm::reserve_asset_transfer` to transfer a reserve asset +/// out of the local chain to another one. +pub struct AllowAllDebug; +impl ShouldExecute for AllowAllDebug { + fn should_execute( + _origin: &MultiLocation, + _message: &mut Xcm, + _max_weight: Weight, + _weight_credit: &mut Weight, + ) -> Result<(), ()> { + Ok(()) + } +} + +pub type Barrier = DenyThenTry< + DenyTransact, + ( + TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, + AllowAllDebug, + ), +>; --- a/runtime/quartz/Cargo.toml +++ b/runtime/quartz/Cargo.toml @@ -36,6 +36,7 @@ 'pallet-proxy-rmrk-core/runtime-benchmarks', 'pallet-proxy-rmrk-equip/runtime-benchmarks', 'pallet-unique/runtime-benchmarks', + 'pallet-foreign-assets/runtime-benchmarks', 'pallet-inflation/runtime-benchmarks', 'pallet-unique-scheduler/runtime-benchmarks', 'pallet-xcm/runtime-benchmarks', @@ -116,15 +117,23 @@ 'xcm-builder/std', 'xcm-executor/std', 'up-common/std', + 'rmrk-rpc/std', + 'evm-coder/std', + 'up-sponsorship/std', "orml-vesting/std", + "orml-tokens/std", + "orml-xtokens/std", + "orml-traits/std", + "pallet-foreign-assets/std" ] limit-testing = ['pallet-unique/limit-testing', 'up-data-structs/limit-testing'] -quartz-runtime = [] +quartz-runtime = ['refungible'] refungible = [] scheduler = [] rmrk = [] +foreign-assets = [] ################################################################################ # Substrate Dependencies @@ -397,6 +406,25 @@ version = "0.4.1-dev" default-features = false +[dependencies.orml-xtokens] +git = "https://github.com/open-web3-stack/open-runtime-module-library" +branch = "polkadot-v0.9.27" +version = "0.4.1-dev" +default-features = false + +[dependencies.orml-tokens] +git = "https://github.com/open-web3-stack/open-runtime-module-library" +branch = "polkadot-v0.9.27" +version = "0.4.1-dev" +default-features = false + +[dependencies.orml-traits] +git = "https://github.com/open-web3-stack/open-runtime-module-library" +branch = "polkadot-v0.9.27" +version = "0.4.1-dev" +default-features = false + + ################################################################################ # RMRK dependencies @@ -443,7 +471,19 @@ fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } fp-self-contained = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } evm-coder = { default-features = false, path = '../../crates/evm-coder' } -up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.27" } +up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = 'polkadot-v0.9.27' } +pallet-foreign-assets = { default-features = false, path = "../../pallets/foreign-assets" } + +################################################################################ +# Other Dependencies + +impl-trait-for-tuples = "0.2.2" + +################################################################################ +# Dev Dependencies + +[dev-dependencies.logtest] +version = "2.0.0" ################################################################################ # Build Dependencies --- a/runtime/quartz/src/lib.rs +++ b/runtime/quartz/src/lib.rs @@ -35,6 +35,11 @@ #[path = "../../common/mod.rs"] mod runtime_common; +pub mod xcm_barrier; + +#[cfg(test)] +mod tests; + pub use runtime_common::*; pub const RUNTIME_NAME: &str = "quartz"; @@ -45,7 +50,7 @@ spec_name: create_runtime_str!(RUNTIME_NAME), impl_name: create_runtime_str!(RUNTIME_NAME), authoring_version: 1, - spec_version: 927020, + spec_version: 927030, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, --- /dev/null +++ b/runtime/quartz/src/tests/logcapture.rs @@ -0,0 +1,25 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +use logtest::Logger; +use super::xcm::quartz_xcm_tests; + +#[test] +fn quartz_log_capture_tests() { + let mut logger = Logger::start(); + + quartz_xcm_tests(&mut logger); +} --- /dev/null +++ b/runtime/quartz/src/tests/mod.rs @@ -0,0 +1,18 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +mod logcapture; +mod xcm; --- /dev/null +++ b/runtime/quartz/src/tests/xcm.rs @@ -0,0 +1,27 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +use logtest::Logger; +use crate::{runtime_common::tests::xcm::*, xcm_barrier::Barrier}; + +const QUARTZ_PARA_ID: u32 = 2095; + +pub fn quartz_xcm_tests(logger: &mut Logger) { + barrier_denies_transact::(logger); + + barrier_denies_transfer_from_unknown_location::(logger, QUARTZ_PARA_ID) + .expect("quartz runtime denies an unknown location"); +} --- /dev/null +++ b/runtime/quartz/src/xcm_barrier.rs @@ -0,0 +1,83 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +use frame_support::{ + match_types, parameter_types, + traits::{Get, Everything}, +}; +use sp_std::{vec, vec::Vec}; +use xcm::v1::{Junction::*, Junctions::*, MultiLocation}; +use xcm_builder::{ + AllowKnownQueryResponses, AllowSubscriptionsFrom, TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, +}; + +use crate::{ + ParachainInfo, PolkadotXcm, + runtime_common::config::xcm::{DenyThenTry, DenyTransact, DenyExchangeWithUnknownLocation}, +}; + +match_types! { + pub type ParentOrSiblings: impl Contains = { + MultiLocation { parents: 1, interior: Here } | + MultiLocation { parents: 1, interior: X1(_) } + }; +} + +parameter_types! { + pub QuartzAllowedLocations: Vec = vec![ + // Self location + MultiLocation { + parents: 0, + interior: Here, + }, + // Parent location + MultiLocation { + parents: 1, + interior: Here, + }, + // Karura/Acala location + MultiLocation { + parents: 1, + interior: X1(Parachain(2000)), + }, + // Moonriver location + MultiLocation { + parents: 1, + interior: X1(Parachain(2023)), + }, + // Self parachain address + MultiLocation { + parents: 1, + interior: X1(Parachain(ParachainInfo::get().into())), + }, + ]; +} + +pub type Barrier = DenyThenTry< + ( + DenyTransact, + DenyExchangeWithUnknownLocation, + ), + ( + TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, + // Expected responses are OK. + AllowKnownQueryResponses, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), +>; --- a/runtime/unique/Cargo.toml +++ b/runtime/unique/Cargo.toml @@ -36,6 +36,7 @@ 'pallet-proxy-rmrk-core/runtime-benchmarks', 'pallet-proxy-rmrk-equip/runtime-benchmarks', 'pallet-unique/runtime-benchmarks', + 'pallet-foreign-assets/runtime-benchmarks', 'pallet-inflation/runtime-benchmarks', 'pallet-unique-scheduler/runtime-benchmarks', 'pallet-xcm/runtime-benchmarks', @@ -117,8 +118,15 @@ 'xcm-builder/std', 'xcm-executor/std', 'up-common/std', + 'rmrk-rpc/std', + 'evm-coder/std', + 'up-sponsorship/std', "orml-vesting/std", + "orml-tokens/std", + "orml-xtokens/std", + "orml-traits/std", + "pallet-foreign-assets/std" ] limit-testing = ['pallet-unique/limit-testing', 'up-data-structs/limit-testing'] unique-runtime = [] @@ -126,6 +134,7 @@ refungible = [] scheduler = [] rmrk = [] +foreign-assets = [] ################################################################################ # Substrate Dependencies @@ -398,6 +407,23 @@ version = "0.4.1-dev" default-features = false +[dependencies.orml-xtokens] +git = "https://github.com/open-web3-stack/open-runtime-module-library" +branch = "polkadot-v0.9.27" +version = "0.4.1-dev" +default-features = false + +[dependencies.orml-tokens] +git = "https://github.com/open-web3-stack/open-runtime-module-library" +branch = "polkadot-v0.9.27" +version = "0.4.1-dev" +default-features = false + +[dependencies.orml-traits] +git = "https://github.com/open-web3-stack/open-runtime-module-library" +branch = "polkadot-v0.9.27" +version = "0.4.1-dev" +default-features = false ################################################################################ # local dependencies @@ -437,7 +463,19 @@ fp-self-contained = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } evm-coder = { default-features = false, path = '../../crates/evm-coder' } -up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.27" } +up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = 'polkadot-v0.9.27' } +pallet-foreign-assets = { default-features = false, path = "../../pallets/foreign-assets" } + +################################################################################ +# Other Dependencies + +impl-trait-for-tuples = "0.2.2" + +################################################################################ +# Dev Dependencies + +[dev-dependencies.logtest] +version = "2.0.0" ################################################################################ # Build Dependencies --- a/runtime/unique/src/lib.rs +++ b/runtime/unique/src/lib.rs @@ -35,6 +35,11 @@ #[path = "../../common/mod.rs"] mod runtime_common; +pub mod xcm_barrier; + +#[cfg(test)] +mod tests; + pub use runtime_common::*; pub const RUNTIME_NAME: &str = "unique"; @@ -45,7 +50,7 @@ spec_name: create_runtime_str!(RUNTIME_NAME), impl_name: create_runtime_str!(RUNTIME_NAME), authoring_version: 1, - spec_version: 927020, + spec_version: 927030, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, --- /dev/null +++ b/runtime/unique/src/tests/logcapture.rs @@ -0,0 +1,25 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +use logtest::Logger; +use super::xcm::unique_xcm_tests; + +#[test] +fn unique_log_capture_tests() { + let mut logger = Logger::start(); + + unique_xcm_tests(&mut logger); +} --- /dev/null +++ b/runtime/unique/src/tests/mod.rs @@ -0,0 +1,18 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +mod logcapture; +mod xcm; --- /dev/null +++ b/runtime/unique/src/tests/xcm.rs @@ -0,0 +1,27 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +use logtest::Logger; +use crate::{runtime_common::tests::xcm::*, xcm_barrier::Barrier}; + +const UNIQUE_PARA_ID: u32 = 2037; + +pub fn unique_xcm_tests(logger: &mut Logger) { + barrier_denies_transact::(logger); + + barrier_denies_transfer_from_unknown_location::(logger, UNIQUE_PARA_ID) + .expect("unique runtime denies an unknown location"); +} --- /dev/null +++ b/runtime/unique/src/xcm_barrier.rs @@ -0,0 +1,83 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +use frame_support::{ + match_types, parameter_types, + traits::{Get, Everything}, +}; +use sp_std::{vec, vec::Vec}; +use xcm::v1::{Junction::*, Junctions::*, MultiLocation}; +use xcm_builder::{ + AllowKnownQueryResponses, AllowSubscriptionsFrom, TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, +}; + +use crate::{ + ParachainInfo, PolkadotXcm, + runtime_common::config::xcm::{DenyThenTry, DenyTransact, DenyExchangeWithUnknownLocation}, +}; + +match_types! { + pub type ParentOrSiblings: impl Contains = { + MultiLocation { parents: 1, interior: Here } | + MultiLocation { parents: 1, interior: X1(_) } + }; +} + +parameter_types! { + pub UniqueAllowedLocations: Vec = vec![ + // Self location + MultiLocation { + parents: 0, + interior: Here, + }, + // Parent location + MultiLocation { + parents: 1, + interior: Here, + }, + // Karura/Acala location + MultiLocation { + parents: 1, + interior: X1(Parachain(2000)), + }, + // Moonbeam location + MultiLocation { + parents: 1, + interior: X1(Parachain(2004)), + }, + // Self parachain address + MultiLocation { + parents: 1, + interior: X1(Parachain(ParachainInfo::get().into())), + }, + ]; +} + +pub type Barrier = DenyThenTry< + ( + DenyTransact, + DenyExchangeWithUnknownLocation, + ), + ( + TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, + // Expected responses are OK. + AllowKnownQueryResponses, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), +>; --- a/tests/package.json +++ b/tests/package.json @@ -49,7 +49,6 @@ "testConfirmSponsorship": "mocha --timeout 9999999 -r ts-node/register ./**/confirmSponsorship.test.ts", "testRemoveCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/removeCollectionAdmin.test.ts", "testRemoveCollectionSponsor": "mocha --timeout 9999999 -r ts-node/register ./**/removeCollectionSponsor.test.ts", - "testRemoveFromAllowList": "mocha --timeout 9999999 -r ts-node/register ./**/removeFromAllowList.test.ts", "testAllowLists": "mocha --timeout 9999999 -r ts-node/register ./**/allowLists.test.ts", "testConnection": "mocha --timeout 9999999 -r ts-node/register ./**/connection.test.ts", "testContracts": "mocha --timeout 9999999 -r ts-node/register ./**/contracts.test.ts", @@ -65,8 +64,7 @@ "testTransfer": "mocha --timeout 9999999 -r ts-node/register ./**/transfer.test.ts", "testBurnItem": "mocha --timeout 9999999 -r ts-node/register ./**/burnItem.test.ts", "testAdminTransferAndBurn": "mocha --timeout 9999999 -r ts-node/register ./**/adminTransferAndBurn.test.ts", - "testSetMintPermission": "mocha --timeout 9999999 -r ts-node/register ./**/setMintPermission.test.ts", - "testSetPublicAccessMode": "mocha --timeout 9999999 -r ts-node/register ./**/setPublicAccessMode.test.ts", + "testSetPermissions": "mocha --timeout 9999999 -r ts-node/register ./**/setPermissions.test.ts", "testCreditFeesToTreasury": "mocha --timeout 9999999 -r ts-node/register ./**/creditFeesToTreasury.test.ts", "testContractSponsoring": "mocha --timeout 9999999 -r ts-node/register ./**/contractSponsoring.test.ts", "testEnableContractSponsoring": "mocha --timeout 9999999 -r ts-node/register ./**/enableContractSponsoring.test.ts", @@ -78,7 +76,12 @@ "testInflation": "mocha --timeout 9999999 -r ts-node/register ./**/inflation.test.ts", "testScheduler": "mocha --timeout 9999999 -r ts-node/register ./**/scheduler.test.ts", "testSchedulingEVM": "mocha --timeout 9999999 -r ts-node/register ./**/eth/scheduling.test.ts", - "testXcmTransfer": "mocha --timeout 9999999 -r ts-node/register ./**/xcmTransfer.test.ts", + "testXcmUnique": "RUN_XCM_TESTS=1 mocha --timeout 9999999 -r ts-node/register ./**/xcm/xcmUnique.test.ts", + "testXcmQuartz": "RUN_XCM_TESTS=1 mocha --timeout 9999999 -r ts-node/register ./**/xcm/xcmQuartz.test.ts", + "testXcmOpal": "RUN_XCM_TESTS=1 mocha --timeout 9999999 -r ts-node/register ./**/xcm/xcmOpal.test.ts", + "testXcmTransferAcala": "mocha --timeout 9999999 -r ts-node/register ./**/xcm/xcmTransferAcala.test.ts acalaId=2000 uniqueId=5000", + "testXcmTransferStatemine": "mocha --timeout 9999999 -r ts-node/register ./**/xcm/xcmTransferStatemine.test.ts statemineId=1000 uniqueId=5000", + "testXcmTransferMoonbeam": "mocha --timeout 9999999 -r ts-node/register ./**/xcm/xcmTransferMoonbeam.test.ts", "testPalletPresence": "mocha --timeout 9999999 -r ts-node/register ./**/pallet-presence.test.ts", "testBlockProduction": "mocha --timeout 9999999 -r ts-node/register ./**/block-production.test.ts", "testEnableDisableTransfers": "mocha --timeout 9999999 -r ts-node/register ./**/enableDisableTransfer.test.ts", --- a/tests/scripts/readyness.js +++ b/tests/scripts/readyness.js @@ -1,7 +1,7 @@ const { ApiPromise, WsProvider } = require('@polkadot/api'); const connect = async () => { - const wsEndpoint = 'ws://127.0.0.1:9944' + const wsEndpoint = 'ws://127.0.0.1:9944'; const api = new ApiPromise({provider: new WsProvider(wsEndpoint)}); await api.isReadyOrError; @@ -12,10 +12,10 @@ } const sleep = time => { - return new Promise(resolve => { - setTimeout(() => resolve(), time); - }); -} + return new Promise(resolve => { + setTimeout(() => resolve(), time); + }); +}; const main = async () => { while(true) { --- a/tests/src/addToAllowList.test.ts +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {IKeyringPair} from '@polkadot/types/types'; -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import usingApi, {submitTransactionExpectFailAsync} from './substrate/substrate-api'; -import { - addToAllowListExpectSuccess, - createCollectionExpectSuccess, - createItemExpectSuccess, - destroyCollectionExpectSuccess, - enablePublicMintingExpectSuccess, - enableAllowListExpectSuccess, - normalizeAccountId, - addCollectionAdminExpectSuccess, - addToAllowListExpectFail, - getCreatedCollectionCount, -} from './util/helpers'; - -chai.use(chaiAsPromised); -const expect = chai.expect; - -let alice: IKeyringPair; -let bob: IKeyringPair; -let charlie: IKeyringPair; - -describe('Integration Test ext. addToAllowList()', () => { - - before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); - }); - }); - - it('Execute the extrinsic with parameters: Collection ID and address to add to the allow list', async () => { - const collectionId = await createCollectionExpectSuccess(); - await addToAllowListExpectSuccess(alice, collectionId, bob.address); - }); - - it('Allowlisted minting: list restrictions', async () => { - const collectionId = await createCollectionExpectSuccess(); - await addToAllowListExpectSuccess(alice, collectionId, bob.address); - await enableAllowListExpectSuccess(alice, collectionId); - await enablePublicMintingExpectSuccess(alice, collectionId); - await createItemExpectSuccess(bob, collectionId, 'NFT', bob.address); - }); -}); - -describe('Negative Integration Test ext. addToAllowList()', () => { - - it('Allow list an address in the collection that does not exist', async () => { - await usingApi(async (api, privateKeyWrapper) => { - // tslint:disable-next-line: no-bitwise - const collectionId = await getCreatedCollectionCount(api) + 1; - const bob = privateKeyWrapper('//Bob'); - - const tx = api.tx.unique.addToAllowList(collectionId, normalizeAccountId(bob.address)); - await expect(submitTransactionExpectFailAsync(alice, tx)).to.be.rejected; - }); - }); - - it('Allow list an address in the collection that was destroyed', async () => { - await usingApi(async (api, privateKeyWrapper) => { - const alice = privateKeyWrapper('//Alice'); - const bob = privateKeyWrapper('//Bob'); - // tslint:disable-next-line: no-bitwise - const collectionId = await createCollectionExpectSuccess(); - await destroyCollectionExpectSuccess(collectionId); - const tx = api.tx.unique.addToAllowList(collectionId, normalizeAccountId(bob.address)); - await expect(submitTransactionExpectFailAsync(alice, tx)).to.be.rejected; - }); - }); - - it('Allow list an address in the collection that does not have allow list access enabled', async () => { - await usingApi(async (api, privateKeyWrapper) => { - const alice = privateKeyWrapper('//Alice'); - const ferdie = privateKeyWrapper('//Ferdie'); - const collectionId = await createCollectionExpectSuccess(); - await enableAllowListExpectSuccess(alice, collectionId); - await enablePublicMintingExpectSuccess(alice, collectionId); - const tx = api.tx.unique.createItem(collectionId, normalizeAccountId(ferdie.address), 'NFT'); - await expect(submitTransactionExpectFailAsync(ferdie, tx)).to.be.rejected; - }); - }); - -}); - -describe('Integration Test ext. addToAllowList() with collection admin permissions:', () => { - - before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); - charlie = privateKeyWrapper('//Charlie'); - }); - }); - - it('Negative. Add to the allow list by regular user', async () => { - const collectionId = await createCollectionExpectSuccess(); - await addToAllowListExpectFail(bob, collectionId, charlie.address); - }); - - it('Execute the extrinsic with parameters: Collection ID and address to add to the allow list', async () => { - const collectionId = await createCollectionExpectSuccess(); - await addCollectionAdminExpectSuccess(alice, collectionId, bob.address); - await addToAllowListExpectSuccess(bob, collectionId, charlie.address); - }); - - it('Allowlisted minting: list restrictions', async () => { - const collectionId = await createCollectionExpectSuccess(); - await addCollectionAdminExpectSuccess(alice, collectionId, bob.address); - await addToAllowListExpectSuccess(bob, collectionId, charlie.address); - - // allowed only for collection owner - await enableAllowListExpectSuccess(alice, collectionId); - await enablePublicMintingExpectSuccess(alice, collectionId); - - await createItemExpectSuccess(charlie, collectionId, 'NFT', charlie.address); - }); -}); --- a/tests/src/addToContractAllowList.test.ts +++ b/tests/src/addToContractAllowList.test.ts @@ -27,6 +27,7 @@ chai.use(chaiAsPromised); const expect = chai.expect; +// todo:playgrounds skipped ~ postponed describe.skip('Integration Test addToContractAllowList', () => { it('Add an address to a contract allow list', async () => { --- a/tests/src/allowLists.test.ts +++ b/tests/src/allowLists.test.ts @@ -16,6 +16,7 @@ import {IKeyringPair} from '@polkadot/types/types'; import {usingPlaygrounds, expect, itSub} from './util/playgrounds'; +import {ICollectionPermissions} from './util/playgrounds/types'; describe('Integration Test ext. Allow list tests', () => { let alice: IKeyringPair; @@ -25,287 +26,335 @@ before(async () => { await usingPlaygrounds(async (helper, privateKey) => { const donor = privateKey('//Alice'); - [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); + [alice, bob, charlie] = await helper.arrange.createAccounts([30n, 10n, 10n], donor); }); }); - itSub('Owner can add address to allow list', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); - const allowList = await helper.nft.getAllowList(collectionId); - expect(allowList).to.be.deep.contains({Substrate: bob.address}); - }); + describe('Positive', async () => { + itSub('Owner can add address to allow list', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + // allow list does not need to be enabled to add someone in advance + await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); + const allowList = await helper.nft.getAllowList(collectionId); + expect(allowList).to.deep.contain({Substrate: bob.address}); + }); - itSub('Admin can add address to allow list', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.addAdmin(alice, collectionId, {Substrate: bob.address}); + itSub('Admin can add address to allow list', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.nft.addAdmin(alice, collectionId, {Substrate: bob.address}); - await helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address}); - const allowList = await helper.nft.getAllowList(collectionId); - expect(allowList).to.be.deep.contains({Substrate: charlie.address}); - }); + // allow list does not need to be enabled to add someone in advance + await helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address}); + const allowList = await helper.nft.getAllowList(collectionId); + expect(allowList).to.deep.contain({Substrate: charlie.address}); + }); - itSub('Non-privileged user cannot add address to allow list', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const addToAllowListTx = async () => helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address}); - await expect(addToAllowListTx()).to.be.rejectedWith(/common\.NoPermission/); + itSub('If address is already added to allow list, nothing happens', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); + const allowList = await helper.nft.getAllowList(collectionId); + expect(allowList).to.deep.contain({Substrate: bob.address}); + }); }); - itSub('Nobody can add address to allow list of non-existing collection', async ({helper}) => { - const collectionId = (1<<32) - 1; - const addToAllowListTx = async () => helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address}); - await expect(addToAllowListTx()).to.be.rejectedWith(/common\.CollectionNotFound/); - }); + describe('Negative', async () => { + itSub('Nobody can add address to allow list of non-existing collection', async ({helper}) => { + const collectionId = (1<<32) - 1; + await expect(helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); - itSub('Nobody can add address to allow list of destroyed collection', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.collection.burn(alice, collectionId); - const addToAllowListTx = async () => helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); - await expect(addToAllowListTx()).to.be.rejectedWith(/common\.CollectionNotFound/); + itSub('Nobody can add address to allow list of destroyed collection', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.collection.burn(alice, collectionId); + await expect(helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('Non-privileged user cannot add address to allow list', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await expect(helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.NoPermission/); + }); }); +}); - itSub('If address is already added to allow list, nothing happens', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); - const allowList = await helper.nft.getAllowList(collectionId); - expect(allowList).to.be.deep.contains({Substrate: bob.address}); +describe('Integration Test ext. Remove from Allow List', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob, charlie] = await helper.arrange.createAccounts([30n, 10n, 10n], donor); + }); }); - itSub('Owner can remove address from allow list', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); + describe('Positive', async () => { + itSub('Owner can remove address from allow list', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); - await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address}); + await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address}); - const allowList = await helper.nft.getAllowList(collectionId); + const allowList = await helper.nft.getAllowList(collectionId); - expect(allowList).to.be.not.deep.contains({Substrate: bob.address}); - }); + expect(allowList).to.not.deep.contain({Substrate: bob.address}); + }); - itSub('Admin can remove address from allow list', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.addAdmin(alice, collectionId, {Substrate: charlie.address}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); - await helper.collection.removeFromAllowList(charlie, collectionId, {Substrate: bob.address}); + itSub('Admin can remove address from allow list', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.nft.addAdmin(alice, collectionId, {Substrate: charlie.address}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); + await helper.collection.removeFromAllowList(charlie, collectionId, {Substrate: bob.address}); - const allowList = await helper.nft.getAllowList(collectionId); + const allowList = await helper.nft.getAllowList(collectionId); + expect(allowList).to.not.deep.contain({Substrate: bob.address}); + }); - expect(allowList).to.be.not.deep.contains({Substrate: bob.address}); - }); + itSub('If address is already removed from allow list, nothing happens', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); + await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address}); + const allowListBefore = await helper.nft.getAllowList(collectionId); + expect(allowListBefore).to.not.deep.contain({Substrate: bob.address}); - itSub('Non-privileged user cannot remove address from allow list', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); - const removeTx = async () => helper.collection.removeFromAllowList(bob, collectionId, {Substrate: charlie.address}); - await expect(removeTx()).to.be.rejectedWith(/common\.NoPermission/); - const allowList = await helper.nft.getAllowList(collectionId); + await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address}); - expect(allowList).to.be.deep.contains({Substrate: charlie.address}); + const allowListAfter = await helper.nft.getAllowList(collectionId); + expect(allowListAfter).to.not.deep.contain({Substrate: bob.address}); + }); }); - itSub('Nobody can remove address from allow list of non-existing collection', async ({helper}) => { - const collectionId = (1<<32) - 1; - const removeTx = async () => helper.collection.removeFromAllowList(bob, collectionId, {Substrate: charlie.address}); - await expect(removeTx()).to.be.rejectedWith(/common\.CollectionNotFound/); + describe('Negative', async () => { + itSub('Non-privileged user cannot remove address from allow list', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); + await expect(helper.collection.removeFromAllowList(charlie, collectionId, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.NoPermission/); + + const allowList = await helper.nft.getAllowList(collectionId); + expect(allowList).to.deep.contain({Substrate: charlie.address}); + }); + + itSub('Nobody can remove address from allow list of non-existing collection', async ({helper}) => { + const collectionId = (1<<32) - 1; + await expect(helper.collection.removeFromAllowList(bob, collectionId, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('Nobody can remove address from allow list of deleted collection', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); + await helper.collection.burn(alice, collectionId); + + await expect(helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); }); +}); - itSub('Nobody can remove address from allow list of deleted collection', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); - await helper.collection.burn(alice, collectionId); - const removeTx = async () => helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address}); +describe('Integration Test ext. Transfer if included in Allow List', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; - await expect(removeTx()).to.be.rejectedWith(/common\.CollectionNotFound/); + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob, charlie] = await helper.arrange.createAccounts([30n, 10n, 10n], donor); + }); }); - itSub('If address is already removed from allow list, nothing happens', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); - await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address}); - const allowListBefore = await helper.nft.getAllowList(collectionId); - expect(allowListBefore).to.be.not.deep.contains({Substrate: bob.address}); + describe('Positive', async () => { + itSub('If Public Access mode is set to AllowList, tokens can be transferred to a allowlisted address with transfer.', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); + await helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address}); + const owner = await helper.nft.getTokenOwner(collectionId, tokenId); + expect(owner.Substrate).to.be.equal(charlie.address); + }); - await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address}); + itSub('If Public Access mode is set to AllowList, tokens can be transferred to a allowlisted address with transferFrom.', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); + await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address}); - const allowListAfter = await helper.nft.getAllowList(collectionId); - expect(allowListAfter).to.be.not.deep.contains({Substrate: bob.address}); - }); + await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: alice.address}, {Substrate: charlie.address}); + const owner = await helper.nft.getTokenOwner(collectionId, tokenId); + expect(owner.Substrate).to.be.equal(charlie.address); + }); - itSub('If Public Access mode is set to AllowList, tokens can’t be transferred from a non-allowlisted address with transfer or transferFrom. Test1', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); + itSub('If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transfer', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); - const transferResult = async () => helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address}); - await expect(transferResult()).to.be.rejectedWith(/common\.AddressNotInAllowlist/); - }); + await helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address}); + const owner = await helper.nft.getTokenOwner(collectionId, tokenId); + expect(owner.Substrate).to.be.equal(charlie.address); + }); - itSub('If Public Access mode is set to AllowList, tokens can’t be transferred from a non-allowlisted address with transfer or transferFrom. Test2', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); - await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address}); - await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: alice.address}); + itSub('If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transferFrom', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); + await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address}); - const transferResult = async () => helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address}); - await expect(transferResult()).to.be.rejectedWith(/common\.AddressNotInAllowlist/); + await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: alice.address}, {Substrate: charlie.address}); + const owner = await helper.nft.getTokenOwner(collectionId, tokenId); + expect(owner.Substrate).to.be.equal(charlie.address); + }); }); - itSub('If Public Access mode is set to AllowList, tokens can’t be transferred to a non-allowlisted address with transfer or transferFrom. Test1', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); + describe('Negative', async () => { + itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred from a non-allowlisted address with transfer or transferFrom. Test1', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); + + await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.AddressNotInAllowlist/); + }); + + itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred from a non-allowlisted address with transfer or transferFrom. Test2', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); + await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address}); + await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: alice.address}); + + await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.AddressNotInAllowlist/); + }); - const transferResult = async () => helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address}); - await expect(transferResult()).to.be.rejectedWith(/common\.AddressNotInAllowlist/); - }); + itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred to a non-allowlisted address with transfer or transferFrom. Test1', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); - itSub('If Public Access mode is set to AllowList, tokens can’t be transferred to a non-allowlisted address with transfer or transferFrom. Test2', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); + await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.AddressNotInAllowlist/); + }); - await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address}); - await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: alice.address}); + itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred to a non-allowlisted address with transfer or transferFrom. Test2', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); - const transferResult = async () => helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address}); - await expect(transferResult()).to.be.rejectedWith(/common\.AddressNotInAllowlist/); - }); + await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address}); + await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: alice.address}); - itSub('If Public Access mode is set to AllowList, tokens can’t be destroyed by a non-allowlisted address (even if itSub owned them before enabling AllowList mode)', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); - const burnTx = async () => helper.nft.burnToken(bob, collectionId, tokenId); - await expect(burnTx()).to.be.rejectedWith(/common\.NoPermission/); - }); + await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.AddressNotInAllowlist/); + }); - itSub('If Public Access mode is set to AllowList, token transfers can’t be Approved by a non-allowlisted address (see Approve method)', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); - const approveTx = async () => helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); - await expect(approveTx()).to.be.rejectedWith(/common\.AddressNotInAllowlist/); - }); + itSub('If Public Access mode is set to AllowList, tokens can\'t be destroyed by a non-allowlisted address (even if it owned them before enabling AllowList mode)', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); + await expect(helper.nft.burnToken(bob, collectionId, tokenId)) + .to.be.rejectedWith(/common\.NoPermission/); + }); - itSub('If Public Access mode is set to AllowList, tokens can be transferred to a allowlisted address with transfer.', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); - await helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address}); - const owner = await helper.nft.getTokenOwner(collectionId, tokenId); - expect(owner.Substrate).to.be.equal(charlie.address); + itSub('If Public Access mode is set to AllowList, token transfers can\'t be Approved by a non-allowlisted address (see Approve method)', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); + await expect(helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.AddressNotInAllowlist/); + }); }); +}); - itSub('If Public Access mode is set to AllowList, tokens can be transferred to a allowlisted address with transferFrom.', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); - await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address}); +describe('Integration Test ext. Mint if included in Allow List', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; - await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: alice.address}, {Substrate: charlie.address}); - const owner = await helper.nft.getTokenOwner(collectionId, tokenId); - expect(owner.Substrate).to.be.equal(charlie.address); + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); + }); }); - itSub('If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transfer', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); + const permissionSet: ICollectionPermissions[] = [ + {access: 'Normal', mintMode: false}, + {access: 'Normal', mintMode: true}, + {access: 'AllowList', mintMode: false}, + {access: 'AllowList', mintMode: true}, + ]; - await helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address}); - const owner = await helper.nft.getTokenOwner(collectionId, tokenId); - expect(owner.Substrate).to.be.equal(charlie.address); - }); + const testPermissionSuite = async (permissions: ICollectionPermissions) => { + const allowlistedMintingShouldFail = !permissions.mintMode!; - itSub('If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transferFrom', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); - await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address}); + const appropriateRejectionMessage = permissions.mintMode! ? /common\.AddressNotInAllowlist/ : /common\.PublicMintingNotAllowed/; - await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: alice.address}, {Substrate: charlie.address}); - const owner = await helper.nft.getTokenOwner(collectionId, tokenId); - expect(owner.Substrate).to.be.equal(charlie.address); - }); + const allowlistedMintingTest = () => itSub( + `With the condtions above, tokens can${allowlistedMintingShouldFail ? '\'t' : ''} be created by allow-listed addresses`, + async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {}); + await collection.setPermissions(alice, permissions); + await collection.addToAllowList(alice, {Substrate: bob.address}); - itSub('If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens can be created by owner', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: false}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - const token = await helper.nft.getToken(collectionId, tokenId); - expect(token).to.be.not.null; - }); + if (allowlistedMintingShouldFail) + await expect(collection.mintToken(bob, {Substrate: bob.address})).to.be.rejectedWith(appropriateRejectionMessage); + else + await expect(collection.mintToken(bob, {Substrate: bob.address})).to.not.be.rejected; + }, + ); - itSub('If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens can be created by admin', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: false}); - await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address}); - const {tokenId} = await helper.nft.mintToken(bob, {collectionId: collectionId, owner: bob.address}); - const token = await helper.nft.getToken(collectionId, tokenId); - expect(token).to.be.not.null; - }); - itSub('If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens cannot be created by non-privileged and allow-listed address', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: false}); - await helper.collection.addToAllowList(alice, collectionId, {Substrate: bob.address}); - const mintTokenTx = async () => helper.nft.mintToken(bob, {collectionId: collectionId, owner: bob.address}); - await expect(mintTokenTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/); - }); + describe(`Public Access Mode = ${permissions.access}, Mint Mode = ${permissions.mintMode}`, async () => { + describe('Positive', async () => { + itSub('With the condtions above, tokens can be created by owner', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {}); + await collection.setPermissions(alice, permissions); + await expect(collection.mintToken(alice, {Substrate: alice.address})).to.not.be.rejected; + }); - itSub('If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens cannot be created by non-privileged and non-allow listed address', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: false}); - const mintTokenTx = async () => helper.nft.mintToken(bob, {collectionId: collectionId, owner: bob.address}); - await expect(mintTokenTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/); - }); + itSub('With the condtions above, tokens can be created by admin', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {}); + await collection.setPermissions(alice, permissions); + await collection.addAdmin(alice, {Substrate: bob.address}); + await expect(collection.mintToken(bob, {Substrate: bob.address})).to.not.be.rejected; + }); - itSub('If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by owner', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: true}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - const owner = await helper.nft.getTokenOwner(collectionId, tokenId); - expect(owner.Substrate).to.be.equal(alice.address); - }); + if (!allowlistedMintingShouldFail) allowlistedMintingTest(); + }); - itSub('If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by admin', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: true}); - await helper.nft.addAdmin(alice, collectionId, {Substrate: bob.address}); - const {tokenId} = await helper.nft.mintToken(bob, {collectionId: collectionId, owner: bob.address}); - const owner = await helper.nft.getTokenOwner(collectionId, tokenId); - expect(owner.Substrate).to.be.equal(bob.address); - }); + describe('Negative', async () => { + itSub('With the condtions above, tokens can\'t be created by non-priviliged non-allow-listed address', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await collection.setPermissions(alice, permissions); + await expect(collection.mintToken(bob, {Substrate: bob.address})) + .to.be.rejectedWith(appropriateRejectionMessage); + }); - itSub('If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens cannot be created by non-privileged and non-allow listed address', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: true}); - const mintTokenTx = async () => helper.nft.mintToken(bob, {collectionId: collectionId, owner: bob.address}); - await expect(mintTokenTx()).to.be.rejectedWith(/common\.AddressNotInAllowlist/); - }); + if (allowlistedMintingShouldFail) allowlistedMintingTest(); + }); + }); + }; - itSub('If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by non-privileged and allow listed address', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: true}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); - const {tokenId} = await helper.nft.mintToken(bob, {collectionId: collectionId, owner: bob.address}); - const owner = await helper.nft.getTokenOwner(collectionId, tokenId); - expect(owner.Substrate).to.be.equal(bob.address); - }); + for (const permissions of permissionSet) { + testPermissionSuite(permissions); + } }); --- a/tests/src/app-promotion.test.ts +++ b/tests/src/app-promotion.test.ts @@ -20,17 +20,12 @@ getModuleNames, Pallets, } from './util/helpers'; - -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import {usingPlaygrounds} from './util/playgrounds'; - +import {itSub, usingPlaygrounds} from './util/playgrounds'; import {encodeAddress} from '@polkadot/util-crypto'; import {stringToU8a} from '@polkadot/util'; -import {SponsoringMode, contractHelpers, createEthAccountWithBalance, deployFlipper, itWeb3, transferBalanceToEth} from './eth/util/helpers'; +import {SponsoringMode} from './eth/util/helpers'; import {DevUniqueHelper} from './util/playgrounds/unique.dev'; -chai.use(chaiAsPromised); -const expect = chai.expect; +import {itEth, expect} from './eth/util/playgrounds'; let alice: IKeyringPair; let palletAdmin: IKeyringPair; @@ -38,723 +33,651 @@ const palletAddress = calculatePalleteAddress('appstake'); let accounts: IKeyringPair[] = []; const LOCKING_PERIOD = 20n; // 20 blocks of relay -const UNLOCKING_PERIOD = 10n; // 20 blocks of parachain +const UNLOCKING_PERIOD = 10n; // 10 blocks of parachain const rewardAvailableInBlock = (stakedInBlock: bigint) => (stakedInBlock - stakedInBlock % LOCKING_PERIOD) + (LOCKING_PERIOD * 2n); -const beforeEach = async (context: Mocha.Context) => { - await usingPlaygrounds(async (helper, privateKey) => { - if (!getModuleNames(helper.api!).includes(Pallets.AppPromotion)) context.skip(); - alice = privateKey('//Alice'); - palletAdmin = privateKey('//Charlie'); // TODO use custom address - await helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address}))); - nominal = helper.balance.getOneTokenNominal(); - await helper.balance.transferToSubstrate(alice, palletAdmin.address, 1000n * nominal); - await helper.balance.transferToSubstrate(alice, palletAddress, 1000n * nominal); - accounts = await helper.arrange.createCrowd(100, 1000n, alice); // create accounts-pool to speed up tests - }); -}; - -describe('app-promotions.stake extrinsic', () => { +describe('App promotion', () => { before(async function () { - await beforeEach(this); + await usingPlaygrounds(async (helper, privateKey) => { + if (!getModuleNames(helper.api!).includes(Pallets.AppPromotion)) this.skip(); + alice = privateKey('//Alice'); + palletAdmin = privateKey('//Charlie'); // TODO use custom address + await helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address}))); + nominal = helper.balance.getOneTokenNominal(); + await helper.balance.transferToSubstrate(alice, palletAdmin.address, 1000n * nominal); + await helper.balance.transferToSubstrate(alice, palletAddress, 1000n * nominal); + accounts = await helper.arrange.createCrowd(100, 1000n, alice); // create accounts-pool to speed up tests + }); }); - it('should "lock" staking balance, add it to "staked" map, and increase "totalStaked" amount', async () => { - await usingPlaygrounds(async (helper) => { + describe('stake extrinsic', () => { + itSub('should "lock" staking balance, add it to "staked" map, and increase "totalStaked" amount', async ({helper}) => { const [staker, recepient] = [accounts.pop()!, accounts.pop()!]; const totalStakedBefore = await helper.staking.getTotalStaked(); - + // Minimum stake amount is 100: - await expect(helper.staking.stake(staker, 100n * nominal - 1n)).to.be.eventually.rejected; + await expect(helper.staking.stake(staker, 100n * nominal - 1n)).to.be.rejected; await helper.staking.stake(staker, 100n * nominal); - + // Staker balance is: miscFrozen: 100, feeFrozen: 100, reserved: 0n... // ...so he can not transfer 900 expect (await helper.balance.getSubstrateFull(staker.address)).to.contain({miscFrozen: 100n * nominal, feeFrozen: 100n * nominal, reserved: 0n}); - await expect(helper.balance.transferToSubstrate(staker, recepient.address, 900n * nominal)).to.be.rejected; + await expect(helper.balance.transferToSubstrate(staker, recepient.address, 900n * nominal)).to.be.rejectedWith('balances.LiquidityRestrictions'); expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(100n * nominal); expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); // it is potentially flaky test. Promotion can credited some tokens. Maybe we need to use closeTo? expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedBefore + 100n * nominal); // total tokens amount staked in app-promotion increased - + await helper.staking.stake(staker, 200n * nominal); expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(300n * nominal); - expect((await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).map((x) => x[1])).to.be.deep.equal([100n * nominal, 200n * nominal]); + const totalStakedPerBlock = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + expect(totalStakedPerBlock[0].amount).to.equal(100n * nominal); + expect(totalStakedPerBlock[1].amount).to.equal(200n * nominal); }); - }); - - it('should allow to create maximum 10 stakes for account', async () => { - await usingPlaygrounds(async (helper) => { + + itSub('should allow to create maximum 10 stakes for account', async ({helper}) => { const [staker] = await helper.arrange.createAccounts([2000n], alice); for (let i = 0; i < 10; i++) { await helper.staking.stake(staker, 100n * nominal); } - + // can have 10 stakes expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(1000n * nominal); expect(await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).to.have.length(10); - - await expect(helper.staking.stake(staker, 100n * nominal)).to.be.rejected; - + + await expect(helper.staking.stake(staker, 100n * nominal)).to.be.rejectedWith('appPromotion.NoPermission'); + // After unstake can stake again await helper.staking.unstake(staker); await helper.staking.stake(staker, 100n * nominal); expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.equal(100n * nominal); }); - }); + + itSub('should reject transaction if stake amount is more than total free balance minus frozen', async ({helper}) => { + const staker = accounts.pop()!; - it('should reject transaction if stake amount is more than total free balance minus frozen', async () => { - await usingPlaygrounds(async helper => { - const staker = accounts.pop()!; - // Can't stake full balance because Alice needs to pay some fee - await expect(helper.staking.stake(staker, 1000n * nominal)).to.be.eventually.rejected; + await expect(helper.staking.stake(staker, 1000n * nominal)).to.be.rejected; await helper.staking.stake(staker, 500n * nominal); - + // Can't stake 500 tkn because Alice has Less than 500 transferable; - await expect(helper.staking.stake(staker, 500n * nominal)).to.be.eventually.rejected; + await expect(helper.staking.stake(staker, 500n * nominal)).to.be.rejectedWith('balances.LiquidityRestrictions'); expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(500n * nominal); }); - }); - - it('for different accounts in one block is possible', async () => { - await usingPlaygrounds(async helper => { + + itSub('for different accounts in one block is possible', async ({helper}) => { const crowd = [accounts.pop()!, accounts.pop()!, accounts.pop()!, accounts.pop()!]; - + const crowdStartsToStake = crowd.map(user => helper.staking.stake(user, 100n * nominal)); - await expect(Promise.all(crowdStartsToStake)).to.be.eventually.fulfilled; - + await expect(Promise.all(crowdStartsToStake)).to.be.fulfilled; + const crowdStakes = await Promise.all(crowd.map(address => helper.staking.getTotalStaked({Substrate: address.address}))); expect(crowdStakes).to.deep.equal([100n * nominal, 100n * nominal, 100n * nominal, 100n * nominal]); }); }); -}); - -describe('unstake balance extrinsic', () => { - before(async function () { - await beforeEach(this); - }); - - it('should change balance state from "frozen" to "reserved", add it to "pendingUnstake" map, and subtract it from totalStaked', async () => { - await usingPlaygrounds(async helper => { + + describe('unstake extrinsic', () => { + itSub('should change balance state from "frozen" to "reserved", add it to "pendingUnstake" map, and subtract it from totalStaked', async ({helper}) => { const [staker, recepient] = [accounts.pop()!, accounts.pop()!]; const totalStakedBefore = await helper.staking.getTotalStaked(); await helper.staking.stake(staker, 900n * nominal); await helper.staking.unstake(staker); - + // Right after unstake balance is reserved // Staker can not transfer expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 900n * nominal, miscFrozen: 0n, feeFrozen: 0n}); - await expect(helper.balance.transferToSubstrate(staker, recepient.address, 100n * nominal)).to.be.rejected; + await expect(helper.balance.transferToSubstrate(staker, recepient.address, 100n * nominal)).to.be.rejectedWith('balances.InsufficientBalance'); expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(900n * nominal); expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n); expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedBefore); }); - }); - - it('should unlock balance after unlocking period ends and remove it from "pendingUnstake"', async () => { - await usingPlaygrounds(async (helper) => { + + itSub('should unlock balance after unlocking period ends and remove it from "pendingUnstake"', async ({helper}) => { const staker = accounts.pop()!; await helper.staking.stake(staker, 100n * nominal); await helper.staking.unstake(staker); - const unstakedInBlock = (await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}))[0][0]; - + const [pendingUnstake] = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}); + // Wait for unstaking period. Balance now free ~1000; reserved, frozen, miscFrozeb: 0n - await helper.wait.forParachainBlockNumber(unstakedInBlock); + await helper.wait.forParachainBlockNumber(pendingUnstake.block); expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, miscFrozen: 0n, feeFrozen: 0n}); expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); - + // staker can transfer: await helper.balance.transferToSubstrate(staker, alice.address, 998n * nominal); expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(1n); }); - }); - - it('should successfully unstake multiple stakes', async () => { - await usingPlaygrounds(async helper => { + + itSub('should successfully unstake multiple stakes', async ({helper}) => { const staker = accounts.pop()!; await helper.staking.stake(staker, 100n * nominal); await helper.staking.stake(staker, 200n * nominal); await helper.staking.stake(staker, 300n * nominal); - + // staked: [100, 200, 300]; unstaked: 0 - let pendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address}); - let unstakedPerBlock = (await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address})).map(stake => stake[1]); - let stakedPerBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).map(stake => stake[1]); - expect(pendingUnstake).to.be.deep.equal(0n); - expect(unstakedPerBlock).to.be.deep.equal([]); - expect(stakedPerBlock).to.be.deep.equal([100n * nominal, 200n * nominal, 300n * nominal]); - + let totalPendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address}); + let pendingUnstake = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}); + let stakes = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + expect(totalPendingUnstake).to.be.deep.equal(0n); + expect(pendingUnstake).to.be.deep.equal([]); + expect(stakes[0].amount).to.equal(100n * nominal); + expect(stakes[1].amount).to.equal(200n * nominal); + expect(stakes[2].amount).to.equal(300n * nominal); + // Can unstake multiple stakes await helper.staking.unstake(staker); - const unstakingBlock = (await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}))[0][0]; - pendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address}); - unstakedPerBlock = (await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address})).map(stake => stake[1]); - stakedPerBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).map(stake => stake[1]); - expect(pendingUnstake).to.be.equal(600n * nominal); - expect(stakedPerBlock).to.be.deep.equal([]); - expect(unstakedPerBlock).to.be.deep.equal([600n * nominal]); - + pendingUnstake = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}); + totalPendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address}); + stakes = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + expect(totalPendingUnstake).to.be.equal(600n * nominal); + expect(stakes).to.be.deep.equal([]); + expect(pendingUnstake[0].amount).to.equal(600n * nominal); + expect (await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 600n * nominal, feeFrozen: 0n, miscFrozen: 0n}); - await helper.wait.forParachainBlockNumber(unstakingBlock); + await helper.wait.forParachainBlockNumber(pendingUnstake[0].block); expect (await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, feeFrozen: 0n, miscFrozen: 0n}); expect (await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); }); - }); - - it('should not have any effects if no active stakes', async () => { - await usingPlaygrounds(async (helper) => { + + itSub('should not have any effects if no active stakes', async ({helper}) => { const staker = accounts.pop()!; - + // unstake has no effect if no stakes at all await helper.staking.unstake(staker); expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(0n); expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); // TODO bigint closeTo helper - + // TODO stake() unstake() waitUnstaked() unstake(); - + // can't unstake if there are only pendingUnstakes await helper.staking.stake(staker, 100n * nominal); await helper.staking.unstake(staker); await helper.staking.unstake(staker); - + expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(100n * nominal); expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n); }); - }); - - it('should keep different unlocking block for each unlocking stake', async () => { - await usingPlaygrounds(async (helper) => { + + itSub('should keep different unlocking block for each unlocking stake', async ({helper}) => { const staker = accounts.pop()!; await helper.staking.stake(staker, 100n * nominal); await helper.staking.unstake(staker); await helper.staking.stake(staker, 120n * nominal); await helper.staking.unstake(staker); - + const unstakingPerBlock = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}); expect(unstakingPerBlock).has.length(2); - expect(unstakingPerBlock[0][1]).to.equal(100n * nominal); - expect(unstakingPerBlock[1][1]).to.equal(120n * nominal); + expect(unstakingPerBlock[0].amount).to.equal(100n * nominal); + expect(unstakingPerBlock[1].amount).to.equal(120n * nominal); }); - }); - - it('should be possible for different accounts in one block', async () => { - await usingPlaygrounds(async (helper) => { + + itSub('should be possible for different accounts in one block', async ({helper}) => { const stakers = [accounts.pop()!, accounts.pop()!, accounts.pop()!]; - + await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal))); await Promise.all(stakers.map(staker => helper.staking.unstake(staker))); - + await Promise.all(stakers.map(async (staker) => { expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(100n * nominal); expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n); })); }); }); -}); - -describe('Admin adress', () => { - before(async function () { - await beforeEach(this); - }); - - it('can be set by sudo only', async () => { - await usingPlaygrounds(async (helper) => { + + describe('admin adress', () => { + itSub('can be set by sudo only', async ({helper}) => { const nonAdmin = accounts.pop()!; // nonAdmin can not set admin not from himself nor as a sudo - await expect(helper.signTransaction(nonAdmin, helper.api!.tx.appPromotion.setAdminAddress({Substrate: nonAdmin.address}))).to.be.eventually.rejected; - await expect(helper.signTransaction(nonAdmin, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress({Substrate: nonAdmin.address})))).to.be.eventually.rejected; - + await expect(helper.signTransaction(nonAdmin, helper.api!.tx.appPromotion.setAdminAddress({Substrate: nonAdmin.address}))).to.be.rejected; + await expect(helper.signTransaction(nonAdmin, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress({Substrate: nonAdmin.address})))).to.be.rejected; + // Alice can - await expect(helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})))).to.be.eventually.fulfilled; + await expect(helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})))).to.be.fulfilled; }); - }); - - it('can be any valid CrossAccountId', async () => { - // We are not going to set an eth address as a sponsor, - // but we do want to check, it doesn't break anything; - await usingPlaygrounds(async (helper) => { + + itSub('can be any valid CrossAccountId', async ({helper}) => { + // We are not going to set an eth address as a sponsor, + // but we do want to check, it doesn't break anything; const account = accounts.pop()!; const ethAccount = helper.address.substrateToEth(account.address); // Alice sets Ethereum address as a sudo. Then Substrate address back... - await expect(helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress({Ethereum: ethAccount})))).to.be.eventually.fulfilled; - await expect(helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})))).to.be.eventually.fulfilled; - + await expect(helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress({Ethereum: ethAccount})))).to.be.fulfilled; + await expect(helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})))).to.be.fulfilled; + // ...It doesn't break anything; const collection = await helper.nft.mintCollection(account, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); - await expect(helper.signTransaction(account, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.eventually.rejected; + await expect(helper.signTransaction(account, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected; }); - }); - - it('can be reassigned', async () => { - await usingPlaygrounds(async (helper) => { + + itSub('can be reassigned', async ({helper}) => { const [oldAdmin, newAdmin, collectionOwner] = [accounts.pop()!, accounts.pop()!, accounts.pop()!]; const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); - - await expect(helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress(normalizeAccountId(oldAdmin))))).to.be.eventually.fulfilled; - await expect(helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress(normalizeAccountId(newAdmin))))).to.be.eventually.fulfilled; - await expect(helper.signTransaction(oldAdmin, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.eventually.rejected; - - await expect(helper.signTransaction(newAdmin, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.eventually.fulfilled; + + await expect(helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress(normalizeAccountId(oldAdmin))))).to.be.fulfilled; + await expect(helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress(normalizeAccountId(newAdmin))))).to.be.fulfilled; + await expect(helper.signTransaction(oldAdmin, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected; + + await expect(helper.signTransaction(newAdmin, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.fulfilled; }); }); -}); - -describe('App-promotion collection sponsoring', () => { - before(async function () { - await beforeEach(this); - await usingPlaygrounds(async (helper) => { - const tx = helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})); - await helper.signTransaction(alice, tx); + + describe('collection sponsoring', () => { + before(async function () { + await usingPlaygrounds(async (helper) => { + const tx = helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})); + await helper.signTransaction(alice, tx); + }); }); - }); - - it('should actually sponsor transactions', async () => { - await usingPlaygrounds(async (helper) => { + + itSub('should actually sponsor transactions', async ({helper}) => { const [collectionOwner, tokenSender, receiver] = [accounts.pop()!, accounts.pop()!, accounts.pop()!]; const collection = await helper.nft.mintCollection(collectionOwner, {name: 'Name', description: 'Description', tokenPrefix: 'Prefix', limits: {sponsorTransferTimeout: 0}}); const token = await collection.mintToken(collectionOwner, {Substrate: tokenSender.address}); await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId)); const palletBalanceBefore = await helper.balance.getSubstrate(palletAddress); - + await token.transfer(tokenSender, {Substrate: receiver.address}); expect (await token.getOwner()).to.be.deep.equal({Substrate: receiver.address}); const palletBalanceAfter = await helper.balance.getSubstrate(palletAddress); - + // senders balance the same, transaction has sponsored expect (await helper.balance.getSubstrate(tokenSender.address)).to.be.equal(1000n * nominal); expect (palletBalanceBefore > palletBalanceAfter).to.be.true; }); - }); - - it('can not be set by non admin', async () => { - await usingPlaygrounds(async (helper) => { + + itSub('can not be set by non admin', async ({helper}) => { const [collectionOwner, nonAdmin] = [accounts.pop()!, accounts.pop()!]; - + const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); - - await expect(helper.signTransaction(nonAdmin, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.eventually.rejected; + + await expect(helper.signTransaction(nonAdmin, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected; expect((await collection.getData())?.raw.sponsorship).to.equal('Disabled'); }); - }); - - it('should set pallet address as confirmed admin', async () => { - await usingPlaygrounds(async (helper) => { + + itSub('should set pallet address as confirmed admin', async ({helper}) => { const [collectionOwner, oldSponsor] = [accounts.pop()!, accounts.pop()!]; - + // Can set sponsoring for collection without sponsor const collectionWithoutSponsor = await helper.nft.mintCollection(collectionOwner, {name: 'No-sponsor', description: 'New Collection', tokenPrefix: 'Promotion'}); - await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collectionWithoutSponsor.collectionId))).to.be.eventually.fulfilled; + await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collectionWithoutSponsor.collectionId))).to.be.fulfilled; expect((await collectionWithoutSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress}); - + // Can set sponsoring for collection with unconfirmed sponsor const collectionWithUnconfirmedSponsor = await helper.nft.mintCollection(collectionOwner, {name: 'Unconfirmed', description: 'New Collection', tokenPrefix: 'Promotion', pendingSponsor: oldSponsor.address}); expect((await collectionWithUnconfirmedSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Unconfirmed: oldSponsor.address}); - await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collectionWithUnconfirmedSponsor.collectionId))).to.be.eventually.fulfilled; + await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collectionWithUnconfirmedSponsor.collectionId))).to.be.fulfilled; expect((await collectionWithUnconfirmedSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress}); - + // Can set sponsoring for collection with confirmed sponsor const collectionWithConfirmedSponsor = await helper.nft.mintCollection(collectionOwner, {name: 'Confirmed', description: 'New Collection', tokenPrefix: 'Promotion', pendingSponsor: oldSponsor.address}); await collectionWithConfirmedSponsor.confirmSponsorship(oldSponsor); - await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collectionWithConfirmedSponsor.collectionId))).to.be.eventually.fulfilled; + await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collectionWithConfirmedSponsor.collectionId))).to.be.fulfilled; expect((await collectionWithConfirmedSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress}); }); - }); - - it('can be overwritten by collection owner', async () => { - await usingPlaygrounds(async (helper) => { + + itSub('can be overwritten by collection owner', async ({helper}) => { const [collectionOwner, newSponsor] = [accounts.pop()!, accounts.pop()!]; const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); const collectionId = collection.collectionId; - - await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collectionId))).to.be.eventually.fulfilled; - + + await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collectionId))).to.be.fulfilled; + // Collection limits still can be changed by the owner expect(await collection.setLimits(collectionOwner, {sponsorTransferTimeout: 0})).to.be.true; expect((await collection.getData())?.raw.limits.sponsorTransferTimeout).to.be.equal(0); expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress}); - + // Collection sponsor can be changed too expect((await collection.setSponsor(collectionOwner, newSponsor.address))).to.be.true; expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Unconfirmed: newSponsor.address}); }); - }); - - it('should not overwrite collection limits set by the owner earlier', async () => { - await usingPlaygrounds(async (helper) => { + + itSub('should not overwrite collection limits set by the owner earlier', async ({helper}) => { const limits = {ownerCanDestroy: true, ownerCanTransfer: true, sponsorTransferTimeout: 0}; const collectionWithLimits = await helper.nft.mintCollection(alice, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion', limits}); - - await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collectionWithLimits.collectionId))).to.be.eventually.fulfilled; + + await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collectionWithLimits.collectionId))).to.be.fulfilled; expect((await collectionWithLimits.getData())?.raw.limits).to.be.deep.contain(limits); }); - }); - - it('should reject transaction if collection doesn\'t exist', async () => { - await usingPlaygrounds(async (helper) => { + + itSub('should reject transaction if collection doesn\'t exist', async ({helper}) => { const collectionOwner = accounts.pop()!; - + // collection has never existed - await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(999999999))).to.be.eventually.rejected; + await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(999999999))).to.be.rejected; // collection has been burned const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); await collection.burn(collectionOwner); - - await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.eventually.rejected; + + await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected; }); }); -}); - -describe('app-promotion stopSponsoringCollection', () => { - before(async function () { - await beforeEach(this); - }); - - it('can not be called by non-admin', async () => { - await usingPlaygrounds(async (helper) => { + + describe('stopSponsoringCollection', () => { + itSub('can not be called by non-admin', async ({helper}) => { const [collectionOwner, nonAdmin] = [accounts.pop()!, accounts.pop()!]; const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); - - await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.eventually.fulfilled; - - await expect(helper.signTransaction(nonAdmin, helper.api!.tx.appPromotion.stopSponsoringCollection(collection.collectionId))).to.be.eventually.rejected; + + await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.fulfilled; + + await expect(helper.signTransaction(nonAdmin, helper.api!.tx.appPromotion.stopSponsoringCollection(collection.collectionId))).to.be.rejected; expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress}); }); - }); - - it('should set sponsoring as disabled', async () => { - await usingPlaygrounds(async (helper) => { + + itSub('should set sponsoring as disabled', async ({helper}) => { const [collectionOwner, recepient] = [accounts.pop()!, accounts.pop()!]; const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion', limits: {sponsorTransferTimeout: 0}}); const token = await collection.mintToken(collectionOwner, {Substrate: collectionOwner.address}); - + await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId)); await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.stopSponsoringCollection(collection.collectionId)); - + expect((await collection.getData())?.raw.sponsorship).to.be.equal('Disabled'); - + // Transactions are not sponsored anymore: const ownerBalanceBefore = await helper.balance.getSubstrate(collectionOwner.address); await token.transfer(collectionOwner, {Substrate: recepient.address}); const ownerBalanceAfter = await helper.balance.getSubstrate(collectionOwner.address); expect(ownerBalanceAfter < ownerBalanceBefore).to.be.equal(true); }); - }); - - it('should not affect collection which is not sponsored by pallete', async () => { - await usingPlaygrounds(async (helper) => { + + itSub('should not affect collection which is not sponsored by pallete', async ({helper}) => { const collectionOwner = accounts.pop()!; const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion', pendingSponsor: collectionOwner.address}); await collection.confirmSponsorship(collectionOwner); - - await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.stopSponsoringCollection(collection.collectionId))).to.be.eventually.rejected; - + + await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.stopSponsoringCollection(collection.collectionId))).to.be.rejected; + expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: collectionOwner.address}); }); - }); - - it('should reject transaction if collection does not exist', async () => { - await usingPlaygrounds(async (helper) => { + + itSub('should reject transaction if collection does not exist', async ({helper}) => { const collectionOwner = accounts.pop()!; const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); - + await collection.burn(collectionOwner); - await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.stopSponsoringCollection(collection.collectionId))).to.be.eventually.rejected; - await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.stopSponsoringCollection(999999999))).to.be.eventually.rejected; + await expect(helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.stopSponsoringCollection', [collection.collectionId], true)).to.be.rejectedWith('common.CollectionNotFound'); + await expect(helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.stopSponsoringCollection', [999_999_999], true)).to.be.rejectedWith('common.CollectionNotFound'); }); }); -}); - -describe('app-promotion contract sponsoring', () => { - before(async function () { - await beforeEach(this); - }); - - itWeb3('should set palletes address as a sponsor', async ({api, web3, privateKeyWrapper}) => { - await usingPlaygrounds(async (helper) => { - const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase(); - const flipper = await deployFlipper(web3, contractOwner); - const contractMethods = contractHelpers(web3, contractOwner); - - await helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorContract(flipper.options.address)); + + describe('contract sponsoring', () => { + itEth('should set palletes address as a sponsor', async ({helper}) => { + const contractOwner = (await helper.eth.createAccountWithBalance(alice, 1000n)).toLowerCase(); + const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner); + const contractHelper = helper.ethNativeContract.contractHelpers(contractOwner); + + await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address]); - expect(await contractMethods.methods.hasSponsor(flipper.options.address).call()).to.be.true; - expect((await api.query.evmContractHelpers.owner(flipper.options.address)).toJSON()).to.be.equal(contractOwner); - expect((await api.query.evmContractHelpers.sponsoring(flipper.options.address)).toJSON()).to.deep.equal({ + expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.true; + expect((await helper.api!.query.evmContractHelpers.owner(flipper.options.address)).toJSON()).to.be.equal(contractOwner); + expect((await helper.api!.query.evmContractHelpers.sponsoring(flipper.options.address)).toJSON()).to.deep.equal({ confirmed: { substrate: palletAddress, }, }); }); - }); - - itWeb3('should overwrite sponsoring mode and existed sponsor', async ({api, web3, privateKeyWrapper}) => { - await usingPlaygrounds(async (helper) => { - const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase(); - const flipper = await deployFlipper(web3, contractOwner); - const contractMethods = contractHelpers(web3, contractOwner); - - await expect(contractMethods.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.not.rejected; - + + itEth('should overwrite sponsoring mode and existed sponsor', async ({helper}) => { + const contractOwner = (await helper.eth.createAccountWithBalance(alice, 1000n)).toLowerCase(); + const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner); + const contractHelper = helper.ethNativeContract.contractHelpers(contractOwner); + + await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.fulfilled; + // Contract is self sponsored - expect((await api.query.evmContractHelpers.sponsoring(flipper.options.address)).toJSON()).to.be.deep.equal({ + expect((await helper.api!.query.evmContractHelpers.sponsoring(flipper.options.address)).toJSON()).to.be.deep.equal({ confirmed: { ethereum: flipper.options.address.toLowerCase(), }, }); - + // set promotion sponsoring - await helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorContract(flipper.options.address)); - + await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true); + // new sponsor is pallet address - expect(await contractMethods.methods.hasSponsor(flipper.options.address).call()).to.be.true; - expect((await api.query.evmContractHelpers.owner(flipper.options.address)).toJSON()).to.be.equal(contractOwner); - expect((await api.query.evmContractHelpers.sponsoring(flipper.options.address)).toJSON()).to.deep.equal({ + expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.true; + expect((await helper.callRpc('api.query.evmContractHelpers.owner', [flipper.options.address])).toJSON()).to.be.equal(contractOwner); + expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({ confirmed: { substrate: palletAddress, }, }); }); - }); - - itWeb3('can be overwritten by contract owner', async ({api, web3, privateKeyWrapper}) => { - await usingPlaygrounds(async (helper) => { - const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase(); - const flipper = await deployFlipper(web3, contractOwner); - const contractMethods = contractHelpers(web3, contractOwner); - + + itEth('can be overwritten by contract owner', async ({helper}) => { + const contractOwner = (await helper.eth.createAccountWithBalance(alice, 1000n)).toLowerCase(); + const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner); + const contractHelper = helper.ethNativeContract.contractHelpers(contractOwner); + // contract sponsored by pallet - await helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorContract(flipper.options.address)); - + await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true); + // owner sets self sponsoring - await expect(contractMethods.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.not.rejected; - - expect(await contractMethods.methods.hasSponsor(flipper.options.address).call()).to.be.true; - expect((await api.query.evmContractHelpers.owner(flipper.options.address)).toJSON()).to.be.equal(contractOwner); - expect((await api.query.evmContractHelpers.sponsoring(flipper.options.address)).toJSON()).to.deep.equal({ + await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.not.rejected; + + expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.true; + expect((await helper.api!.query.evmContractHelpers.owner(flipper.options.address)).toJSON()).to.be.equal(contractOwner); + expect((await helper.api!.query.evmContractHelpers.sponsoring(flipper.options.address)).toJSON()).to.deep.equal({ confirmed: { ethereum: flipper.options.address.toLowerCase(), }, }); }); - }); - - itWeb3('can not be set by non admin', async ({api, web3, privateKeyWrapper}) => { - await usingPlaygrounds(async (helper) => { + + itEth('can not be set by non admin', async ({helper}) => { const nonAdmin = accounts.pop()!; - const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase(); - const flipper = await deployFlipper(web3, contractOwner); - const contractMethods = contractHelpers(web3, contractOwner); - - await expect(contractMethods.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.not.rejected; - + const contractOwner = (await helper.eth.createAccountWithBalance(alice, 1000n)).toLowerCase(); + const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner); + const contractHelper = helper.ethNativeContract.contractHelpers(contractOwner); + + await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.fulfilled; + // nonAdmin calls sponsorContract - await expect(helper.signTransaction(nonAdmin, api.tx.appPromotion.sponsorContract(flipper.options.address))).to.be.rejected; - + await expect(helper.executeExtrinsic(nonAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true)).to.be.rejectedWith('appPromotion.NoPermission'); + // contract still self-sponsored - expect((await api.query.evmContractHelpers.sponsoring(flipper.options.address)).toJSON()).to.deep.equal({ + expect((await helper.api!.query.evmContractHelpers.sponsoring(flipper.options.address)).toJSON()).to.deep.equal({ confirmed: { ethereum: flipper.options.address.toLowerCase(), }, }); }); - - itWeb3('should be rejected for non-contract address', async ({api, web3, privateKeyWrapper}) => { - await usingPlaygrounds(async (helper) => { - - }); - }); - }); - - itWeb3('should actually sponsor transactions', async ({api, web3, privateKeyWrapper}) => { - await usingPlaygrounds(async (helper) => { - const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase(); - const flipper = await deployFlipper(web3, contractOwner); - const contractHelper = contractHelpers(web3, contractOwner); + + itEth('should actually sponsor transactions', async ({helper}) => { + // Contract caller + const caller = await helper.eth.createAccountWithBalance(alice, 1000n); + const palletBalanceBefore = await helper.balance.getSubstrate(palletAddress); + + // Deploy flipper + const contractOwner = (await helper.eth.createAccountWithBalance(alice, 1000n)).toLowerCase(); + const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner); + const contractHelper = helper.ethNativeContract.contractHelpers(contractOwner); + + // Owner sets to sponsor every tx await contractHelper.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: contractOwner}); await contractHelper.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: contractOwner}); - await transferBalanceToEth(api, alice, flipper.options.address, 1000n); - - await helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorContract(flipper.options.address)); + await helper.eth.transferBalanceFromSubstrate(alice, flipper.options.address, 1000n); // transferBalanceToEth(api, alice, flipper.options.address, 1000n); + + // Set promotion to the Flipper + await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorContract(flipper.options.address)); + + // Caller calls Flipper await flipper.methods.flip().send({from: caller}); expect(await flipper.methods.getValue().call()).to.be.true; - + + // The contracts and caller balances have not changed const callerBalance = await helper.balance.getEthereum(caller); const contractBalanceAfter = await helper.balance.getEthereum(flipper.options.address); - expect(callerBalance).to.be.equal(1000n * nominal); - expect(1000n * nominal > contractBalanceAfter).to.be.true; + expect(1000n * nominal === contractBalanceAfter).to.be.true; + + // The pallet balance has decreased + const palletBalanceAfter = await helper.balance.getSubstrate(palletAddress); + expect(palletBalanceAfter < palletBalanceBefore).to.be.true; }); }); -}); - -describe('app-promotion stopSponsoringContract', () => { - before(async function () { - await beforeEach(this); - }); - - itWeb3('should remove pallet address from contract sponsors', async ({api, web3, privateKeyWrapper}) => { - await usingPlaygrounds(async (helper) => { - const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase(); - const flipper = await deployFlipper(web3, contractOwner); - await transferBalanceToEth(api, alice, flipper.options.address); - const contractHelper = contractHelpers(web3, contractOwner); + + describe('stopSponsoringContract', () => { + itEth('should remove pallet address from contract sponsors', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(alice, 1000n); + const contractOwner = (await helper.eth.createAccountWithBalance(alice, 1000n)).toLowerCase(); + const flipper = await helper.eth.deployFlipper(contractOwner); + await helper.eth.transferBalanceFromSubstrate(alice, flipper.options.address); + const contractHelper = helper.ethNativeContract.contractHelpers(contractOwner); + await contractHelper.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: contractOwner}); - await helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorContract(flipper.options.address)); - await helper.signTransaction(palletAdmin, api.tx.appPromotion.stopSponsoringContract(flipper.options.address)); - + await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true); + await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.stopSponsoringContract', [flipper.options.address], true); + expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.false; - expect((await api.query.evmContractHelpers.owner(flipper.options.address)).toJSON()).to.be.equal(contractOwner); - expect((await api.query.evmContractHelpers.sponsoring(flipper.options.address)).toJSON()).to.deep.equal({ + expect((await helper.api!.query.evmContractHelpers.owner(flipper.options.address)).toJSON()).to.be.equal(contractOwner); + expect((await helper.api!.query.evmContractHelpers.sponsoring(flipper.options.address)).toJSON()).to.deep.equal({ disabled: null, }); - + await flipper.methods.flip().send({from: caller}); expect(await flipper.methods.getValue().call()).to.be.true; - + const callerBalance = await helper.balance.getEthereum(caller); const contractBalanceAfter = await helper.balance.getEthereum(flipper.options.address); - + // caller payed for call expect(1000n * nominal > callerBalance).to.be.true; expect(contractBalanceAfter).to.be.equal(1000n * nominal); }); - }); - - itWeb3('can not be called by non-admin', async ({api, web3, privateKeyWrapper}) => { - await usingPlaygrounds(async (helper) => { + + itEth('can not be called by non-admin', async ({helper}) => { const nonAdmin = accounts.pop()!; - const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase(); - const flipper = await deployFlipper(web3, contractOwner); - - await helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorContract(flipper.options.address)); - await expect(helper.signTransaction(nonAdmin, api.tx.appPromotion.stopSponsoringContract(flipper.options.address))).to.be.rejected; + const contractOwner = (await helper.eth.createAccountWithBalance(alice, 1000n)).toLowerCase(); + const flipper = await helper.eth.deployFlipper(contractOwner); + + await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address]); + await expect(helper.executeExtrinsic(nonAdmin, 'api.tx.appPromotion.stopSponsoringContract', [flipper.options.address])) + .to.be.rejectedWith(/appPromotion\.NoPermission/); }); - }); - - itWeb3('should not affect a contract which is not sponsored by pallete', async ({api, web3, privateKeyWrapper}) => { - await usingPlaygrounds(async (helper) => { + + itEth('should not affect a contract which is not sponsored by pallete', async ({helper}) => { const nonAdmin = accounts.pop()!; - const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase(); - const flipper = await deployFlipper(web3, contractOwner); - const contractHelper = contractHelpers(web3, contractOwner); - await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.not.rejected; - - await expect(helper.signTransaction(nonAdmin, api.tx.appPromotion.stopSponsoringContract(flipper.options.address))).to.be.rejected; + const contractOwner = (await helper.eth.createAccountWithBalance(alice, 1000n)).toLowerCase(); + const flipper = await helper.eth.deployFlipper(contractOwner); + const contractHelper = helper.ethNativeContract.contractHelpers(contractOwner); + await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.fulfilled; + + await expect(helper.executeExtrinsic(nonAdmin, 'api.tx.appPromotion.stopSponsoringContract', [flipper.options.address], true)).to.be.rejectedWith('appPromotion.NoPermission'); }); }); -}); - -describe('app-promotion rewards', () => { - before(async function () { - await beforeEach(this); - }); - - it('can not be called by non admin', async () => { - await usingPlaygrounds(async (helper) => { + + describe('rewards', () => { + itSub('can not be called by non admin', async ({helper}) => { const nonAdmin = accounts.pop()!; - await expect(helper.signTransaction(nonAdmin, helper.api!.tx.appPromotion.payoutStakers(100))).to.be.rejected; + await expect(helper.admin.payoutStakers(nonAdmin, 100)).to.be.rejectedWith('appPromotion.NoPermission'); }); - }); - - it('should credit 0.05% for staking period', async () => { - await usingPlaygrounds(async helper => { + + itSub('should increase total staked', async ({helper}) => { const staker = accounts.pop()!; - + const totalStakedBefore = await helper.staking.getTotalStaked(); + await helper.staking.stake(staker, 100n * nominal); + + // Wait for rewards and pay + const [stakedInBlock] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock.block)); + const totalPayout = (await helper.admin.payoutStakers(palletAdmin, 100)).reduce((prev, payout) => prev + payout.payout, 0n); + + const totalStakedAfter = await helper.staking.getTotalStaked(); + expect(totalStakedAfter).to.equal(totalStakedBefore + (100n * nominal) + totalPayout); + // staker can unstake + await helper.staking.unstake(staker); + expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedAfter - calculateIncome(100n * nominal, 10n)); + }); + + itSub('should credit 0.05% for staking period', async ({helper}) => { + const staker = accounts.pop()!; + await waitPromotionPeriodDoesntEnd(helper); - + await helper.staking.stake(staker, 100n * nominal); await helper.staking.stake(staker, 200n * nominal); - + // wait rewards are available: - const stakedInBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}))[1][0]; - await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock)); - - await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.payoutStakers(100)); - - const totalStakedPerBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).map(s => s[1]); - expect(totalStakedPerBlock).to.be.deep.equal([calculateIncome(100n * nominal, 10n), calculateIncome(200n * nominal, 10n)]); + const [_, stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake2.block)); + + const payoutToStaker = (await helper.admin.payoutStakers(palletAdmin, 100)).find((payout) => payout.staker === staker.address)?.payout; + expect(payoutToStaker + 300n * nominal).to.equal(calculateIncome(300n * nominal, 10n)); + + const totalStakedPerBlock = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + expect(totalStakedPerBlock[0].amount).to.equal(calculateIncome(100n * nominal, 10n)); + expect(totalStakedPerBlock[1].amount).to.equal(calculateIncome(200n * nominal, 10n)); }); - }); - - it('shoud be paid for more than one period if payments was missed', async () => { - await usingPlaygrounds(async (helper) => { + + itSub('shoud be paid for more than one period if payments was missed', async ({helper}) => { const staker = accounts.pop()!; - + await helper.staking.stake(staker, 100n * nominal); // wait for two rewards are available: - const stakedInBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}))[0][0]; - await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock) + LOCKING_PERIOD); - - await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.payoutStakers(100)); - const stakedPerBlock = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + let [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD); + + await helper.admin.payoutStakers(palletAdmin, 100); + [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); const frozenBalanceShouldBe = calculateIncome(100n * nominal, 10n, 2); - expect(stakedPerBlock[0][1]).to.be.equal(frozenBalanceShouldBe); - + expect(stake.amount).to.be.equal(frozenBalanceShouldBe); + const stakerFullBalance = await helper.balance.getSubstrateFull(staker.address); - + expect(stakerFullBalance).to.contain({reserved: 0n, feeFrozen: frozenBalanceShouldBe, miscFrozen: frozenBalanceShouldBe}); }); - }); - - it('should not be credited for unstaked (reserved) balance', async () => { - await usingPlaygrounds(async helper => { + + itSub('should not be credited for unstaked (reserved) balance', async ({helper}) => { // staker unstakes before rewards has been payed const staker = accounts.pop()!; await helper.staking.stake(staker, 100n * nominal); - const stakedInBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}))[0][0]; - await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock) + LOCKING_PERIOD); + const [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD); await helper.staking.unstake(staker); - + // so he did not receive any rewards const totalBalanceBefore = await helper.balance.getSubstrate(staker.address); - await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.payoutStakers(100)); + await helper.admin.payoutStakers(palletAdmin, 100); const totalBalanceAfter = await helper.balance.getSubstrate(staker.address); - + expect(totalBalanceBefore).to.be.equal(totalBalanceAfter); }); - }); - - it('should bring compound interest', async () => { - await usingPlaygrounds(async helper => { + + itSub('should bring compound interest', async ({helper}) => { const staker = accounts.pop()!; - + await helper.staking.stake(staker, 100n * nominal); - - const stakedInBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}))[0][0]; - await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock)); - - await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.payoutStakers(100)); - let totalStakedPerBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).map(s => s[1]); - expect(totalStakedPerBlock).to.deep.equal([calculateIncome(100n * nominal, 10n)]); - - await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock) + LOCKING_PERIOD); - await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.payoutStakers(100)); - totalStakedPerBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).map(s => s[1]); - expect(totalStakedPerBlock).to.deep.equal([calculateIncome(100n * nominal, 10n, 2)]); + + let [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block)); + + await helper.admin.payoutStakers(palletAdmin, 100); + [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + expect(stake.amount).to.equal(calculateIncome(100n * nominal, 10n)); + + await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD); + await helper.admin.payoutStakers(palletAdmin, 100); + [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + expect(stake.amount).to.equal(calculateIncome(100n * nominal, 10n, 2)); }); - }); - - it.skip('can be paid 1000 rewards in a time', async () => { - // all other stakes should be unstaked - await usingPlaygrounds(async (helper) => { + + itSub.skip('can be paid 1000 rewards in a time', async ({helper}) => { + // all other stakes should be unstaked const oneHundredStakers = await helper.arrange.createCrowd(100, 1050n, alice); - + // stakers stakes 10 times each for (let i = 0; i < 10; i++) { await Promise.all(oneHundredStakers.map(staker => helper.staking.stake(staker, 100n * nominal))); } await helper.wait.newBlocks(40); - const result = await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.payoutStakers(100)); + await helper.admin.payoutStakers(palletAdmin, 100); }); - }); - - it.skip('can handle 40.000 rewards', async () => { - await usingPlaygrounds(async (helper) => { + + itSub.skip('can handle 40.000 rewards', async ({helper}) => { const [donor] = await helper.arrange.createAccounts([7_000_000n], alice); const crowdStakes = async () => { // each account in the crowd stakes 2 times @@ -763,11 +686,11 @@ await Promise.all(crowd.map(account => helper.staking.stake(account, 100n * nominal))); // }; - + for (let i = 0; i < 40; i++) { await crowdStakes(); } - + // TODO pay rewards for some period }); }); --- a/tests/src/block-production.test.ts +++ b/tests/src/block-production.test.ts @@ -14,9 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -import usingApi from './substrate/substrate-api'; -import {expect} from 'chai'; import {ApiPromise} from '@polkadot/api'; +import {expect, itSub} from './util/playgrounds'; const BLOCK_TIME_MS = 12000; const TOLERANCE_MS = 3000; @@ -37,10 +36,8 @@ } describe('Block Production smoke test', () => { - it('Node produces new blocks', async () => { - await usingApi(async (api) => { - const blocks: number[] | undefined = await getBlocks(api); - expect(blocks[0]).to.be.lessThan(blocks[1]); - }); + itSub('Node produces new blocks', async ({helper}) => { + const blocks: number[] | undefined = await getBlocks(helper.api!); + expect(blocks[0]).to.be.lessThan(blocks[1]); }); }); --- a/tests/src/connection.test.ts +++ b/tests/src/connection.test.ts @@ -14,29 +14,19 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -import usingApi from './substrate/substrate-api'; -import {WsProvider} from '@polkadot/api'; -import * as chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; - -chai.use(chaiAsPromised); - -const expect = chai.expect; +import {itSub, expect, usingPlaygrounds} from './util/playgrounds'; describe('Connection smoke test', () => { - it('Connection can be established', async () => { - await usingApi(async api => { - const health = await api.rpc.system.health(); - expect(health).to.be.not.empty; - }); + itSub('Connection can be established', async ({helper}) => { + const health = (await helper.callRpc('api.rpc.system.health')).toJSON(); + expect(health).to.be.not.empty; }); it('Cannot connect to 255.255.255.255', async () => { - const neverConnectProvider = new WsProvider('ws://255.255.255.255:9944'); await expect((async () => { - await usingApi(async api => { - await api.rpc.system.health(); - }, {provider: neverConnectProvider}); + await usingPlaygrounds(async helper => { + await helper.callRpc('api.rpc.system.health'); + }, 'ws://255.255.255.255:9944'); })()).to.be.eventually.rejected; }); }); --- a/tests/src/contracts.test.ts +++ b/tests/src/contracts.test.ts @@ -47,6 +47,7 @@ const gasLimit = 9000n * 1000000n; const marketContractAddress = '5CYN9j3YvRkqxewoxeSvRbhAym4465C57uMmX5j4yz99L5H6'; +// todo:playgrounds skipped ~ postponed describe.skip('Contracts', () => { it('Can deploy smart contract Flipper, instantiate it and call it\'s get and flip messages.', async () => { await usingApi(async (api, privateKeyWrapper) => { --- a/tests/src/enableContractSponsoring.test.ts +++ b/tests/src/enableContractSponsoring.test.ts @@ -29,6 +29,7 @@ chai.use(chaiAsPromised); const expect = chai.expect; +// todo:playgrounds skipped ~ postponed describe.skip('Integration Test enableContractSponsoring', () => { it('ensure tx fee is paid from endowment', async () => { await usingApi(async (api, privateKeyWrapper) => { --- a/tests/src/eth/api/ContractHelpers.sol +++ b/tests/src/eth/api/ContractHelpers.sol @@ -15,15 +15,12 @@ /// @dev inlined interface interface ContractHelpersEvents { event ContractSponsorSet(address indexed contractAddress, address sponsor); - event ContractSponsorshipConfirmed( - address indexed contractAddress, - address sponsor - ); + event ContractSponsorshipConfirmed(address indexed contractAddress, address sponsor); event ContractSponsorRemoved(address indexed contractAddress); } /// @title Magic contract, which allows users to reconfigure other contracts -/// @dev the ERC-165 identifier for this interface is 0x172cb4fb +/// @dev the ERC-165 identifier for this interface is 0x30afad04 interface ContractHelpers is Dummy, ERC165, ContractHelpersEvents { /// Get user, which deployed specified contract /// @dev May return zero address in case if contract is deployed @@ -34,10 +31,7 @@ /// @return address Owner of contract /// @dev EVM selector for this function is: 0x5152b14c, /// or in textual repr: contractOwner(address) - function contractOwner(address contractAddress) - external - view - returns (address); + function contractOwner(address contractAddress) external view returns (address); /// Set sponsor. /// @param contractAddress Contract for which a sponsor is being established. @@ -73,12 +67,9 @@ /// /// @param contractAddress The contract for which a sponsor is requested. /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. - /// @dev EVM selector for this function is: 0x743fc745, - /// or in textual repr: getSponsor(address) - function getSponsor(address contractAddress) - external - view - returns (Tuple0 memory); + /// @dev EVM selector for this function is: 0x766c4f37, + /// or in textual repr: sponsor(address) + function sponsor(address contractAddress) external view returns (Tuple0 memory); /// Check tat contract has confirmed sponsor. /// @@ -94,17 +85,11 @@ /// @return **true** if contract has pending sponsor. /// @dev EVM selector for this function is: 0x39b9b242, /// or in textual repr: hasPendingSponsor(address) - function hasPendingSponsor(address contractAddress) - external - view - returns (bool); + function hasPendingSponsor(address contractAddress) external view returns (bool); /// @dev EVM selector for this function is: 0x6027dc61, /// or in textual repr: sponsoringEnabled(address) - function sponsoringEnabled(address contractAddress) - external - view - returns (bool); + function sponsoringEnabled(address contractAddress) external view returns (bool); /// @dev EVM selector for this function is: 0xfde8a560, /// or in textual repr: setSponsoringMode(address,uint8) @@ -113,12 +98,9 @@ /// Get current contract sponsoring rate limit /// @param contractAddress Contract to get sponsoring rate limit of /// @return uint32 Amount of blocks between two sponsored transactions - /// @dev EVM selector for this function is: 0x610cfabd, - /// or in textual repr: getSponsoringRateLimit(address) - function getSponsoringRateLimit(address contractAddress) - external - view - returns (uint32); + /// @dev EVM selector for this function is: 0xf29694d8, + /// or in textual repr: sponsoringRateLimit(address) + function sponsoringRateLimit(address contractAddress) external view returns (uint32); /// Set contract sponsoring rate limit /// @dev Sponsoring rate limit - is a minimum amount of blocks that should @@ -128,8 +110,7 @@ /// @dev Only contract owner can change this setting /// @dev EVM selector for this function is: 0x77b6c908, /// or in textual repr: setSponsoringRateLimit(address,uint32) - function setSponsoringRateLimit(address contractAddress, uint32 rateLimit) - external; + function setSponsoringRateLimit(address contractAddress, uint32 rateLimit) external; /// Set contract sponsoring fee limit /// @dev Sponsoring fee limit - is maximum fee that could be spent by @@ -139,19 +120,15 @@ /// @dev Only contract owner can change this setting /// @dev EVM selector for this function is: 0x03aed665, /// or in textual repr: setSponsoringFeeLimit(address,uint256) - function setSponsoringFeeLimit(address contractAddress, uint256 feeLimit) - external; + function setSponsoringFeeLimit(address contractAddress, uint256 feeLimit) external; /// Get current contract sponsoring fee limit /// @param contractAddress Contract to get sponsoring fee limit of /// @return uint256 Maximum amount of fee that could be spent by single /// transaction - /// @dev EVM selector for this function is: 0xc3fdc9ee, - /// or in textual repr: getSponsoringFeeLimit(address) - function getSponsoringFeeLimit(address contractAddress) - external - view - returns (uint256); + /// @dev EVM selector for this function is: 0x75b73606, + /// or in textual repr: sponsoringFeeLimit(address) + function sponsoringFeeLimit(address contractAddress) external view returns (uint256); /// Is specified user present in contract allow list /// @dev Contract owner always implicitly included @@ -160,10 +137,7 @@ /// @return bool Is specified users exists in contract allowlist /// @dev EVM selector for this function is: 0x5c658165, /// or in textual repr: allowed(address,address) - function allowed(address contractAddress, address user) - external - view - returns (bool); + function allowed(address contractAddress, address user) external view returns (bool); /// Toggle user presence in contract allowlist /// @param contractAddress Contract to change allowlist of @@ -187,10 +161,7 @@ /// @return bool Is specified contract has allowlist access enabled /// @dev EVM selector for this function is: 0xc772ef6c, /// or in textual repr: allowlistEnabled(address) - function allowlistEnabled(address contractAddress) - external - view - returns (bool); + function allowlistEnabled(address contractAddress) external view returns (bool); /// Toggle contract allowlist access /// @param contractAddress Contract to change allowlist access of --- a/tests/src/eth/api/UniqueFungible.sol +++ b/tests/src/eth/api/UniqueFungible.sol @@ -13,7 +13,7 @@ } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0x9f70d4e0 +/// @dev the ERC-165 identifier for this interface is 0x47dbc105 interface Collection is Dummy, ERC165 { /// Set collection property. /// @@ -21,8 +21,7 @@ /// @param value Propery value. /// @dev EVM selector for this function is: 0x2f073f66, /// or in textual repr: setCollectionProperty(string,bytes) - function setCollectionProperty(string memory key, bytes memory value) - external; + function setCollectionProperty(string memory key, bytes memory value) external; /// Delete collection property. /// @@ -39,10 +38,7 @@ /// @return bytes The property corresponding to the key. /// @dev EVM selector for this function is: 0xcf24fd6d, /// or in textual repr: collectionProperty(string) - function collectionProperty(string memory key) - external - view - returns (bytes memory); + function collectionProperty(string memory key) external view returns (bytes memory); /// Set the sponsor of the collection. /// @@ -62,6 +58,7 @@ /// or in textual repr: setCollectionSponsorSubstrate(uint256) function setCollectionSponsorSubstrate(uint256 sponsor) external; + /// Whether there is a pending sponsor. /// @dev EVM selector for this function is: 0x058ac185, /// or in textual repr: hasCollectionPendingSponsor() function hasCollectionPendingSponsor() external view returns (bool); @@ -81,9 +78,9 @@ /// Get current sponsor. /// /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. - /// @dev EVM selector for this function is: 0xb66bbc14, - /// or in textual repr: getCollectionSponsor() - function getCollectionSponsor() external view returns (Tuple6 memory); + /// @dev EVM selector for this function is: 0x6ec0a9f1, + /// or in textual repr: collectionSponsor() + function collectionSponsor() external view returns (Tuple6 memory); /// Set limits for the collection. /// @dev Throws error if limit not found. @@ -153,8 +150,7 @@ /// @param collections Addresses of collections that will be available for nesting. /// @dev EVM selector for this function is: 0x64872396, /// or in textual repr: setCollectionNesting(bool,address[]) - function setCollectionNesting(bool enable, address[] memory collections) - external; + function setCollectionNesting(bool enable, address[] memory collections) external; /// Set the collection access method. /// @param mode Access mode @@ -227,7 +223,7 @@ /// @return `Fungible` or `NFT` or `ReFungible` /// @dev EVM selector for this function is: 0xd34b55b8, /// or in textual repr: uniqueCollectionType() - function uniqueCollectionType() external returns (string memory); + function uniqueCollectionType() external view returns (string memory); /// Get collection owner. /// @@ -291,11 +287,7 @@ /// @dev inlined interface interface ERC20Events { event Transfer(address indexed from, address indexed to, uint256 value); - event Approval( - address indexed owner, - address indexed spender, - uint256 value - ); + event Approval(address indexed owner, address indexed spender, uint256 value); } /// @dev the ERC-165 identifier for this interface is 0x942e8b22 @@ -338,17 +330,7 @@ /// @dev EVM selector for this function is: 0xdd62ed3e, /// or in textual repr: allowance(address,address) - function allowance(address owner, address spender) - external - view - returns (uint256); + function allowance(address owner, address spender) external view returns (uint256); } -interface UniqueFungible is - Dummy, - ERC165, - ERC20, - ERC20Mintable, - ERC20UniqueExtensions, - Collection -{} +interface UniqueFungible is Dummy, ERC165, ERC20, ERC20Mintable, ERC20UniqueExtensions, Collection {} --- a/tests/src/eth/api/UniqueNFT.sol +++ b/tests/src/eth/api/UniqueNFT.sol @@ -58,14 +58,11 @@ /// @return Property value bytes /// @dev EVM selector for this function is: 0x7228c327, /// or in textual repr: property(uint256,string) - function property(uint256 tokenId, string memory key) - external - view - returns (bytes memory); + function property(uint256 tokenId, string memory key) external view returns (bytes memory); } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0x9f70d4e0 +/// @dev the ERC-165 identifier for this interface is 0x47dbc105 interface Collection is Dummy, ERC165 { /// Set collection property. /// @@ -73,8 +70,7 @@ /// @param value Propery value. /// @dev EVM selector for this function is: 0x2f073f66, /// or in textual repr: setCollectionProperty(string,bytes) - function setCollectionProperty(string memory key, bytes memory value) - external; + function setCollectionProperty(string memory key, bytes memory value) external; /// Delete collection property. /// @@ -91,10 +87,7 @@ /// @return bytes The property corresponding to the key. /// @dev EVM selector for this function is: 0xcf24fd6d, /// or in textual repr: collectionProperty(string) - function collectionProperty(string memory key) - external - view - returns (bytes memory); + function collectionProperty(string memory key) external view returns (bytes memory); /// Set the sponsor of the collection. /// @@ -114,6 +107,7 @@ /// or in textual repr: setCollectionSponsorSubstrate(uint256) function setCollectionSponsorSubstrate(uint256 sponsor) external; + /// Whether there is a pending sponsor. /// @dev EVM selector for this function is: 0x058ac185, /// or in textual repr: hasCollectionPendingSponsor() function hasCollectionPendingSponsor() external view returns (bool); @@ -133,9 +127,9 @@ /// Get current sponsor. /// /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. - /// @dev EVM selector for this function is: 0xb66bbc14, - /// or in textual repr: getCollectionSponsor() - function getCollectionSponsor() external view returns (Tuple17 memory); + /// @dev EVM selector for this function is: 0x6ec0a9f1, + /// or in textual repr: collectionSponsor() + function collectionSponsor() external view returns (Tuple17 memory); /// Set limits for the collection. /// @dev Throws error if limit not found. @@ -205,8 +199,7 @@ /// @param collections Addresses of collections that will be available for nesting. /// @dev EVM selector for this function is: 0x64872396, /// or in textual repr: setCollectionNesting(bool,address[]) - function setCollectionNesting(bool enable, address[] memory collections) - external; + function setCollectionNesting(bool enable, address[] memory collections) external; /// Set the collection access method. /// @param mode Access mode @@ -279,7 +272,7 @@ /// @return `Fungible` or `NFT` or `ReFungible` /// @dev EVM selector for this function is: 0xd34b55b8, /// or in textual repr: uniqueCollectionType() - function uniqueCollectionType() external returns (string memory); + function uniqueCollectionType() external view returns (string memory); /// Get collection owner. /// @@ -399,9 +392,7 @@ /// @param tokenIds IDs of the minted NFTs /// @dev EVM selector for this function is: 0x44a9945e, /// or in textual repr: mintBulk(address,uint256[]) - function mintBulk(address to, uint256[] memory tokenIds) - external - returns (bool); + function mintBulk(address to, uint256[] memory tokenIds) external returns (bool); /// @notice Function to mint multiple tokens with the given tokenUris. /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive @@ -410,9 +401,7 @@ /// @param tokens array of pairs of token ID and token URI for minted tokens /// @dev EVM selector for this function is: 0x36543006, /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[]) - function mintBulkWithTokenURI(address to, Tuple8[] memory tokens) - external - returns (bool); + function mintBulkWithTokenURI(address to, Tuple8[] memory tokens) external returns (bool); } /// @dev anonymous struct @@ -436,10 +425,7 @@ /// @dev Not implemented /// @dev EVM selector for this function is: 0x2f745c59, /// or in textual repr: tokenOfOwnerByIndex(address,uint256) - function tokenOfOwnerByIndex(address owner, uint256 index) - external - view - returns (uint256); + function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); /// @notice Count NFTs tracked by this contract /// @return A count of valid NFTs tracked by this contract, where each one of @@ -479,21 +465,9 @@ /// @dev inlined interface interface ERC721Events { - event Transfer( - address indexed from, - address indexed to, - uint256 indexed tokenId - ); - event Approval( - address indexed owner, - address indexed approved, - uint256 indexed tokenId - ); - event ApprovalForAll( - address indexed owner, - address indexed operator, - bool approved - ); + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); } /// @title ERC-721 Non-Fungible Token Standard @@ -577,10 +551,7 @@ /// @dev Not implemented /// @dev EVM selector for this function is: 0xe985e9c5, /// or in textual repr: isApprovedForAll(address,address) - function isApprovedForAll(address owner, address operator) - external - view - returns (address); + function isApprovedForAll(address owner, address operator) external view returns (address); } interface UniqueNFT is --- a/tests/src/eth/api/UniqueRefungible.sol +++ b/tests/src/eth/api/UniqueRefungible.sol @@ -58,14 +58,11 @@ /// @return Property value bytes /// @dev EVM selector for this function is: 0x7228c327, /// or in textual repr: property(uint256,string) - function property(uint256 tokenId, string memory key) - external - view - returns (bytes memory); + function property(uint256 tokenId, string memory key) external view returns (bytes memory); } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0x9f70d4e0 +/// @dev the ERC-165 identifier for this interface is 0x47dbc105 interface Collection is Dummy, ERC165 { /// Set collection property. /// @@ -73,8 +70,7 @@ /// @param value Propery value. /// @dev EVM selector for this function is: 0x2f073f66, /// or in textual repr: setCollectionProperty(string,bytes) - function setCollectionProperty(string memory key, bytes memory value) - external; + function setCollectionProperty(string memory key, bytes memory value) external; /// Delete collection property. /// @@ -91,10 +87,7 @@ /// @return bytes The property corresponding to the key. /// @dev EVM selector for this function is: 0xcf24fd6d, /// or in textual repr: collectionProperty(string) - function collectionProperty(string memory key) - external - view - returns (bytes memory); + function collectionProperty(string memory key) external view returns (bytes memory); /// Set the sponsor of the collection. /// @@ -114,6 +107,7 @@ /// or in textual repr: setCollectionSponsorSubstrate(uint256) function setCollectionSponsorSubstrate(uint256 sponsor) external; + /// Whether there is a pending sponsor. /// @dev EVM selector for this function is: 0x058ac185, /// or in textual repr: hasCollectionPendingSponsor() function hasCollectionPendingSponsor() external view returns (bool); @@ -133,9 +127,9 @@ /// Get current sponsor. /// /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. - /// @dev EVM selector for this function is: 0xb66bbc14, - /// or in textual repr: getCollectionSponsor() - function getCollectionSponsor() external view returns (Tuple17 memory); + /// @dev EVM selector for this function is: 0x6ec0a9f1, + /// or in textual repr: collectionSponsor() + function collectionSponsor() external view returns (Tuple17 memory); /// Set limits for the collection. /// @dev Throws error if limit not found. @@ -205,8 +199,7 @@ /// @param collections Addresses of collections that will be available for nesting. /// @dev EVM selector for this function is: 0x64872396, /// or in textual repr: setCollectionNesting(bool,address[]) - function setCollectionNesting(bool enable, address[] memory collections) - external; + function setCollectionNesting(bool enable, address[] memory collections) external; /// Set the collection access method. /// @param mode Access mode @@ -279,7 +272,7 @@ /// @return `Fungible` or `NFT` or `ReFungible` /// @dev EVM selector for this function is: 0xd34b55b8, /// or in textual repr: uniqueCollectionType() - function uniqueCollectionType() external returns (string memory); + function uniqueCollectionType() external view returns (string memory); /// Get collection owner. /// @@ -401,9 +394,7 @@ /// @param tokenIds IDs of the minted RFTs /// @dev EVM selector for this function is: 0x44a9945e, /// or in textual repr: mintBulk(address,uint256[]) - function mintBulk(address to, uint256[] memory tokenIds) - external - returns (bool); + function mintBulk(address to, uint256[] memory tokenIds) external returns (bool); /// @notice Function to mint multiple tokens with the given tokenUris. /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive @@ -412,19 +403,14 @@ /// @param tokens array of pairs of token ID and token URI for minted tokens /// @dev EVM selector for this function is: 0x36543006, /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[]) - function mintBulkWithTokenURI(address to, Tuple8[] memory tokens) - external - returns (bool); + function mintBulkWithTokenURI(address to, Tuple8[] memory tokens) external returns (bool); /// Returns EVM address for refungible token /// /// @param token ID of the token /// @dev EVM selector for this function is: 0xab76fac6, /// or in textual repr: tokenContractAddress(uint256) - function tokenContractAddress(uint256 token) - external - view - returns (address); + function tokenContractAddress(uint256 token) external view returns (address); } /// @dev anonymous struct @@ -448,10 +434,7 @@ /// Not implemented /// @dev EVM selector for this function is: 0x2f745c59, /// or in textual repr: tokenOfOwnerByIndex(address,uint256) - function tokenOfOwnerByIndex(address owner, uint256 index) - external - view - returns (uint256); + function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); /// @notice Count RFTs tracked by this contract /// @return A count of valid RFTs tracked by this contract, where each one of @@ -489,21 +472,9 @@ /// @dev inlined interface interface ERC721Events { - event Transfer( - address indexed from, - address indexed to, - uint256 indexed tokenId - ); - event Approval( - address indexed owner, - address indexed approved, - uint256 indexed tokenId - ); - event ApprovalForAll( - address indexed owner, - address indexed operator, - bool approved - ); + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); } /// @title ERC-721 Non-Fungible Token Standard @@ -585,10 +556,7 @@ /// @dev Not implemented /// @dev EVM selector for this function is: 0xe985e9c5, /// or in textual repr: isApprovedForAll(address,address) - function isApprovedForAll(address owner, address operator) - external - view - returns (address); + function isApprovedForAll(address owner, address operator) external view returns (address); } interface UniqueRefungible is --- a/tests/src/eth/api/UniqueRefungibleToken.sol +++ b/tests/src/eth/api/UniqueRefungibleToken.sol @@ -44,11 +44,7 @@ /// @dev inlined interface interface ERC20Events { event Transfer(address indexed from, address indexed to, uint256 value); - event Approval( - address indexed owner, - address indexed spender, - uint256 value - ); + event Approval(address indexed owner, address indexed spender, uint256 value); } /// @title Standard ERC20 token @@ -120,16 +116,7 @@ /// @return A uint256 specifying the amount of tokens still available for the spender. /// @dev EVM selector for this function is: 0xdd62ed3e, /// or in textual repr: allowance(address,address) - function allowance(address owner, address spender) - external - view - returns (uint256); + function allowance(address owner, address spender) external view returns (uint256); } -interface UniqueRefungibleToken is - Dummy, - ERC165, - ERC20, - ERC20UniqueExtensions, - ERC1633 -{} +interface UniqueRefungibleToken is Dummy, ERC165, ERC20, ERC20UniqueExtensions, ERC1633 {} --- a/tests/src/eth/collectionSponsoring.test.ts +++ b/tests/src/eth/collectionSponsoring.test.ts @@ -56,7 +56,7 @@ await submitTransactionAsync(sponsor, confirmTx); expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; - const sponsorTuple = await collectionEvm.methods.getCollectionSponsor().call({from: owner}); + const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner}); expect(bigIntToSub(api, BigInt(sponsorTuple[1]))).to.be.eq(sponsor.address); }); @@ -77,7 +77,7 @@ await collectionEvm.methods.removeCollectionSponsor().send({from: owner}); - const sponsorTuple = await collectionEvm.methods.getCollectionSponsor().call({from: owner}); + const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner}); expect(sponsorTuple.field_0).to.be.eq('0x0000000000000000000000000000000000000000'); }); --- a/tests/src/eth/contractSponsoring.test.ts +++ b/tests/src/eth/contractSponsoring.test.ts @@ -223,7 +223,7 @@ const helpers = contractHelpers(web3, owner); await helpers.methods.selfSponsoredEnable(flipper.options.address).send(); - const result = await helpers.methods.getSponsor(flipper.options.address).call(); + const result = await helpers.methods.sponsor(flipper.options.address).call(); expect(result[0]).to.be.eq(flipper.options.address); expect(result[1]).to.be.eq('0'); @@ -237,7 +237,7 @@ await helpers.methods.setSponsor(flipper.options.address, sponsor).send(); await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor}); - const result = await helpers.methods.getSponsor(flipper.options.address).call(); + const result = await helpers.methods.sponsor(flipper.options.address).call(); expect(result[0]).to.be.eq(sponsor); expect(result[1]).to.be.eq('0'); @@ -482,7 +482,7 @@ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const flipper = await deployFlipper(web3, owner); const helpers = contractHelpers(web3, owner); - expect(await helpers.methods.getSponsoringRateLimit(flipper.options.address).call()).to.be.equals('7200'); + expect(await helpers.methods.sponsoringRateLimit(flipper.options.address).call()).to.be.equals('7200'); }); }); @@ -551,7 +551,7 @@ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const flipper = await deployFlipper(web3, owner); const helpers = contractHelpers(web3, owner); - expect(await helpers.methods.getSponsoringFeeLimit(flipper.options.address).call()).to.be.equals('115792089237316195423570985008687907853269984665640564039457584007913129639935'); + expect(await helpers.methods.sponsoringFeeLimit(flipper.options.address).call()).to.be.equals('115792089237316195423570985008687907853269984665640564039457584007913129639935'); }); itWeb3('Set fee limit', async ({api, web3, privateKeyWrapper}) => { @@ -559,7 +559,7 @@ const flipper = await deployFlipper(web3, owner); const helpers = contractHelpers(web3, owner); await helpers.methods.setSponsoringFeeLimit(flipper.options.address, 100).send(); - expect(await helpers.methods.getSponsoringFeeLimit(flipper.options.address).call()).to.be.equals('100'); + expect(await helpers.methods.sponsoringFeeLimit(flipper.options.address).call()).to.be.equals('100'); }); itWeb3('Negative test - set fee limit by non-owner', async ({api, web3, privateKeyWrapper}) => { --- a/tests/src/eth/fungibleAbi.json +++ b/tests/src/eth/fungibleAbi.json @@ -159,6 +159,23 @@ }, { "inputs": [], + "name": "collectionSponsor", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "field_0", "type": "address" }, + { "internalType": "uint256", "name": "field_1", "type": "uint256" } + ], + "internalType": "struct Tuple6", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], "name": "confirmCollectionSponsorship", "outputs": [], "stateMutability": "nonpayable", @@ -183,23 +200,6 @@ "name": "deleteCollectionProperty", "outputs": [], "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "getCollectionSponsor", - "outputs": [ - { - "components": [ - { "internalType": "address", "name": "field_0", "type": "address" }, - { "internalType": "uint256", "name": "field_1", "type": "uint256" } - ], - "internalType": "struct Tuple6", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", "type": "function" }, { @@ -453,7 +453,7 @@ "inputs": [], "name": "uniqueCollectionType", "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" } ] --- a/tests/src/eth/nonFungibleAbi.json +++ b/tests/src/eth/nonFungibleAbi.json @@ -189,6 +189,23 @@ }, { "inputs": [], + "name": "collectionSponsor", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "field_0", "type": "address" }, + { "internalType": "uint256", "name": "field_1", "type": "uint256" } + ], + "internalType": "struct Tuple17", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], "name": "confirmCollectionSponsorship", "outputs": [], "stateMutability": "nonpayable", @@ -231,23 +248,6 @@ ], "name": "getApproved", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getCollectionSponsor", - "outputs": [ - { - "components": [ - { "internalType": "address", "name": "field_0", "type": "address" }, - { "internalType": "uint256", "name": "field_1", "type": "uint256" } - ], - "internalType": "struct Tuple17", - "name": "", - "type": "tuple" - } - ], "stateMutability": "view", "type": "function" }, @@ -651,7 +651,7 @@ "inputs": [], "name": "uniqueCollectionType", "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" } ] --- a/tests/src/eth/reFungibleAbi.json +++ b/tests/src/eth/reFungibleAbi.json @@ -189,6 +189,23 @@ }, { "inputs": [], + "name": "collectionSponsor", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "field_0", "type": "address" }, + { "internalType": "uint256", "name": "field_1", "type": "uint256" } + ], + "internalType": "struct Tuple17", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], "name": "confirmCollectionSponsorship", "outputs": [], "stateMutability": "nonpayable", @@ -231,23 +248,6 @@ ], "name": "getApproved", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getCollectionSponsor", - "outputs": [ - { - "components": [ - { "internalType": "address", "name": "field_0", "type": "address" }, - { "internalType": "uint256", "name": "field_1", "type": "uint256" } - ], - "internalType": "struct Tuple17", - "name": "", - "type": "tuple" - } - ], "stateMutability": "view", "type": "function" }, @@ -660,7 +660,7 @@ "inputs": [], "name": "uniqueCollectionType", "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" } ] --- a/tests/src/eth/util/contractHelpersAbi.json +++ b/tests/src/eth/util/contractHelpersAbi.json @@ -111,18 +111,8 @@ "type": "address" } ], - "name": "getSponsor", - "outputs": [ - { - "components": [ - { "internalType": "address", "name": "field_0", "type": "address" }, - { "internalType": "uint256", "name": "field_1", "type": "uint256" } - ], - "internalType": "struct Tuple0", - "name": "", - "type": "tuple" - } - ], + "name": "hasPendingSponsor", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "view", "type": "function" }, @@ -134,8 +124,8 @@ "type": "address" } ], - "name": "getSponsoringFeeLimit", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "hasSponsor", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "view", "type": "function" }, @@ -147,9 +137,9 @@ "type": "address" } ], - "name": "getSponsoringRateLimit", - "outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }], - "stateMutability": "view", + "name": "removeSponsor", + "outputs": [], + "stateMutability": "nonpayable", "type": "function" }, { @@ -160,9 +150,9 @@ "type": "address" } ], - "name": "hasPendingSponsor", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", + "name": "selfSponsoredEnable", + "outputs": [], + "stateMutability": "nonpayable", "type": "function" }, { @@ -171,11 +161,12 @@ "internalType": "address", "name": "contractAddress", "type": "address" - } + }, + { "internalType": "address", "name": "sponsor", "type": "address" } ], - "name": "hasSponsor", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", + "name": "setSponsor", + "outputs": [], + "stateMutability": "nonpayable", "type": "function" }, { @@ -184,9 +175,10 @@ "internalType": "address", "name": "contractAddress", "type": "address" - } + }, + { "internalType": "uint256", "name": "feeLimit", "type": "uint256" } ], - "name": "removeSponsor", + "name": "setSponsoringFeeLimit", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -197,9 +189,10 @@ "internalType": "address", "name": "contractAddress", "type": "address" - } + }, + { "internalType": "uint8", "name": "mode", "type": "uint8" } ], - "name": "selfSponsoredEnable", + "name": "setSponsoringMode", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -211,9 +204,9 @@ "name": "contractAddress", "type": "address" }, - { "internalType": "address", "name": "sponsor", "type": "address" } + { "internalType": "uint32", "name": "rateLimit", "type": "uint32" } ], - "name": "setSponsor", + "name": "setSponsoringRateLimit", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -224,12 +217,21 @@ "internalType": "address", "name": "contractAddress", "type": "address" - }, - { "internalType": "uint256", "name": "feeLimit", "type": "uint256" } + } + ], + "name": "sponsor", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "field_0", "type": "address" }, + { "internalType": "uint256", "name": "field_1", "type": "uint256" } + ], + "internalType": "struct Tuple0", + "name": "", + "type": "tuple" + } ], - "name": "setSponsoringFeeLimit", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { @@ -238,12 +240,11 @@ "internalType": "address", "name": "contractAddress", "type": "address" - }, - { "internalType": "uint8", "name": "mode", "type": "uint8" } + } ], - "name": "setSponsoringMode", - "outputs": [], - "stateMutability": "nonpayable", + "name": "sponsoringEnabled", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", "type": "function" }, { @@ -252,12 +253,11 @@ "internalType": "address", "name": "contractAddress", "type": "address" - }, - { "internalType": "uint32", "name": "rateLimit", "type": "uint32" } + } ], - "name": "setSponsoringRateLimit", - "outputs": [], - "stateMutability": "nonpayable", + "name": "sponsoringFeeLimit", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", "type": "function" }, { @@ -268,8 +268,8 @@ "type": "address" } ], - "name": "sponsoringEnabled", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "name": "sponsoringRateLimit", + "outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }], "stateMutability": "view", "type": "function" }, --- a/tests/src/eth/util/playgrounds/unique.dev.ts +++ b/tests/src/eth/util/playgrounds/unique.dev.ts @@ -198,8 +198,24 @@ } `); } -} - + + async deployFlipper(signer: string): Promise { + return await this.helper.ethContract.deployByCode(signer, 'Flipper', ` + // SPDX-License-Identifier: UNLICENSED + pragma solidity ^0.8.6; + + contract Flipper { + bool value = false; + function flip() public { + value = !value; + } + function getValue() public view returns (bool) { + return value; + } + } + `); + } +} class EthAddressGroup extends EthGroupBase { extractCollectionId(address: string): number { --- a/tests/src/evmCoder.test.ts +++ b/tests/src/evmCoder.test.ts @@ -14,13 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . +import {IKeyringPair} from '@polkadot/types/types'; import Web3 from 'web3'; -import {createEthAccountWithBalance, createNonfungibleCollection, GAS_ARGS, itWeb3} from './eth/util/helpers'; +import {itEth, expect, usingEthPlaygrounds} from './eth/util/playgrounds'; import * as solc from 'solc'; -import chai from 'chai'; -const expect = chai.expect; - async function compileTestContract(collectionAddress: string, contractAddress: string) { const input = { language: 'Solidity', @@ -79,22 +77,30 @@ }; } -async function deployTestContract(web3: Web3, owner: string, collectionAddress: string, contractAddress: string) { +async function deployTestContract(web3: Web3, owner: string, collectionAddress: string, contractAddress: string, gas: number) { const compiled = await compileTestContract(collectionAddress, contractAddress); const fractionalizerContract = new web3.eth.Contract(compiled.abi, undefined, { data: compiled.object, from: owner, - ...GAS_ARGS, + gas, }); return await fractionalizerContract.deploy({data: compiled.object}).send({from: owner}); } describe('Evm Coder tests', () => { - itWeb3('Call non-existing function', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const {collectionIdAddress} = await createNonfungibleCollection(api, web3, owner); - const contract = await deployTestContract(web3, owner, collectionIdAddress, '0x1bfed5D614b886b9Ab2eA4CBAc22A96B7EC29c9c'); - const testContract = await deployTestContract(web3, owner, collectionIdAddress, contract.options.address); + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = privateKey('//Alice'); + }); + }); + + itEth('Call non-existing function', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.eth.createNonfungibleCollection(owner, 'EVMCODER', '', 'TEST'); + const contract = await deployTestContract(helper.getWeb3(), owner, collection.collectionAddress, '0x1bfed5D614b886b9Ab2eA4CBAc22A96B7EC29c9c', helper.eth.DEFAULT_GAS); + const testContract = await deployTestContract(helper.getWeb3(), owner, collection.collectionAddress, contract.options.address, helper.eth.DEFAULT_GAS); { const result = await testContract.methods.test1().send(); expect(result.events.Result.returnValues).to.deep.equal({ --- a/tests/src/fungible.test.ts +++ b/tests/src/fungible.test.ts @@ -15,18 +15,18 @@ // along with Unique Network. If not, see . import {IKeyringPair} from '@polkadot/types/types'; -import {U128_MAX} from './util/helpers'; import {itSub, usingPlaygrounds, expect} from './util/playgrounds'; -// todo:playgrounds get rid of globals -let alice: IKeyringPair; -let bob: IKeyringPair; +const U128_MAX = (1n << 128n) - 1n; describe('integration test: Fungible functionality:', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + before(async () => { await usingPlaygrounds(async (helper, privateKey) => { - alice = privateKey('//Alice'); - bob = privateKey('//Bob'); + const donor = privateKey('//Alice'); + [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor); }); }); @@ -82,7 +82,7 @@ expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(60n); expect(await collection.getBalance(ethAcc)).to.be.equal(140n); - await expect(collection.transfer(alice, {Substrate: bob.address}, 350n)).to.eventually.be.rejected; + await expect(collection.transfer(alice, {Substrate: bob.address}, 350n)).to.eventually.be.rejectedWith(/common\.TokenValueTooLow/); }); itSub('Tokens multiple creation', async ({helper}) => { --- a/tests/src/inflation.test.ts +++ b/tests/src/inflation.test.ts @@ -14,42 +14,45 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import {default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync} from './substrate/substrate-api'; - -chai.use(chaiAsPromised); -const expect = chai.expect; +import {IKeyringPair} from '@polkadot/types/types'; +import {expect, itSub, usingPlaygrounds} from './util/playgrounds'; +// todo:playgrounds requires sudo, look into on the later stage describe('integration test: Inflation', () => { - it('First year inflation is 10%', async () => { - await usingApi(async (api, privateKeyWrapper) => { + let superuser: IKeyringPair; - // Make sure non-sudo can't start inflation - const tx = api.tx.inflation.startInflation(1); - const bob = privateKeyWrapper('//Bob'); - await expect(submitTransactionExpectFailAsync(bob, tx)).to.be.rejected; + before(async () => { + await usingPlaygrounds(async (_, privateKey) => { + superuser = privateKey('//Alice'); + }); + }); + + itSub('First year inflation is 10%', async ({helper}) => { + // Make sure non-sudo can't start inflation + const [bob] = await helper.arrange.createAccounts([10n], superuser); - // Start inflation on relay block 1 (Alice is sudo) - const alice = privateKeyWrapper('//Alice'); - const sudoTx = api.tx.sudo.sudo(tx as any); - await submitTransactionAsync(alice, sudoTx); + await expect(helper.executeExtrinsic(bob, 'api.tx.inflation.startInflation', [1])).to.be.rejectedWith(/BadOrigin/); - const blockInterval = (api.consts.inflation.inflationBlockInterval).toBigInt(); - const totalIssuanceStart = (await api.query.inflation.startingYearTotalIssuance()).toBigInt(); - const blockInflation = (await api.query.inflation.blockInflation()).toBigInt(); + // Make sure superuser can't start inflation without explicit sudo + await expect(helper.executeExtrinsic(superuser, 'api.tx.inflation.startInflation', [1])).to.be.rejectedWith(/BadOrigin/); - const YEAR = 5259600n; // 6-second block. Blocks in one year - // const YEAR = 2629800n; // 12-second block. Blocks in one year + // Start inflation on relay block 1 (Alice is sudo) + const tx = helper.constructApiCall('api.tx.inflation.startInflation', [1]); + await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [tx])).to.not.be.rejected; - const totalExpectedInflation = totalIssuanceStart / 10n; - const totalActualInflation = blockInflation * YEAR / blockInterval; + const blockInterval = (helper.api!.consts.inflation.inflationBlockInterval as any).toBigInt(); + const totalIssuanceStart = ((await helper.api!.query.inflation.startingYearTotalIssuance()) as any).toBigInt(); + const blockInflation = (await helper.api!.query.inflation.blockInflation() as any).toBigInt(); - const tolerance = 0.00001; // Relative difference per year between theoretical and actual inflation - const expectedInflation = totalExpectedInflation / totalActualInflation - 1n; + const YEAR = 5259600n; // 6-second block. Blocks in one year + // const YEAR = 2629800n; // 12-second block. Blocks in one year + + const totalExpectedInflation = totalIssuanceStart / 10n; + const totalActualInflation = blockInflation * YEAR / blockInterval; + + const tolerance = 0.00001; // Relative difference per year between theoretical and actual inflation + const expectedInflation = totalExpectedInflation / totalActualInflation - 1n; - expect(Math.abs(Number(expectedInflation))).to.be.lessThanOrEqual(tolerance); - }); + expect(Math.abs(Number(expectedInflation))).to.be.lessThanOrEqual(tolerance); }); - }); --- a/tests/src/interfaces/augment-api-consts.ts +++ b/tests/src/interfaces/augment-api-consts.ts @@ -9,7 +9,7 @@ import type { Option, u128, u16, u32, u64, u8 } from '@polkadot/types-codec'; import type { Codec } from '@polkadot/types-codec/types'; import type { Perbill, Permill } from '@polkadot/types/interfaces/runtime'; -import type { FrameSupportPalletId, FrameSupportWeightsRuntimeDbWeight, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, SpVersionRuntimeVersion } from '@polkadot/types/lookup'; +import type { FrameSupportPalletId, FrameSupportWeightsRuntimeDbWeight, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, SpVersionRuntimeVersion, XcmV1MultiLocation } from '@polkadot/types/lookup'; export type __AugmentedConst = AugmentedConst; @@ -25,7 +25,7 @@ **/ nominal: u128 & AugmentedConst; /** - * The app's pallet id, used for deriving its sovereign account ID. + * The app's pallet id, used for deriving its sovereign account address. **/ palletId: FrameSupportPalletId & AugmentedConst; /** @@ -155,6 +155,17 @@ **/ [key: string]: Codec; }; + tokens: { + maxLocks: u32 & AugmentedConst; + /** + * The maximum number of named reserves that can exist on an account. + **/ + maxReserves: u32 & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; transactionPayment: { /** * A fee mulitplier for `Operational` extrinsics to compute "virtual tip" to boost their @@ -232,5 +243,22 @@ **/ [key: string]: Codec; }; + xTokens: { + /** + * Base XCM weight. + * + * The actually weight for an XCM message is `T::BaseXcmWeight + + * T::Weigher::weight(&msg)`. + **/ + baseXcmWeight: u64 & AugmentedConst; + /** + * Self chain location. + **/ + selfLocation: XcmV1MultiLocation & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; } // AugmentedConsts } // declare module --- a/tests/src/interfaces/augment-api-errors.ts +++ b/tests/src/interfaces/augment-api-errors.ts @@ -303,6 +303,10 @@ **/ NoPermission: AugmentedError; /** + * Number of methods that sponsored limit is defined for exceeds maximum. + **/ + TooManyMethodsHaveSponsoredLimit: AugmentedError; + /** * Generic error **/ [key: string]: AugmentedError; @@ -321,6 +325,29 @@ **/ [key: string]: AugmentedError; }; + foreignAssets: { + /** + * AssetId exists + **/ + AssetIdExisted: AugmentedError; + /** + * AssetId not exists + **/ + AssetIdNotExists: AugmentedError; + /** + * The given location could not be used (e.g. because it cannot be expressed in the + * desired version of XCM). + **/ + BadLocation: AugmentedError; + /** + * MultiLocation existed + **/ + MultiLocationExisted: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; fungible: { /** * Fungible token does not support nesting. @@ -697,6 +724,41 @@ **/ [key: string]: AugmentedError; }; + tokens: { + /** + * Cannot convert Amount into Balance type + **/ + AmountIntoBalanceFailed: AugmentedError; + /** + * The balance is too low + **/ + BalanceTooLow: AugmentedError; + /** + * Beneficiary account must pre-exist + **/ + DeadAccount: AugmentedError; + /** + * Value too low to create account due to existential deposit + **/ + ExistentialDeposit: AugmentedError; + /** + * Transfer/payment would kill account + **/ + KeepAlive: AugmentedError; + /** + * Failed because liquidity restrictions due to locking + **/ + LiquidityRestrictions: AugmentedError; + /** + * Failed because the maximum locks was exceeded + **/ + MaxLocksExceeded: AugmentedError; + TooManyReserves: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; treasury: { /** * The spend origin is valid but the amount it is allowed to spend is lower than the @@ -802,5 +864,90 @@ **/ [key: string]: AugmentedError; }; + xTokens: { + /** + * Asset has no reserve location. + **/ + AssetHasNoReserve: AugmentedError; + /** + * The specified index does not exist in a MultiAssets struct. + **/ + AssetIndexNonExistent: AugmentedError; + /** + * The version of the `Versioned` value used is not able to be + * interpreted. + **/ + BadVersion: AugmentedError; + /** + * Could not re-anchor the assets to declare the fees for the + * destination chain. + **/ + CannotReanchor: AugmentedError; + /** + * The destination `MultiLocation` provided cannot be inverted. + **/ + DestinationNotInvertible: AugmentedError; + /** + * We tried sending distinct asset and fee but they have different + * reserve chains. + **/ + DistinctReserveForAssetAndFee: AugmentedError; + /** + * Fee is not enough. + **/ + FeeNotEnough: AugmentedError; + /** + * Could not get ancestry of asset reserve location. + **/ + InvalidAncestry: AugmentedError; + /** + * The MultiAsset is invalid. + **/ + InvalidAsset: AugmentedError; + /** + * Invalid transfer destination. + **/ + InvalidDest: AugmentedError; + /** + * MinXcmFee not registered for certain reserve location + **/ + MinXcmFeeNotDefined: AugmentedError; + /** + * Not cross-chain transfer. + **/ + NotCrossChainTransfer: AugmentedError; + /** + * Currency is not cross-chain transferable. + **/ + NotCrossChainTransferableCurrency: AugmentedError; + /** + * Not supported MultiLocation + **/ + NotSupportedMultiLocation: AugmentedError; + /** + * The number of assets to be sent is over the maximum. + **/ + TooManyAssetsBeingSent: AugmentedError; + /** + * The message's weight could not be determined. + **/ + UnweighableMessage: AugmentedError; + /** + * XCM execution failed. + **/ + XcmExecutionFailed: AugmentedError; + /** + * The transfering asset amount is zero. + **/ + ZeroAmount: AugmentedError; + /** + * The fee is zero. + **/ + ZeroFee: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; } // AugmentedErrors } // declare module --- a/tests/src/interfaces/augment-api-events.ts +++ b/tests/src/interfaces/augment-api-events.ts @@ -9,7 +9,7 @@ import type { Bytes, Null, Option, Result, U256, U8aFixed, bool, u128, u32, u64, u8 } from '@polkadot/types-codec'; import type { ITuple } from '@polkadot/types-codec/types'; import type { AccountId32, H160, H256 } from '@polkadot/types/interfaces/runtime'; -import type { EthereumLog, EvmCoreErrorExitReason, FrameSupportScheduleLookupError, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchInfo, OrmlVestingVestingSchedule, PalletEvmAccountBasicCrossAccountIdRepr, RmrkTraitsNftAccountIdOrCollectionNftTuple, SpRuntimeDispatchError, XcmV1MultiLocation, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation } from '@polkadot/types/lookup'; +import type { EthereumLog, EvmCoreErrorExitReason, FrameSupportScheduleLookupError, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchInfo, OrmlVestingVestingSchedule, PalletEvmAccountBasicCrossAccountIdRepr, PalletForeignAssetsAssetIds, PalletForeignAssetsModuleAssetMetadata, RmrkTraitsNftAccountIdOrCollectionNftTuple, SpRuntimeDispatchError, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetMultiAssets, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation } from '@polkadot/types/lookup'; export type __AugmentedEvent = AugmentedEvent; @@ -20,14 +20,14 @@ * The admin was set * * # Arguments - * * AccountId: ID of the admin + * * AccountId: account address of the admin **/ SetAdmin: AugmentedEvent; /** * Staking was performed * * # Arguments - * * AccountId: ID of the staker + * * AccountId: account of the staker * * Balance : staking amount **/ Stake: AugmentedEvent; @@ -35,7 +35,7 @@ * Staking recalculation was performed * * # Arguments - * * AccountId: ID of the staker. + * * AccountId: account of the staker. * * Balance : recalculation base * * Balance : total income **/ @@ -44,7 +44,7 @@ * Unstaking was performed * * # Arguments - * * AccountId: ID of the staker + * * AccountId: account of the staker * * Balance : unstaking amount **/ Unstake: AugmentedEvent; @@ -264,6 +264,28 @@ **/ [key: string]: AugmentedEvent; }; + foreignAssets: { + /** + * The asset registered. + **/ + AssetRegistered: AugmentedEvent; + /** + * The asset updated. + **/ + AssetUpdated: AugmentedEvent; + /** + * The foreign asset registered. + **/ + ForeignAssetRegistered: AugmentedEvent; + /** + * The foreign asset updated. + **/ + ForeignAssetUpdated: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; parachainSystem: { /** * Downward messages were processed using the given weight. @@ -525,6 +547,66 @@ **/ [key: string]: AugmentedEvent; }; + tokens: { + /** + * A balance was set by root. + **/ + BalanceSet: AugmentedEvent; + /** + * Deposited some balance into an account + **/ + Deposited: AugmentedEvent; + /** + * An account was removed whose balance was non-zero but below + * ExistentialDeposit, resulting in an outright loss. + **/ + DustLost: AugmentedEvent; + /** + * An account was created with some free balance. + **/ + Endowed: AugmentedEvent; + /** + * Some locked funds were unlocked + **/ + LockRemoved: AugmentedEvent; + /** + * Some funds are locked + **/ + LockSet: AugmentedEvent; + /** + * Some balance was reserved (moved from free to reserved). + **/ + Reserved: AugmentedEvent; + /** + * Some reserved balance was repatriated (moved from reserved to + * another account). + **/ + ReserveRepatriated: AugmentedEvent; + /** + * Some balances were slashed (e.g. due to mis-behavior) + **/ + Slashed: AugmentedEvent; + /** + * The total issuance of an currency has been set + **/ + TotalIssuanceSet: AugmentedEvent; + /** + * Transfer succeeded. + **/ + Transfer: AugmentedEvent; + /** + * Some balance was unreserved (moved from reserved to free). + **/ + Unreserved: AugmentedEvent; + /** + * Some balances were withdrawn (e.g. pay for transaction fee) + **/ + Withdrawn: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; transactionPayment: { /** * A transaction fee `actual_fee`, of which `tip` was added to the minimum inclusion fee, @@ -713,5 +795,15 @@ **/ [key: string]: AugmentedEvent; }; + xTokens: { + /** + * Transferred `MultiAsset` with fee. + **/ + TransferredMultiAssets: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; } // AugmentedEvents } // declare module --- a/tests/src/interfaces/augment-api-query.ts +++ b/tests/src/interfaces/augment-api-query.ts @@ -9,7 +9,7 @@ import type { BTreeMap, Bytes, Option, U256, U8aFixed, Vec, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec'; import type { AnyNumber, ITuple } from '@polkadot/types-codec/types'; import type { AccountId32, H160, H256 } from '@polkadot/types/interfaces/runtime'; -import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueQueueConfigData, EthereumBlock, EthereumLog, EthereumReceiptReceiptV3, EthereumTransactionTransactionV2, FpRpcTransactionStatus, FrameSupportWeightsPerDispatchClassU64, FrameSystemAccountInfo, FrameSystemEventRecord, FrameSystemLastRuntimeUpgradeInfo, FrameSystemPhase, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesReleases, PalletBalancesReserveData, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmContractHelpersSponsoringModeT, PalletNonfungibleItemData, PalletRefungibleItemData, PalletTransactionPaymentReleases, PalletTreasuryProposal, PalletUniqueSchedulerScheduledV3, PhantomTypeUpDataStructs, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, SpRuntimeDigest, SpTrieStorageProof, UpDataStructsCollection, UpDataStructsCollectionStats, UpDataStructsProperties, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild } from '@polkadot/types/lookup'; +import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueQueueConfigData, EthereumBlock, EthereumLog, EthereumReceiptReceiptV3, EthereumTransactionTransactionV2, FpRpcTransactionStatus, FrameSupportWeightsPerDispatchClassU64, FrameSystemAccountInfo, FrameSystemEventRecord, FrameSystemLastRuntimeUpgradeInfo, FrameSystemPhase, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensReserveData, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesReleases, PalletBalancesReserveData, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmContractHelpersSponsoringModeT, PalletForeignAssetsAssetIds, PalletForeignAssetsModuleAssetMetadata, PalletNonfungibleItemData, PalletRefungibleItemData, PalletTransactionPaymentReleases, PalletTreasuryProposal, PalletUniqueSchedulerScheduledV3, PhantomTypeUpDataStructs, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, SpRuntimeDigest, SpTrieStorageProof, UpDataStructsCollection, UpDataStructsCollectionStats, UpDataStructsProperties, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, XcmV1MultiLocation } from '@polkadot/types/lookup'; import type { Observable } from '@polkadot/types/types'; export type __AugmentedQuery = AugmentedQuery unknown>; @@ -18,21 +18,41 @@ declare module '@polkadot/api-base/types/storage' { interface AugmentedQueries { appPromotion: { + /** + * Stores the `admin` account. Some extrinsics can only be executed if they were signed by `admin`. + **/ admin: AugmentedQuery Observable>, []> & QueryableStorageEntry; /** * Stores a key for record for which the next revenue recalculation would be performed. * If `None`, then recalculation has not yet been performed or calculations have been completed for all stakers. **/ nextCalculatedRecord: AugmentedQuery Observable>>, []> & QueryableStorageEntry; + /** + * Stores amount of stakes for an `Account`. + * + * * **Key** - Staker account. + * * **Value** - Amount of stakes. + **/ pendingUnstake: AugmentedQuery Observable>>, [u32]> & QueryableStorageEntry; /** - * Amount of tokens staked by account in the blocknumber. + * Stores the amount of tokens staked by account in the blocknumber. + * + * * **Key1** - Staker account. + * * **Key2** - Relay block number when the stake was made. + * * **(Balance, BlockNumber)** - Balance of the stake. + * The number of the relay block in which we must perform the interest recalculation **/ staked: AugmentedQuery Observable>, [AccountId32, u32]> & QueryableStorageEntry; /** - * Amount of stakes for an Account + * Stores amount of stakes for an `Account`. + * + * * **Key** - Staker account. + * * **Value** - Amount of stakes. **/ stakesPerAccount: AugmentedQuery Observable, [AccountId32]> & QueryableStorageEntry; + /** + * Stores the total staked amount. + **/ totalStaked: AugmentedQuery Observable, []> & QueryableStorageEntry; /** * Generic query @@ -245,13 +265,6 @@ **/ owner: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; selfSponsoring: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; - /** - * Storage for last sponsored block. - * - * * **Key1** - contract address. - * * **Key2** - sponsored user address. - * * **Value** - last sponsored block number. - **/ sponsorBasket: AugmentedQuery Observable>, [H160, H160]> & QueryableStorageEntry; /** * Store for contract sponsorship state. @@ -261,6 +274,14 @@ **/ sponsoring: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; /** + * Storage for last sponsored block. + * + * * **Key1** - contract address. + * * **Key2** - sponsored user address. + * * **Value** - last sponsored block number. + **/ + sponsoringFeeLimit: AugmentedQuery Observable>, [H160]> & QueryableStorageEntry; + /** * Store for sponsoring mode. * * ### Usage @@ -289,6 +310,41 @@ **/ [key: string]: QueryableStorageEntry; }; + foreignAssets: { + /** + * The storages for assets to fungible collection binding + * + **/ + assetBinding: AugmentedQuery Observable>, [u32]> & QueryableStorageEntry; + /** + * The storages for AssetMetadatas. + * + * AssetMetadatas: map AssetIds => Option + **/ + assetMetadatas: AugmentedQuery Observable>, [PalletForeignAssetsAssetIds]> & QueryableStorageEntry; + /** + * The storages for MultiLocations. + * + * ForeignAssetLocations: map ForeignAssetId => Option + **/ + foreignAssetLocations: AugmentedQuery Observable>, [u32]> & QueryableStorageEntry; + /** + * The storages for CurrencyIds. + * + * LocationToCurrencyIds: map MultiLocation => Option + **/ + locationToCurrencyIds: AugmentedQuery Observable>, [XcmV1MultiLocation]> & QueryableStorageEntry; + /** + * Next available Foreign AssetId ID. + * + * NextForeignAssetId: ForeignAssetId + **/ + nextForeignAssetId: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; fungible: { /** * Storage for assets delegated to a limited extent to other users. @@ -744,6 +800,34 @@ **/ [key: string]: QueryableStorageEntry; }; + tokens: { + /** + * The balance of a token type under an account. + * + * NOTE: If the total is ever zero, decrease account ref account. + * + * NOTE: This is only used in the case that this module is used to store + * balances. + **/ + accounts: AugmentedQuery Observable, [AccountId32, PalletForeignAssetsAssetIds]> & QueryableStorageEntry; + /** + * Any liquidity locks of a token type under an account. + * NOTE: Should only be accessed when setting, changing and freeing a lock. + **/ + locks: AugmentedQuery Observable>, [AccountId32, PalletForeignAssetsAssetIds]> & QueryableStorageEntry; + /** + * Named reserves on some account balances. + **/ + reserves: AugmentedQuery Observable>, [AccountId32, PalletForeignAssetsAssetIds]> & QueryableStorageEntry; + /** + * The total issuance of a token type. + **/ + totalIssuance: AugmentedQuery Observable, [PalletForeignAssetsAssetIds]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; transactionPayment: { nextFeeMultiplier: AugmentedQuery Observable, []> & QueryableStorageEntry; storageVersion: AugmentedQuery Observable, []> & QueryableStorageEntry; --- a/tests/src/interfaces/augment-api-tx.ts +++ b/tests/src/interfaces/augment-api-tx.ts @@ -9,7 +9,7 @@ import type { Bytes, Compact, Option, U256, U8aFixed, Vec, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec'; import type { AnyNumber, IMethod, ITuple } from '@polkadot/types-codec/types'; import type { AccountId32, Call, H160, H256, MultiAddress, Perbill, Permill } from '@polkadot/types/interfaces/runtime'; -import type { CumulusPrimitivesParachainInherentParachainInherentData, EthereumTransactionTransactionV2, FrameSupportScheduleMaybeHashed, OrmlVestingVestingSchedule, PalletEvmAccountBasicCrossAccountIdRepr, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsPartEquippableList, RmrkTraitsPartPartType, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCreateCollectionData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, XcmV1MultiLocation, XcmV2WeightLimit, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup'; +import type { CumulusPrimitivesParachainInherentParachainInherentData, EthereumTransactionTransactionV2, FrameSupportScheduleMaybeHashed, OrmlVestingVestingSchedule, PalletEvmAccountBasicCrossAccountIdRepr, PalletForeignAssetsAssetIds, PalletForeignAssetsModuleAssetMetadata, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsPartEquippableList, RmrkTraitsPartPartType, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCreateCollectionData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, XcmV1MultiLocation, XcmV2WeightLimit, XcmVersionedMultiAsset, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup'; export type __AugmentedSubmittable = AugmentedSubmittable<() => unknown>; export type __SubmittableExtrinsic = SubmittableExtrinsic; @@ -18,13 +18,101 @@ declare module '@polkadot/api-base/types/submittable' { interface AugmentedSubmittables { appPromotion: { + /** + * Recalculates interest for the specified number of stakers. + * If all stakers are not recalculated, the next call of the extrinsic + * will continue the recalculation, from those stakers for whom this + * was not perform in last call. + * + * # Permissions + * + * * Pallet admin + * + * # Arguments + * + * * `stakers_number`: the number of stakers for which recalculation will be performed + **/ payoutStakers: AugmentedSubmittable<(stakersNumber: Option | null | Uint8Array | u8 | AnyNumber) => SubmittableExtrinsic, [Option]>; + /** + * Sets an address as the the admin. + * + * # Permissions + * + * * Sudo + * + * # Arguments + * + * * `admin`: account of the new admin. + **/ setAdminAddress: AugmentedSubmittable<(admin: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic, [PalletEvmAccountBasicCrossAccountIdRepr]>; + /** + * Sets the pallet to be the sponsor for the collection. + * + * # Permissions + * + * * Pallet admin + * + * # Arguments + * + * * `collection_id`: ID of the collection that will be sponsored by `pallet_id` + **/ sponsorCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * Sets the pallet to be the sponsor for the contract. + * + * # Permissions + * + * * Pallet admin + * + * # Arguments + * + * * `contract_id`: the contract address that will be sponsored by `pallet_id` + **/ sponsorContract: AugmentedSubmittable<(contractId: H160 | string | Uint8Array) => SubmittableExtrinsic, [H160]>; + /** + * Stakes the amount of native tokens. + * Sets `amount` to the locked state. + * The maximum number of stakes for a staker is 10. + * + * # Arguments + * + * * `amount`: in native tokens. + **/ stake: AugmentedSubmittable<(amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u128]>; + /** + * Removes the pallet as the sponsor for the collection. + * Returns [`NoPermission`][`Error::NoPermission`] + * if the pallet wasn't the sponsor. + * + * # Permissions + * + * * Pallet admin + * + * # Arguments + * + * * `collection_id`: ID of the collection that is sponsored by `pallet_id` + **/ stopSponsoringCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * Removes the pallet as the sponsor for the contract. + * Returns [`NoPermission`][`Error::NoPermission`] + * if the pallet wasn't the sponsor. + * + * # Permissions + * + * * Pallet admin + * + * # Arguments + * + * * `contract_id`: the contract address that is sponsored by `pallet_id` + **/ stopSponsoringContract: AugmentedSubmittable<(contractId: H160 | string | Uint8Array) => SubmittableExtrinsic, [H160]>; + /** + * Unstakes all stakes. + * Moves the sum of all stakes to the `reserved` state. + * After the end of `PendingInterval` this sum becomes completely + * free for further use. + **/ unstake: AugmentedSubmittable<() => SubmittableExtrinsic, []>; /** * Generic tx @@ -216,6 +304,14 @@ **/ [key: string]: SubmittableExtrinsicFunction; }; + foreignAssets: { + registerForeignAsset: AugmentedSubmittable<(owner: AccountId32 | string | Uint8Array, location: XcmVersionedMultiLocation | { V0: any } | { V1: any } | string | Uint8Array, metadata: PalletForeignAssetsModuleAssetMetadata | { name?: any; symbol?: any; decimals?: any; minimalBalance?: any } | string | Uint8Array) => SubmittableExtrinsic, [AccountId32, XcmVersionedMultiLocation, PalletForeignAssetsModuleAssetMetadata]>; + updateForeignAsset: AugmentedSubmittable<(foreignAssetId: u32 | AnyNumber | Uint8Array, location: XcmVersionedMultiLocation | { V0: any } | { V1: any } | string | Uint8Array, metadata: PalletForeignAssetsModuleAssetMetadata | { name?: any; symbol?: any; decimals?: any; minimalBalance?: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, XcmVersionedMultiLocation, PalletForeignAssetsModuleAssetMetadata]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; inflation: { /** * This method sets the inflation start date. Can be only called once. @@ -905,6 +1001,87 @@ **/ [key: string]: SubmittableExtrinsicFunction; }; + tokens: { + /** + * Exactly as `transfer`, except the origin must be root and the source + * account may be specified. + * + * The dispatch origin for this call must be _Root_. + * + * - `source`: The sender of the transfer. + * - `dest`: The recipient of the transfer. + * - `currency_id`: currency type. + * - `amount`: free balance amount to tranfer. + **/ + forceTransfer: AugmentedSubmittable<(source: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetIds | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, MultiAddress, PalletForeignAssetsAssetIds, Compact]>; + /** + * Set the balances of a given account. + * + * This will alter `FreeBalance` and `ReservedBalance` in storage. it + * will also decrease the total issuance of the system + * (`TotalIssuance`). If the new free or reserved balance is below the + * existential deposit, it will reap the `AccountInfo`. + * + * The dispatch origin for this call is `root`. + **/ + setBalance: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetIds | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, newFree: Compact | AnyNumber | Uint8Array, newReserved: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, PalletForeignAssetsAssetIds, Compact, Compact]>; + /** + * Transfer some liquid free balance to another account. + * + * `transfer` will set the `FreeBalance` of the sender and receiver. + * It will decrease the total issuance of the system by the + * `TransferFee`. If the sender's account is below the existential + * deposit as a result of the transfer, the account will be reaped. + * + * The dispatch origin for this call must be `Signed` by the + * transactor. + * + * - `dest`: The recipient of the transfer. + * - `currency_id`: currency type. + * - `amount`: free balance amount to tranfer. + **/ + transfer: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetIds | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, PalletForeignAssetsAssetIds, Compact]>; + /** + * Transfer all remaining balance to the given account. + * + * NOTE: This function only attempts to transfer _transferable_ + * balances. This means that any locked, reserved, or existential + * deposits (when `keep_alive` is `true`), will not be transferred by + * this function. To ensure that this function results in a killed + * account, you might need to prepare the account by removing any + * reference counters, storage deposits, etc... + * + * The dispatch origin for this call must be `Signed` by the + * transactor. + * + * - `dest`: The recipient of the transfer. + * - `currency_id`: currency type. + * - `keep_alive`: A boolean to determine if the `transfer_all` + * operation should send all of the funds the account has, causing + * the sender account to be killed (false), or transfer everything + * except at least the existential deposit, which will guarantee to + * keep the sender account alive (true). + **/ + transferAll: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetIds | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, keepAlive: bool | boolean | Uint8Array) => SubmittableExtrinsic, [MultiAddress, PalletForeignAssetsAssetIds, bool]>; + /** + * Same as the [`transfer`] call, but with a check that the transfer + * will not kill the origin account. + * + * 99% of the time you want [`transfer`] instead. + * + * The dispatch origin for this call must be `Signed` by the + * transactor. + * + * - `dest`: The recipient of the transfer. + * - `currency_id`: currency type. + * - `amount`: free balance amount to tranfer. + **/ + transferKeepAlive: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetIds | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, PalletForeignAssetsAssetIds, Compact]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; treasury: { /** * Approve a proposal. At a later time, the proposal will be allocated to the beneficiary @@ -1565,5 +1742,125 @@ **/ [key: string]: SubmittableExtrinsicFunction; }; + xTokens: { + /** + * Transfer native currencies. + * + * `dest_weight` is the weight for XCM execution on the dest chain, and + * it would be charged from the transferred assets. If set below + * requirements, the execution may fail and assets wouldn't be + * received. + * + * It's a no-op if any error on local XCM execution or message sending. + * Note sending assets out per se doesn't guarantee they would be + * received. Receiving depends on if the XCM message could be delivered + * by the network, and if the receiving chain would handle + * messages correctly. + **/ + transfer: AugmentedSubmittable<(currencyId: PalletForeignAssetsAssetIds | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: u128 | AnyNumber | Uint8Array, dest: XcmVersionedMultiLocation | { V0: any } | { V1: any } | string | Uint8Array, destWeight: u64 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [PalletForeignAssetsAssetIds, u128, XcmVersionedMultiLocation, u64]>; + /** + * Transfer `MultiAsset`. + * + * `dest_weight` is the weight for XCM execution on the dest chain, and + * it would be charged from the transferred assets. If set below + * requirements, the execution may fail and assets wouldn't be + * received. + * + * It's a no-op if any error on local XCM execution or message sending. + * Note sending assets out per se doesn't guarantee they would be + * received. Receiving depends on if the XCM message could be delivered + * by the network, and if the receiving chain would handle + * messages correctly. + **/ + transferMultiasset: AugmentedSubmittable<(asset: XcmVersionedMultiAsset | { V0: any } | { V1: any } | string | Uint8Array, dest: XcmVersionedMultiLocation | { V0: any } | { V1: any } | string | Uint8Array, destWeight: u64 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [XcmVersionedMultiAsset, XcmVersionedMultiLocation, u64]>; + /** + * Transfer several `MultiAsset` specifying the item to be used as fee + * + * `dest_weight` is the weight for XCM execution on the dest chain, and + * it would be charged from the transferred assets. If set below + * requirements, the execution may fail and assets wouldn't be + * received. + * + * `fee_item` is index of the MultiAssets that we want to use for + * payment + * + * It's a no-op if any error on local XCM execution or message sending. + * Note sending assets out per se doesn't guarantee they would be + * received. Receiving depends on if the XCM message could be delivered + * by the network, and if the receiving chain would handle + * messages correctly. + **/ + transferMultiassets: AugmentedSubmittable<(assets: XcmVersionedMultiAssets | { V0: any } | { V1: any } | string | Uint8Array, feeItem: u32 | AnyNumber | Uint8Array, dest: XcmVersionedMultiLocation | { V0: any } | { V1: any } | string | Uint8Array, destWeight: u64 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [XcmVersionedMultiAssets, u32, XcmVersionedMultiLocation, u64]>; + /** + * Transfer `MultiAsset` specifying the fee and amount as separate. + * + * `dest_weight` is the weight for XCM execution on the dest chain, and + * it would be charged from the transferred assets. If set below + * requirements, the execution may fail and assets wouldn't be + * received. + * + * `fee` is the multiasset to be spent to pay for execution in + * destination chain. Both fee and amount will be subtracted form the + * callers balance For now we only accept fee and asset having the same + * `MultiLocation` id. + * + * If `fee` is not high enough to cover for the execution costs in the + * destination chain, then the assets will be trapped in the + * destination chain + * + * It's a no-op if any error on local XCM execution or message sending. + * Note sending assets out per se doesn't guarantee they would be + * received. Receiving depends on if the XCM message could be delivered + * by the network, and if the receiving chain would handle + * messages correctly. + **/ + transferMultiassetWithFee: AugmentedSubmittable<(asset: XcmVersionedMultiAsset | { V0: any } | { V1: any } | string | Uint8Array, fee: XcmVersionedMultiAsset | { V0: any } | { V1: any } | string | Uint8Array, dest: XcmVersionedMultiLocation | { V0: any } | { V1: any } | string | Uint8Array, destWeight: u64 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [XcmVersionedMultiAsset, XcmVersionedMultiAsset, XcmVersionedMultiLocation, u64]>; + /** + * Transfer several currencies specifying the item to be used as fee + * + * `dest_weight` is the weight for XCM execution on the dest chain, and + * it would be charged from the transferred assets. If set below + * requirements, the execution may fail and assets wouldn't be + * received. + * + * `fee_item` is index of the currencies tuple that we want to use for + * payment + * + * It's a no-op if any error on local XCM execution or message sending. + * Note sending assets out per se doesn't guarantee they would be + * received. Receiving depends on if the XCM message could be delivered + * by the network, and if the receiving chain would handle + * messages correctly. + **/ + transferMulticurrencies: AugmentedSubmittable<(currencies: Vec> | ([PalletForeignAssetsAssetIds | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, u128 | AnyNumber | Uint8Array])[], feeItem: u32 | AnyNumber | Uint8Array, dest: XcmVersionedMultiLocation | { V0: any } | { V1: any } | string | Uint8Array, destWeight: u64 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Vec>, u32, XcmVersionedMultiLocation, u64]>; + /** + * Transfer native currencies specifying the fee and amount as + * separate. + * + * `dest_weight` is the weight for XCM execution on the dest chain, and + * it would be charged from the transferred assets. If set below + * requirements, the execution may fail and assets wouldn't be + * received. + * + * `fee` is the amount to be spent to pay for execution in destination + * chain. Both fee and amount will be subtracted form the callers + * balance. + * + * If `fee` is not high enough to cover for the execution costs in the + * destination chain, then the assets will be trapped in the + * destination chain + * + * It's a no-op if any error on local XCM execution or message sending. + * Note sending assets out per se doesn't guarantee they would be + * received. Receiving depends on if the XCM message could be delivered + * by the network, and if the receiving chain would handle + * messages correctly. + **/ + transferWithFee: AugmentedSubmittable<(currencyId: PalletForeignAssetsAssetIds | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: u128 | AnyNumber | Uint8Array, fee: u128 | AnyNumber | Uint8Array, dest: XcmVersionedMultiLocation | { V0: any } | { V1: any } | string | Uint8Array, destWeight: u64 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [PalletForeignAssetsAssetIds, u128, u128, XcmVersionedMultiLocation, u64]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; } // AugmentedSubmittables } // declare module --- a/tests/src/interfaces/augment-types.ts +++ b/tests/src/interfaces/augment-types.ts @@ -5,7 +5,7 @@ // this is required to allow for ambient/previous definitions import '@polkadot/types/types/registry'; -import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletAppPromotionCall, PalletAppPromotionError, PalletAppPromotionEvent, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletConfigurationCall, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersEvent, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUniqueSchedulerCall, PalletUniqueSchedulerError, PalletUniqueSchedulerEvent, PalletUniqueSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipStateAccountId32, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from './default'; +import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensModuleCall, OrmlTokensModuleError, OrmlTokensModuleEvent, OrmlTokensReserveData, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, OrmlXtokensModuleCall, OrmlXtokensModuleError, OrmlXtokensModuleEvent, PalletAppPromotionCall, PalletAppPromotionError, PalletAppPromotionEvent, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletConfigurationCall, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersEvent, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletForeignAssetsAssetIds, PalletForeignAssetsModuleAssetMetadata, PalletForeignAssetsModuleCall, PalletForeignAssetsModuleError, PalletForeignAssetsModuleEvent, PalletForeignAssetsNativeCurrency, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUniqueSchedulerCall, PalletUniqueSchedulerError, PalletUniqueSchedulerEvent, PalletUniqueSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipStateAccountId32, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAsset, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from './default'; import type { Data, StorageKey } from '@polkadot/types'; import type { BitVec, Bool, Bytes, F32, F64, I128, I16, I256, I32, I64, I8, Json, Null, OptionBool, Raw, Text, Type, U128, U16, U256, U32, U64, U8, USize, bool, f32, f64, i128, i16, i256, i32, i64, i8, u128, u16, u256, u32, u64, u8, usize } from '@polkadot/types-codec'; import type { AssetApproval, AssetApprovalKey, AssetBalance, AssetDestroyWitness, AssetDetails, AssetMetadata, TAssetBalance, TAssetDepositBalance } from '@polkadot/types/interfaces/assets'; @@ -790,10 +790,19 @@ OriginKindV0: OriginKindV0; OriginKindV1: OriginKindV1; OriginKindV2: OriginKindV2; + OrmlTokensAccountData: OrmlTokensAccountData; + OrmlTokensBalanceLock: OrmlTokensBalanceLock; + OrmlTokensModuleCall: OrmlTokensModuleCall; + OrmlTokensModuleError: OrmlTokensModuleError; + OrmlTokensModuleEvent: OrmlTokensModuleEvent; + OrmlTokensReserveData: OrmlTokensReserveData; OrmlVestingModuleCall: OrmlVestingModuleCall; OrmlVestingModuleError: OrmlVestingModuleError; OrmlVestingModuleEvent: OrmlVestingModuleEvent; OrmlVestingVestingSchedule: OrmlVestingVestingSchedule; + OrmlXtokensModuleCall: OrmlXtokensModuleCall; + OrmlXtokensModuleError: OrmlXtokensModuleError; + OrmlXtokensModuleEvent: OrmlXtokensModuleEvent; OutboundHrmpMessage: OutboundHrmpMessage; OutboundLaneData: OutboundLaneData; OutboundMessageFee: OutboundMessageFee; @@ -841,6 +850,12 @@ PalletEvmEvent: PalletEvmEvent; PalletEvmMigrationCall: PalletEvmMigrationCall; PalletEvmMigrationError: PalletEvmMigrationError; + PalletForeignAssetsAssetIds: PalletForeignAssetsAssetIds; + PalletForeignAssetsModuleAssetMetadata: PalletForeignAssetsModuleAssetMetadata; + PalletForeignAssetsModuleCall: PalletForeignAssetsModuleCall; + PalletForeignAssetsModuleError: PalletForeignAssetsModuleError; + PalletForeignAssetsModuleEvent: PalletForeignAssetsModuleEvent; + PalletForeignAssetsNativeCurrency: PalletForeignAssetsNativeCurrency; PalletFungibleError: PalletFungibleError; PalletId: PalletId; PalletInflationCall: PalletInflationCall; @@ -1422,6 +1437,7 @@ XcmV2WeightLimit: XcmV2WeightLimit; XcmV2Xcm: XcmV2Xcm; XcmVersion: XcmVersion; + XcmVersionedMultiAsset: XcmVersionedMultiAsset; XcmVersionedMultiAssets: XcmVersionedMultiAssets; XcmVersionedMultiLocation: XcmVersionedMultiLocation; XcmVersionedXcm: XcmVersionedXcm; --- a/tests/src/interfaces/default/types.ts +++ b/tests/src/interfaces/default/types.ts @@ -747,6 +747,163 @@ /** @name OpalRuntimeRuntime */ export interface OpalRuntimeRuntime extends Null {} +/** @name OrmlTokensAccountData */ +export interface OrmlTokensAccountData extends Struct { + readonly free: u128; + readonly reserved: u128; + readonly frozen: u128; +} + +/** @name OrmlTokensBalanceLock */ +export interface OrmlTokensBalanceLock extends Struct { + readonly id: U8aFixed; + readonly amount: u128; +} + +/** @name OrmlTokensModuleCall */ +export interface OrmlTokensModuleCall extends Enum { + readonly isTransfer: boolean; + readonly asTransfer: { + readonly dest: MultiAddress; + readonly currencyId: PalletForeignAssetsAssetIds; + readonly amount: Compact; + } & Struct; + readonly isTransferAll: boolean; + readonly asTransferAll: { + readonly dest: MultiAddress; + readonly currencyId: PalletForeignAssetsAssetIds; + readonly keepAlive: bool; + } & Struct; + readonly isTransferKeepAlive: boolean; + readonly asTransferKeepAlive: { + readonly dest: MultiAddress; + readonly currencyId: PalletForeignAssetsAssetIds; + readonly amount: Compact; + } & Struct; + readonly isForceTransfer: boolean; + readonly asForceTransfer: { + readonly source: MultiAddress; + readonly dest: MultiAddress; + readonly currencyId: PalletForeignAssetsAssetIds; + readonly amount: Compact; + } & Struct; + readonly isSetBalance: boolean; + readonly asSetBalance: { + readonly who: MultiAddress; + readonly currencyId: PalletForeignAssetsAssetIds; + readonly newFree: Compact; + readonly newReserved: Compact; + } & Struct; + readonly type: 'Transfer' | 'TransferAll' | 'TransferKeepAlive' | 'ForceTransfer' | 'SetBalance'; +} + +/** @name OrmlTokensModuleError */ +export interface OrmlTokensModuleError extends Enum { + readonly isBalanceTooLow: boolean; + readonly isAmountIntoBalanceFailed: boolean; + readonly isLiquidityRestrictions: boolean; + readonly isMaxLocksExceeded: boolean; + readonly isKeepAlive: boolean; + readonly isExistentialDeposit: boolean; + readonly isDeadAccount: boolean; + readonly isTooManyReserves: boolean; + readonly type: 'BalanceTooLow' | 'AmountIntoBalanceFailed' | 'LiquidityRestrictions' | 'MaxLocksExceeded' | 'KeepAlive' | 'ExistentialDeposit' | 'DeadAccount' | 'TooManyReserves'; +} + +/** @name OrmlTokensModuleEvent */ +export interface OrmlTokensModuleEvent extends Enum { + readonly isEndowed: boolean; + readonly asEndowed: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isDustLost: boolean; + readonly asDustLost: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isTransfer: boolean; + readonly asTransfer: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly from: AccountId32; + readonly to: AccountId32; + readonly amount: u128; + } & Struct; + readonly isReserved: boolean; + readonly asReserved: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isUnreserved: boolean; + readonly asUnreserved: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isReserveRepatriated: boolean; + readonly asReserveRepatriated: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly from: AccountId32; + readonly to: AccountId32; + readonly amount: u128; + readonly status: FrameSupportTokensMiscBalanceStatus; + } & Struct; + readonly isBalanceSet: boolean; + readonly asBalanceSet: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly who: AccountId32; + readonly free: u128; + readonly reserved: u128; + } & Struct; + readonly isTotalIssuanceSet: boolean; + readonly asTotalIssuanceSet: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly amount: u128; + } & Struct; + readonly isWithdrawn: boolean; + readonly asWithdrawn: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isSlashed: boolean; + readonly asSlashed: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly who: AccountId32; + readonly freeAmount: u128; + readonly reservedAmount: u128; + } & Struct; + readonly isDeposited: boolean; + readonly asDeposited: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isLockSet: boolean; + readonly asLockSet: { + readonly lockId: U8aFixed; + readonly currencyId: PalletForeignAssetsAssetIds; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isLockRemoved: boolean; + readonly asLockRemoved: { + readonly lockId: U8aFixed; + readonly currencyId: PalletForeignAssetsAssetIds; + readonly who: AccountId32; + } & Struct; + readonly type: 'Endowed' | 'DustLost' | 'Transfer' | 'Reserved' | 'Unreserved' | 'ReserveRepatriated' | 'BalanceSet' | 'TotalIssuanceSet' | 'Withdrawn' | 'Slashed' | 'Deposited' | 'LockSet' | 'LockRemoved'; +} + +/** @name OrmlTokensReserveData */ +export interface OrmlTokensReserveData extends Struct { + readonly id: Null; + readonly amount: u128; +} + /** @name OrmlVestingModuleCall */ export interface OrmlVestingModuleCall extends Enum { readonly isClaim: boolean; @@ -806,6 +963,89 @@ readonly perPeriod: Compact; } +/** @name OrmlXtokensModuleCall */ +export interface OrmlXtokensModuleCall extends Enum { + readonly isTransfer: boolean; + readonly asTransfer: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly amount: u128; + readonly dest: XcmVersionedMultiLocation; + readonly destWeight: u64; + } & Struct; + readonly isTransferMultiasset: boolean; + readonly asTransferMultiasset: { + readonly asset: XcmVersionedMultiAsset; + readonly dest: XcmVersionedMultiLocation; + readonly destWeight: u64; + } & Struct; + readonly isTransferWithFee: boolean; + readonly asTransferWithFee: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly amount: u128; + readonly fee: u128; + readonly dest: XcmVersionedMultiLocation; + readonly destWeight: u64; + } & Struct; + readonly isTransferMultiassetWithFee: boolean; + readonly asTransferMultiassetWithFee: { + readonly asset: XcmVersionedMultiAsset; + readonly fee: XcmVersionedMultiAsset; + readonly dest: XcmVersionedMultiLocation; + readonly destWeight: u64; + } & Struct; + readonly isTransferMulticurrencies: boolean; + readonly asTransferMulticurrencies: { + readonly currencies: Vec>; + readonly feeItem: u32; + readonly dest: XcmVersionedMultiLocation; + readonly destWeight: u64; + } & Struct; + readonly isTransferMultiassets: boolean; + readonly asTransferMultiassets: { + readonly assets: XcmVersionedMultiAssets; + readonly feeItem: u32; + readonly dest: XcmVersionedMultiLocation; + readonly destWeight: u64; + } & Struct; + readonly type: 'Transfer' | 'TransferMultiasset' | 'TransferWithFee' | 'TransferMultiassetWithFee' | 'TransferMulticurrencies' | 'TransferMultiassets'; +} + +/** @name OrmlXtokensModuleError */ +export interface OrmlXtokensModuleError extends Enum { + readonly isAssetHasNoReserve: boolean; + readonly isNotCrossChainTransfer: boolean; + readonly isInvalidDest: boolean; + readonly isNotCrossChainTransferableCurrency: boolean; + readonly isUnweighableMessage: boolean; + readonly isXcmExecutionFailed: boolean; + readonly isCannotReanchor: boolean; + readonly isInvalidAncestry: boolean; + readonly isInvalidAsset: boolean; + readonly isDestinationNotInvertible: boolean; + readonly isBadVersion: boolean; + readonly isDistinctReserveForAssetAndFee: boolean; + readonly isZeroFee: boolean; + readonly isZeroAmount: boolean; + readonly isTooManyAssetsBeingSent: boolean; + readonly isAssetIndexNonExistent: boolean; + readonly isFeeNotEnough: boolean; + readonly isNotSupportedMultiLocation: boolean; + readonly isMinXcmFeeNotDefined: boolean; + readonly type: 'AssetHasNoReserve' | 'NotCrossChainTransfer' | 'InvalidDest' | 'NotCrossChainTransferableCurrency' | 'UnweighableMessage' | 'XcmExecutionFailed' | 'CannotReanchor' | 'InvalidAncestry' | 'InvalidAsset' | 'DestinationNotInvertible' | 'BadVersion' | 'DistinctReserveForAssetAndFee' | 'ZeroFee' | 'ZeroAmount' | 'TooManyAssetsBeingSent' | 'AssetIndexNonExistent' | 'FeeNotEnough' | 'NotSupportedMultiLocation' | 'MinXcmFeeNotDefined'; +} + +/** @name OrmlXtokensModuleEvent */ +export interface OrmlXtokensModuleEvent extends Enum { + readonly isTransferredMultiAssets: boolean; + readonly asTransferredMultiAssets: { + readonly sender: AccountId32; + readonly assets: XcmV1MultiassetMultiAssets; + readonly fee: XcmV1MultiAsset; + readonly dest: XcmV1MultiLocation; + } & Struct; + readonly type: 'TransferredMultiAssets'; +} + /** @name PalletAppPromotionCall */ export interface PalletAppPromotionCall extends Enum { readonly isSetAdminAddress: boolean; @@ -1186,7 +1426,8 @@ export interface PalletEvmContractHelpersError extends Enum { readonly isNoPermission: boolean; readonly isNoPendingSponsor: boolean; - readonly type: 'NoPermission' | 'NoPendingSponsor'; + readonly isTooManyMethodsHaveSponsoredLimit: boolean; + readonly type: 'NoPermission' | 'NoPendingSponsor' | 'TooManyMethodsHaveSponsoredLimit'; } /** @name PalletEvmContractHelpersEvent */ @@ -1264,6 +1505,83 @@ readonly type: 'AccountNotEmpty' | 'AccountIsNotMigrating'; } +/** @name PalletForeignAssetsAssetIds */ +export interface PalletForeignAssetsAssetIds extends Enum { + readonly isForeignAssetId: boolean; + readonly asForeignAssetId: u32; + readonly isNativeAssetId: boolean; + readonly asNativeAssetId: PalletForeignAssetsNativeCurrency; + readonly type: 'ForeignAssetId' | 'NativeAssetId'; +} + +/** @name PalletForeignAssetsModuleAssetMetadata */ +export interface PalletForeignAssetsModuleAssetMetadata extends Struct { + readonly name: Bytes; + readonly symbol: Bytes; + readonly decimals: u8; + readonly minimalBalance: u128; +} + +/** @name PalletForeignAssetsModuleCall */ +export interface PalletForeignAssetsModuleCall extends Enum { + readonly isRegisterForeignAsset: boolean; + readonly asRegisterForeignAsset: { + readonly owner: AccountId32; + readonly location: XcmVersionedMultiLocation; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly isUpdateForeignAsset: boolean; + readonly asUpdateForeignAsset: { + readonly foreignAssetId: u32; + readonly location: XcmVersionedMultiLocation; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly type: 'RegisterForeignAsset' | 'UpdateForeignAsset'; +} + +/** @name PalletForeignAssetsModuleError */ +export interface PalletForeignAssetsModuleError extends Enum { + readonly isBadLocation: boolean; + readonly isMultiLocationExisted: boolean; + readonly isAssetIdNotExists: boolean; + readonly isAssetIdExisted: boolean; + readonly type: 'BadLocation' | 'MultiLocationExisted' | 'AssetIdNotExists' | 'AssetIdExisted'; +} + +/** @name PalletForeignAssetsModuleEvent */ +export interface PalletForeignAssetsModuleEvent extends Enum { + readonly isForeignAssetRegistered: boolean; + readonly asForeignAssetRegistered: { + readonly assetId: u32; + readonly assetAddress: XcmV1MultiLocation; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly isForeignAssetUpdated: boolean; + readonly asForeignAssetUpdated: { + readonly assetId: u32; + readonly assetAddress: XcmV1MultiLocation; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly isAssetRegistered: boolean; + readonly asAssetRegistered: { + readonly assetId: PalletForeignAssetsAssetIds; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly isAssetUpdated: boolean; + readonly asAssetUpdated: { + readonly assetId: PalletForeignAssetsAssetIds; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly type: 'ForeignAssetRegistered' | 'ForeignAssetUpdated' | 'AssetRegistered' | 'AssetUpdated'; +} + +/** @name PalletForeignAssetsNativeCurrency */ +export interface PalletForeignAssetsNativeCurrency extends Enum { + readonly isHere: boolean; + readonly isParent: boolean; + readonly type: 'Here' | 'Parent'; +} + /** @name PalletFungibleError */ export interface PalletFungibleError extends Enum { readonly isNotFungibleDataUsedToMintFungibleCollectionToken: boolean; @@ -2492,7 +2810,7 @@ readonly sponsorship: UpDataStructsSponsorshipStateAccountId32; readonly limits: UpDataStructsCollectionLimits; readonly permissions: UpDataStructsCollectionPermissions; - readonly externalCollection: bool; + readonly flags: U8aFixed; } /** @name UpDataStructsCollectionLimits */ @@ -2666,6 +2984,7 @@ readonly tokenPropertyPermissions: Vec; readonly properties: Vec; readonly readOnly: bool; + readonly foreign: bool; } /** @name UpDataStructsSponsoringRateLimit */ @@ -3441,6 +3760,15 @@ /** @name XcmV2Xcm */ export interface XcmV2Xcm extends Vec {} +/** @name XcmVersionedMultiAsset */ +export interface XcmVersionedMultiAsset extends Enum { + readonly isV0: boolean; + readonly asV0: XcmV0MultiAsset; + readonly isV1: boolean; + readonly asV1: XcmV1MultiAsset; + readonly type: 'V0' | 'V1'; +} + /** @name XcmVersionedMultiAssets */ export interface XcmVersionedMultiAssets extends Enum { readonly isV0: boolean; --- a/tests/src/interfaces/lookup.ts +++ b/tests/src/interfaces/lookup.ts @@ -323,118 +323,47 @@ perPeriod: 'Compact' }, /** - * Lookup39: cumulus_pallet_xcmp_queue::pallet::Event + * Lookup39: orml_xtokens::module::Event **/ - CumulusPalletXcmpQueueEvent: { + OrmlXtokensModuleEvent: { _enum: { - Success: { - messageHash: 'Option', - weight: 'u64', - }, - Fail: { - messageHash: 'Option', - error: 'XcmV2TraitsError', - weight: 'u64', - }, - BadVersion: { - messageHash: 'Option', - }, - BadFormat: { - messageHash: 'Option', - }, - UpwardMessageSent: { - messageHash: 'Option', - }, - XcmpMessageSent: { - messageHash: 'Option', - }, - OverweightEnqueued: { - sender: 'u32', - sentAt: 'u32', - index: 'u64', - required: 'u64', - }, - OverweightServiced: { - index: 'u64', - used: 'u64' + TransferredMultiAssets: { + sender: 'AccountId32', + assets: 'XcmV1MultiassetMultiAssets', + fee: 'XcmV1MultiAsset', + dest: 'XcmV1MultiLocation' } } }, /** - * Lookup41: xcm::v2::traits::Error + * Lookup40: xcm::v1::multiasset::MultiAssets **/ - XcmV2TraitsError: { - _enum: { - Overflow: 'Null', - Unimplemented: 'Null', - UntrustedReserveLocation: 'Null', - UntrustedTeleportLocation: 'Null', - MultiLocationFull: 'Null', - MultiLocationNotInvertible: 'Null', - BadOrigin: 'Null', - InvalidLocation: 'Null', - AssetNotFound: 'Null', - FailedToTransactAsset: 'Null', - NotWithdrawable: 'Null', - LocationCannotHold: 'Null', - ExceedsMaxMessageSize: 'Null', - DestinationUnsupported: 'Null', - Transport: 'Null', - Unroutable: 'Null', - UnknownClaim: 'Null', - FailedToDecode: 'Null', - MaxWeightInvalid: 'Null', - NotHoldingFees: 'Null', - TooExpensive: 'Null', - Trap: 'u64', - UnhandledXcmVersion: 'Null', - WeightLimitReached: 'u64', - Barrier: 'Null', - WeightNotComputable: 'Null' - } - }, + XcmV1MultiassetMultiAssets: 'Vec', /** - * Lookup43: pallet_xcm::pallet::Event + * Lookup42: xcm::v1::multiasset::MultiAsset **/ - PalletXcmEvent: { - _enum: { - Attempted: 'XcmV2TraitsOutcome', - Sent: '(XcmV1MultiLocation,XcmV1MultiLocation,XcmV2Xcm)', - UnexpectedResponse: '(XcmV1MultiLocation,u64)', - ResponseReady: '(u64,XcmV2Response)', - Notified: '(u64,u8,u8)', - NotifyOverweight: '(u64,u8,u8,u64,u64)', - NotifyDispatchError: '(u64,u8,u8)', - NotifyDecodeFailed: '(u64,u8,u8)', - InvalidResponder: '(XcmV1MultiLocation,u64,Option)', - InvalidResponderVersion: '(XcmV1MultiLocation,u64)', - ResponseTaken: 'u64', - AssetsTrapped: '(H256,XcmV1MultiLocation,XcmVersionedMultiAssets)', - VersionChangeNotified: '(XcmV1MultiLocation,u32)', - SupportedVersionChanged: '(XcmV1MultiLocation,u32)', - NotifyTargetSendFail: '(XcmV1MultiLocation,u64,XcmV2TraitsError)', - NotifyTargetMigrationFail: '(XcmVersionedMultiLocation,u64)' - } + XcmV1MultiAsset: { + id: 'XcmV1MultiassetAssetId', + fun: 'XcmV1MultiassetFungibility' }, /** - * Lookup44: xcm::v2::traits::Outcome + * Lookup43: xcm::v1::multiasset::AssetId **/ - XcmV2TraitsOutcome: { + XcmV1MultiassetAssetId: { _enum: { - Complete: 'u64', - Incomplete: '(u64,XcmV2TraitsError)', - Error: 'XcmV2TraitsError' + Concrete: 'XcmV1MultiLocation', + Abstract: 'Bytes' } }, /** - * Lookup45: xcm::v1::multilocation::MultiLocation + * Lookup44: xcm::v1::multilocation::MultiLocation **/ XcmV1MultiLocation: { parents: 'u8', interior: 'XcmV1MultilocationJunctions' }, /** - * Lookup46: xcm::v1::multilocation::Junctions + * Lookup45: xcm::v1::multilocation::Junctions **/ XcmV1MultilocationJunctions: { _enum: { @@ -450,7 +379,7 @@ } }, /** - * Lookup47: xcm::v1::junction::Junction + * Lookup46: xcm::v1::junction::Junction **/ XcmV1Junction: { _enum: { @@ -478,7 +407,7 @@ } }, /** - * Lookup49: xcm::v0::junction::NetworkId + * Lookup48: xcm::v0::junction::NetworkId **/ XcmV0JunctionNetworkId: { _enum: { @@ -489,7 +418,7 @@ } }, /** - * Lookup53: xcm::v0::junction::BodyId + * Lookup52: xcm::v0::junction::BodyId **/ XcmV0JunctionBodyId: { _enum: { @@ -503,7 +432,7 @@ } }, /** - * Lookup54: xcm::v0::junction::BodyPart + * Lookup53: xcm::v0::junction::BodyPart **/ XcmV0JunctionBodyPart: { _enum: { @@ -526,11 +455,230 @@ } }, /** - * Lookup55: xcm::v2::Xcm + * Lookup54: xcm::v1::multiasset::Fungibility + **/ + XcmV1MultiassetFungibility: { + _enum: { + Fungible: 'Compact', + NonFungible: 'XcmV1MultiassetAssetInstance' + } + }, + /** + * Lookup55: xcm::v1::multiasset::AssetInstance + **/ + XcmV1MultiassetAssetInstance: { + _enum: { + Undefined: 'Null', + Index: 'Compact', + Array4: '[u8;4]', + Array8: '[u8;8]', + Array16: '[u8;16]', + Array32: '[u8;32]', + Blob: 'Bytes' + } + }, + /** + * Lookup58: orml_tokens::module::Event + **/ + OrmlTokensModuleEvent: { + _enum: { + Endowed: { + currencyId: 'PalletForeignAssetsAssetIds', + who: 'AccountId32', + amount: 'u128', + }, + DustLost: { + currencyId: 'PalletForeignAssetsAssetIds', + who: 'AccountId32', + amount: 'u128', + }, + Transfer: { + currencyId: 'PalletForeignAssetsAssetIds', + from: 'AccountId32', + to: 'AccountId32', + amount: 'u128', + }, + Reserved: { + currencyId: 'PalletForeignAssetsAssetIds', + who: 'AccountId32', + amount: 'u128', + }, + Unreserved: { + currencyId: 'PalletForeignAssetsAssetIds', + who: 'AccountId32', + amount: 'u128', + }, + ReserveRepatriated: { + currencyId: 'PalletForeignAssetsAssetIds', + from: 'AccountId32', + to: 'AccountId32', + amount: 'u128', + status: 'FrameSupportTokensMiscBalanceStatus', + }, + BalanceSet: { + currencyId: 'PalletForeignAssetsAssetIds', + who: 'AccountId32', + free: 'u128', + reserved: 'u128', + }, + TotalIssuanceSet: { + currencyId: 'PalletForeignAssetsAssetIds', + amount: 'u128', + }, + Withdrawn: { + currencyId: 'PalletForeignAssetsAssetIds', + who: 'AccountId32', + amount: 'u128', + }, + Slashed: { + currencyId: 'PalletForeignAssetsAssetIds', + who: 'AccountId32', + freeAmount: 'u128', + reservedAmount: 'u128', + }, + Deposited: { + currencyId: 'PalletForeignAssetsAssetIds', + who: 'AccountId32', + amount: 'u128', + }, + LockSet: { + lockId: '[u8;8]', + currencyId: 'PalletForeignAssetsAssetIds', + who: 'AccountId32', + amount: 'u128', + }, + LockRemoved: { + lockId: '[u8;8]', + currencyId: 'PalletForeignAssetsAssetIds', + who: 'AccountId32' + } + } + }, + /** + * Lookup59: pallet_foreign_assets::AssetIds + **/ + PalletForeignAssetsAssetIds: { + _enum: { + ForeignAssetId: 'u32', + NativeAssetId: 'PalletForeignAssetsNativeCurrency' + } + }, + /** + * Lookup60: pallet_foreign_assets::NativeCurrency + **/ + PalletForeignAssetsNativeCurrency: { + _enum: ['Here', 'Parent'] + }, + /** + * Lookup61: cumulus_pallet_xcmp_queue::pallet::Event + **/ + CumulusPalletXcmpQueueEvent: { + _enum: { + Success: { + messageHash: 'Option', + weight: 'u64', + }, + Fail: { + messageHash: 'Option', + error: 'XcmV2TraitsError', + weight: 'u64', + }, + BadVersion: { + messageHash: 'Option', + }, + BadFormat: { + messageHash: 'Option', + }, + UpwardMessageSent: { + messageHash: 'Option', + }, + XcmpMessageSent: { + messageHash: 'Option', + }, + OverweightEnqueued: { + sender: 'u32', + sentAt: 'u32', + index: 'u64', + required: 'u64', + }, + OverweightServiced: { + index: 'u64', + used: 'u64' + } + } + }, + /** + * Lookup63: xcm::v2::traits::Error + **/ + XcmV2TraitsError: { + _enum: { + Overflow: 'Null', + Unimplemented: 'Null', + UntrustedReserveLocation: 'Null', + UntrustedTeleportLocation: 'Null', + MultiLocationFull: 'Null', + MultiLocationNotInvertible: 'Null', + BadOrigin: 'Null', + InvalidLocation: 'Null', + AssetNotFound: 'Null', + FailedToTransactAsset: 'Null', + NotWithdrawable: 'Null', + LocationCannotHold: 'Null', + ExceedsMaxMessageSize: 'Null', + DestinationUnsupported: 'Null', + Transport: 'Null', + Unroutable: 'Null', + UnknownClaim: 'Null', + FailedToDecode: 'Null', + MaxWeightInvalid: 'Null', + NotHoldingFees: 'Null', + TooExpensive: 'Null', + Trap: 'u64', + UnhandledXcmVersion: 'Null', + WeightLimitReached: 'u64', + Barrier: 'Null', + WeightNotComputable: 'Null' + } + }, + /** + * Lookup65: pallet_xcm::pallet::Event **/ + PalletXcmEvent: { + _enum: { + Attempted: 'XcmV2TraitsOutcome', + Sent: '(XcmV1MultiLocation,XcmV1MultiLocation,XcmV2Xcm)', + UnexpectedResponse: '(XcmV1MultiLocation,u64)', + ResponseReady: '(u64,XcmV2Response)', + Notified: '(u64,u8,u8)', + NotifyOverweight: '(u64,u8,u8,u64,u64)', + NotifyDispatchError: '(u64,u8,u8)', + NotifyDecodeFailed: '(u64,u8,u8)', + InvalidResponder: '(XcmV1MultiLocation,u64,Option)', + InvalidResponderVersion: '(XcmV1MultiLocation,u64)', + ResponseTaken: 'u64', + AssetsTrapped: '(H256,XcmV1MultiLocation,XcmVersionedMultiAssets)', + VersionChangeNotified: '(XcmV1MultiLocation,u32)', + SupportedVersionChanged: '(XcmV1MultiLocation,u32)', + NotifyTargetSendFail: '(XcmV1MultiLocation,u64,XcmV2TraitsError)', + NotifyTargetMigrationFail: '(XcmVersionedMultiLocation,u64)' + } + }, + /** + * Lookup66: xcm::v2::traits::Outcome + **/ + XcmV2TraitsOutcome: { + _enum: { + Complete: 'u64', + Incomplete: '(u64,XcmV2TraitsError)', + Error: 'XcmV2TraitsError' + } + }, + /** + * Lookup67: xcm::v2::Xcm + **/ XcmV2Xcm: 'Vec', /** - * Lookup57: xcm::v2::Instruction + * Lookup69: xcm::v2::Instruction **/ XcmV2Instruction: { _enum: { @@ -625,53 +773,10 @@ maxResponseWeight: 'Compact', }, UnsubscribeVersion: 'Null' - } - }, - /** - * Lookup58: xcm::v1::multiasset::MultiAssets - **/ - XcmV1MultiassetMultiAssets: 'Vec', - /** - * Lookup60: xcm::v1::multiasset::MultiAsset - **/ - XcmV1MultiAsset: { - id: 'XcmV1MultiassetAssetId', - fun: 'XcmV1MultiassetFungibility' - }, - /** - * Lookup61: xcm::v1::multiasset::AssetId - **/ - XcmV1MultiassetAssetId: { - _enum: { - Concrete: 'XcmV1MultiLocation', - Abstract: 'Bytes' } }, /** - * Lookup62: xcm::v1::multiasset::Fungibility - **/ - XcmV1MultiassetFungibility: { - _enum: { - Fungible: 'Compact', - NonFungible: 'XcmV1MultiassetAssetInstance' - } - }, - /** - * Lookup63: xcm::v1::multiasset::AssetInstance - **/ - XcmV1MultiassetAssetInstance: { - _enum: { - Undefined: 'Null', - Index: 'Compact', - Array4: '[u8;4]', - Array8: '[u8;8]', - Array16: '[u8;16]', - Array32: '[u8;32]', - Blob: 'Bytes' - } - }, - /** - * Lookup66: xcm::v2::Response + * Lookup70: xcm::v2::Response **/ XcmV2Response: { _enum: { @@ -682,19 +787,19 @@ } }, /** - * Lookup69: xcm::v0::OriginKind + * Lookup73: xcm::v0::OriginKind **/ XcmV0OriginKind: { _enum: ['Native', 'SovereignAccount', 'Superuser', 'Xcm'] }, /** - * Lookup70: xcm::double_encoded::DoubleEncoded + * Lookup74: xcm::double_encoded::DoubleEncoded **/ XcmDoubleEncoded: { encoded: 'Bytes' }, /** - * Lookup71: xcm::v1::multiasset::MultiAssetFilter + * Lookup75: xcm::v1::multiasset::MultiAssetFilter **/ XcmV1MultiassetMultiAssetFilter: { _enum: { @@ -703,7 +808,7 @@ } }, /** - * Lookup72: xcm::v1::multiasset::WildMultiAsset + * Lookup76: xcm::v1::multiasset::WildMultiAsset **/ XcmV1MultiassetWildMultiAsset: { _enum: { @@ -715,13 +820,13 @@ } }, /** - * Lookup73: xcm::v1::multiasset::WildFungibility + * Lookup77: xcm::v1::multiasset::WildFungibility **/ XcmV1MultiassetWildFungibility: { _enum: ['Fungible', 'NonFungible'] }, /** - * Lookup74: xcm::v2::WeightLimit + * Lookup78: xcm::v2::WeightLimit **/ XcmV2WeightLimit: { _enum: { @@ -730,7 +835,7 @@ } }, /** - * Lookup76: xcm::VersionedMultiAssets + * Lookup80: xcm::VersionedMultiAssets **/ XcmVersionedMultiAssets: { _enum: { @@ -739,7 +844,7 @@ } }, /** - * Lookup78: xcm::v0::multi_asset::MultiAsset + * Lookup82: xcm::v0::multi_asset::MultiAsset **/ XcmV0MultiAsset: { _enum: { @@ -778,7 +883,7 @@ } }, /** - * Lookup79: xcm::v0::multi_location::MultiLocation + * Lookup83: xcm::v0::multi_location::MultiLocation **/ XcmV0MultiLocation: { _enum: { @@ -794,7 +899,7 @@ } }, /** - * Lookup80: xcm::v0::junction::Junction + * Lookup84: xcm::v0::junction::Junction **/ XcmV0Junction: { _enum: { @@ -823,7 +928,7 @@ } }, /** - * Lookup81: xcm::VersionedMultiLocation + * Lookup85: xcm::VersionedMultiLocation **/ XcmVersionedMultiLocation: { _enum: { @@ -832,7 +937,7 @@ } }, /** - * Lookup82: cumulus_pallet_xcm::pallet::Event + * Lookup86: cumulus_pallet_xcm::pallet::Event **/ CumulusPalletXcmEvent: { _enum: { @@ -842,7 +947,7 @@ } }, /** - * Lookup83: cumulus_pallet_dmp_queue::pallet::Event + * Lookup87: cumulus_pallet_dmp_queue::pallet::Event **/ CumulusPalletDmpQueueEvent: { _enum: { @@ -873,7 +978,7 @@ } }, /** - * Lookup84: pallet_unique::RawEvent> + * Lookup88: pallet_unique::RawEvent> **/ PalletUniqueRawEvent: { _enum: { @@ -890,7 +995,7 @@ } }, /** - * Lookup85: pallet_evm::account::BasicCrossAccountIdRepr + * Lookup89: pallet_evm::account::BasicCrossAccountIdRepr **/ PalletEvmAccountBasicCrossAccountIdRepr: { _enum: { @@ -899,7 +1004,7 @@ } }, /** - * Lookup88: pallet_unique_scheduler::pallet::Event + * Lookup92: pallet_unique_scheduler::pallet::Event **/ PalletUniqueSchedulerEvent: { _enum: { @@ -924,13 +1029,13 @@ } }, /** - * Lookup91: frame_support::traits::schedule::LookupError + * Lookup95: frame_support::traits::schedule::LookupError **/ FrameSupportScheduleLookupError: { _enum: ['Unknown', 'BadFormat'] }, /** - * Lookup92: pallet_common::pallet::Event + * Lookup96: pallet_common::pallet::Event **/ PalletCommonEvent: { _enum: { @@ -948,7 +1053,7 @@ } }, /** - * Lookup95: pallet_structure::pallet::Event + * Lookup99: pallet_structure::pallet::Event **/ PalletStructureEvent: { _enum: { @@ -956,7 +1061,7 @@ } }, /** - * Lookup96: pallet_rmrk_core::pallet::Event + * Lookup100: pallet_rmrk_core::pallet::Event **/ PalletRmrkCoreEvent: { _enum: { @@ -1033,7 +1138,7 @@ } }, /** - * Lookup97: rmrk_traits::nft::AccountIdOrCollectionNftTuple + * Lookup101: rmrk_traits::nft::AccountIdOrCollectionNftTuple **/ RmrkTraitsNftAccountIdOrCollectionNftTuple: { _enum: { @@ -1042,7 +1147,7 @@ } }, /** - * Lookup102: pallet_rmrk_equip::pallet::Event + * Lookup106: pallet_rmrk_equip::pallet::Event **/ PalletRmrkEquipEvent: { _enum: { @@ -1057,7 +1162,7 @@ } }, /** - * Lookup103: pallet_app_promotion::pallet::Event + * Lookup107: pallet_app_promotion::pallet::Event **/ PalletAppPromotionEvent: { _enum: { @@ -1068,8 +1173,42 @@ } }, /** - * Lookup104: pallet_evm::pallet::Event + * Lookup108: pallet_foreign_assets::module::Event **/ + PalletForeignAssetsModuleEvent: { + _enum: { + ForeignAssetRegistered: { + assetId: 'u32', + assetAddress: 'XcmV1MultiLocation', + metadata: 'PalletForeignAssetsModuleAssetMetadata', + }, + ForeignAssetUpdated: { + assetId: 'u32', + assetAddress: 'XcmV1MultiLocation', + metadata: 'PalletForeignAssetsModuleAssetMetadata', + }, + AssetRegistered: { + assetId: 'PalletForeignAssetsAssetIds', + metadata: 'PalletForeignAssetsModuleAssetMetadata', + }, + AssetUpdated: { + assetId: 'PalletForeignAssetsAssetIds', + metadata: 'PalletForeignAssetsModuleAssetMetadata' + } + } + }, + /** + * Lookup109: pallet_foreign_assets::module::AssetMetadata + **/ + PalletForeignAssetsModuleAssetMetadata: { + name: 'Bytes', + symbol: 'Bytes', + decimals: 'u8', + minimalBalance: 'u128' + }, + /** + * Lookup110: pallet_evm::pallet::Event + **/ PalletEvmEvent: { _enum: { Log: 'EthereumLog', @@ -1082,7 +1221,7 @@ } }, /** - * Lookup105: ethereum::log::Log + * Lookup111: ethereum::log::Log **/ EthereumLog: { address: 'H160', @@ -1090,7 +1229,7 @@ data: 'Bytes' }, /** - * Lookup109: pallet_ethereum::pallet::Event + * Lookup115: pallet_ethereum::pallet::Event **/ PalletEthereumEvent: { _enum: { @@ -1098,7 +1237,7 @@ } }, /** - * Lookup110: evm_core::error::ExitReason + * Lookup116: evm_core::error::ExitReason **/ EvmCoreErrorExitReason: { _enum: { @@ -1109,13 +1248,13 @@ } }, /** - * Lookup111: evm_core::error::ExitSucceed + * Lookup117: evm_core::error::ExitSucceed **/ EvmCoreErrorExitSucceed: { _enum: ['Stopped', 'Returned', 'Suicided'] }, /** - * Lookup112: evm_core::error::ExitError + * Lookup118: evm_core::error::ExitError **/ EvmCoreErrorExitError: { _enum: { @@ -1137,13 +1276,13 @@ } }, /** - * Lookup115: evm_core::error::ExitRevert + * Lookup121: evm_core::error::ExitRevert **/ EvmCoreErrorExitRevert: { _enum: ['Reverted'] }, /** - * Lookup116: evm_core::error::ExitFatal + * Lookup122: evm_core::error::ExitFatal **/ EvmCoreErrorExitFatal: { _enum: { @@ -1154,7 +1293,7 @@ } }, /** - * Lookup117: pallet_evm_contract_helpers::pallet::Event + * Lookup123: pallet_evm_contract_helpers::pallet::Event **/ PalletEvmContractHelpersEvent: { _enum: { @@ -1164,7 +1303,7 @@ } }, /** - * Lookup118: frame_system::Phase + * Lookup124: frame_system::Phase **/ FrameSystemPhase: { _enum: { @@ -1174,14 +1313,14 @@ } }, /** - * Lookup120: frame_system::LastRuntimeUpgradeInfo + * Lookup126: frame_system::LastRuntimeUpgradeInfo **/ FrameSystemLastRuntimeUpgradeInfo: { specVersion: 'Compact', specName: 'Text' }, /** - * Lookup121: frame_system::pallet::Call + * Lookup127: frame_system::pallet::Call **/ FrameSystemCall: { _enum: { @@ -1219,7 +1358,7 @@ } }, /** - * Lookup126: frame_system::limits::BlockWeights + * Lookup132: frame_system::limits::BlockWeights **/ FrameSystemLimitsBlockWeights: { baseBlock: 'u64', @@ -1227,7 +1366,7 @@ perClass: 'FrameSupportWeightsPerDispatchClassWeightsPerClass' }, /** - * Lookup127: frame_support::weights::PerDispatchClass + * Lookup133: frame_support::weights::PerDispatchClass **/ FrameSupportWeightsPerDispatchClassWeightsPerClass: { normal: 'FrameSystemLimitsWeightsPerClass', @@ -1235,7 +1374,7 @@ mandatory: 'FrameSystemLimitsWeightsPerClass' }, /** - * Lookup128: frame_system::limits::WeightsPerClass + * Lookup134: frame_system::limits::WeightsPerClass **/ FrameSystemLimitsWeightsPerClass: { baseExtrinsic: 'u64', @@ -1244,13 +1383,13 @@ reserved: 'Option' }, /** - * Lookup130: frame_system::limits::BlockLength + * Lookup136: frame_system::limits::BlockLength **/ FrameSystemLimitsBlockLength: { max: 'FrameSupportWeightsPerDispatchClassU32' }, /** - * Lookup131: frame_support::weights::PerDispatchClass + * Lookup137: frame_support::weights::PerDispatchClass **/ FrameSupportWeightsPerDispatchClassU32: { normal: 'u32', @@ -1258,14 +1397,14 @@ mandatory: 'u32' }, /** - * Lookup132: frame_support::weights::RuntimeDbWeight + * Lookup138: frame_support::weights::RuntimeDbWeight **/ FrameSupportWeightsRuntimeDbWeight: { read: 'u64', write: 'u64' }, /** - * Lookup133: sp_version::RuntimeVersion + * Lookup139: sp_version::RuntimeVersion **/ SpVersionRuntimeVersion: { specName: 'Text', @@ -1278,13 +1417,13 @@ stateVersion: 'u8' }, /** - * Lookup138: frame_system::pallet::Error + * Lookup144: frame_system::pallet::Error **/ FrameSystemError: { _enum: ['InvalidSpecName', 'SpecVersionNeedsToIncrease', 'FailedToExtractRuntimeVersion', 'NonDefaultComposite', 'NonZeroRefCount', 'CallFiltered'] }, /** - * Lookup139: polkadot_primitives::v2::PersistedValidationData + * Lookup145: polkadot_primitives::v2::PersistedValidationData **/ PolkadotPrimitivesV2PersistedValidationData: { parentHead: 'Bytes', @@ -1293,19 +1432,19 @@ maxPovSize: 'u32' }, /** - * Lookup142: polkadot_primitives::v2::UpgradeRestriction + * Lookup148: polkadot_primitives::v2::UpgradeRestriction **/ PolkadotPrimitivesV2UpgradeRestriction: { _enum: ['Present'] }, /** - * Lookup143: sp_trie::storage_proof::StorageProof + * Lookup149: sp_trie::storage_proof::StorageProof **/ SpTrieStorageProof: { trieNodes: 'BTreeSet' }, /** - * Lookup145: cumulus_pallet_parachain_system::relay_state_snapshot::MessagingStateSnapshot + * Lookup151: cumulus_pallet_parachain_system::relay_state_snapshot::MessagingStateSnapshot **/ CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot: { dmqMqcHead: 'H256', @@ -1314,7 +1453,7 @@ egressChannels: 'Vec<(u32,PolkadotPrimitivesV2AbridgedHrmpChannel)>' }, /** - * Lookup148: polkadot_primitives::v2::AbridgedHrmpChannel + * Lookup154: polkadot_primitives::v2::AbridgedHrmpChannel **/ PolkadotPrimitivesV2AbridgedHrmpChannel: { maxCapacity: 'u32', @@ -1325,7 +1464,7 @@ mqcHead: 'Option' }, /** - * Lookup149: polkadot_primitives::v2::AbridgedHostConfiguration + * Lookup155: polkadot_primitives::v2::AbridgedHostConfiguration **/ PolkadotPrimitivesV2AbridgedHostConfiguration: { maxCodeSize: 'u32', @@ -1339,14 +1478,14 @@ validationUpgradeDelay: 'u32' }, /** - * Lookup155: polkadot_core_primitives::OutboundHrmpMessage + * Lookup161: polkadot_core_primitives::OutboundHrmpMessage **/ PolkadotCorePrimitivesOutboundHrmpMessage: { recipient: 'u32', data: 'Bytes' }, /** - * Lookup156: cumulus_pallet_parachain_system::pallet::Call + * Lookup162: cumulus_pallet_parachain_system::pallet::Call **/ CumulusPalletParachainSystemCall: { _enum: { @@ -1365,7 +1504,7 @@ } }, /** - * Lookup157: cumulus_primitives_parachain_inherent::ParachainInherentData + * Lookup163: cumulus_primitives_parachain_inherent::ParachainInherentData **/ CumulusPrimitivesParachainInherentParachainInherentData: { validationData: 'PolkadotPrimitivesV2PersistedValidationData', @@ -1374,27 +1513,27 @@ horizontalMessages: 'BTreeMap>' }, /** - * Lookup159: polkadot_core_primitives::InboundDownwardMessage + * Lookup165: polkadot_core_primitives::InboundDownwardMessage **/ PolkadotCorePrimitivesInboundDownwardMessage: { sentAt: 'u32', msg: 'Bytes' }, /** - * Lookup162: polkadot_core_primitives::InboundHrmpMessage + * Lookup168: polkadot_core_primitives::InboundHrmpMessage **/ PolkadotCorePrimitivesInboundHrmpMessage: { sentAt: 'u32', data: 'Bytes' }, /** - * Lookup165: cumulus_pallet_parachain_system::pallet::Error + * Lookup171: cumulus_pallet_parachain_system::pallet::Error **/ CumulusPalletParachainSystemError: { _enum: ['OverlappingUpgrades', 'ProhibitedByPolkadot', 'TooBig', 'ValidationDataNotAvailable', 'HostConfigurationNotAvailable', 'NotScheduled', 'NothingAuthorized', 'Unauthorized'] }, /** - * Lookup167: pallet_balances::BalanceLock + * Lookup173: pallet_balances::BalanceLock **/ PalletBalancesBalanceLock: { id: '[u8;8]', @@ -1402,26 +1541,26 @@ reasons: 'PalletBalancesReasons' }, /** - * Lookup168: pallet_balances::Reasons + * Lookup174: pallet_balances::Reasons **/ PalletBalancesReasons: { _enum: ['Fee', 'Misc', 'All'] }, /** - * Lookup171: pallet_balances::ReserveData + * Lookup177: pallet_balances::ReserveData **/ PalletBalancesReserveData: { id: '[u8;16]', amount: 'u128' }, /** - * Lookup173: pallet_balances::Releases + * Lookup179: pallet_balances::Releases **/ PalletBalancesReleases: { _enum: ['V1_0_0', 'V2_0_0'] }, /** - * Lookup174: pallet_balances::pallet::Call + * Lookup180: pallet_balances::pallet::Call **/ PalletBalancesCall: { _enum: { @@ -1454,13 +1593,13 @@ } }, /** - * Lookup177: pallet_balances::pallet::Error + * Lookup183: pallet_balances::pallet::Error **/ PalletBalancesError: { _enum: ['VestingBalance', 'LiquidityRestrictions', 'InsufficientBalance', 'ExistentialDeposit', 'KeepAlive', 'ExistingVestingSchedule', 'DeadAccount', 'TooManyReserves'] }, /** - * Lookup179: pallet_timestamp::pallet::Call + * Lookup185: pallet_timestamp::pallet::Call **/ PalletTimestampCall: { _enum: { @@ -1470,13 +1609,13 @@ } }, /** - * Lookup181: pallet_transaction_payment::Releases + * Lookup187: pallet_transaction_payment::Releases **/ PalletTransactionPaymentReleases: { _enum: ['V1Ancient', 'V2'] }, /** - * Lookup182: pallet_treasury::Proposal + * Lookup188: pallet_treasury::Proposal **/ PalletTreasuryProposal: { proposer: 'AccountId32', @@ -1485,7 +1624,7 @@ bond: 'u128' }, /** - * Lookup185: pallet_treasury::pallet::Call + * Lookup191: pallet_treasury::pallet::Call **/ PalletTreasuryCall: { _enum: { @@ -1509,17 +1648,17 @@ } }, /** - * Lookup188: frame_support::PalletId + * Lookup194: frame_support::PalletId **/ FrameSupportPalletId: '[u8;8]', /** - * Lookup189: pallet_treasury::pallet::Error + * Lookup195: pallet_treasury::pallet::Error **/ PalletTreasuryError: { _enum: ['InsufficientProposersBalance', 'InvalidIndex', 'TooManyApprovals', 'InsufficientPermission', 'ProposalNotApproved'] }, /** - * Lookup190: pallet_sudo::pallet::Call + * Lookup196: pallet_sudo::pallet::Call **/ PalletSudoCall: { _enum: { @@ -1543,7 +1682,7 @@ } }, /** - * Lookup192: orml_vesting::module::Call + * Lookup198: orml_vesting::module::Call **/ OrmlVestingModuleCall: { _enum: { @@ -1562,8 +1701,94 @@ } }, /** - * Lookup194: cumulus_pallet_xcmp_queue::pallet::Call + * Lookup200: orml_xtokens::module::Call **/ + OrmlXtokensModuleCall: { + _enum: { + transfer: { + currencyId: 'PalletForeignAssetsAssetIds', + amount: 'u128', + dest: 'XcmVersionedMultiLocation', + destWeight: 'u64', + }, + transfer_multiasset: { + asset: 'XcmVersionedMultiAsset', + dest: 'XcmVersionedMultiLocation', + destWeight: 'u64', + }, + transfer_with_fee: { + currencyId: 'PalletForeignAssetsAssetIds', + amount: 'u128', + fee: 'u128', + dest: 'XcmVersionedMultiLocation', + destWeight: 'u64', + }, + transfer_multiasset_with_fee: { + asset: 'XcmVersionedMultiAsset', + fee: 'XcmVersionedMultiAsset', + dest: 'XcmVersionedMultiLocation', + destWeight: 'u64', + }, + transfer_multicurrencies: { + currencies: 'Vec<(PalletForeignAssetsAssetIds,u128)>', + feeItem: 'u32', + dest: 'XcmVersionedMultiLocation', + destWeight: 'u64', + }, + transfer_multiassets: { + assets: 'XcmVersionedMultiAssets', + feeItem: 'u32', + dest: 'XcmVersionedMultiLocation', + destWeight: 'u64' + } + } + }, + /** + * Lookup201: xcm::VersionedMultiAsset + **/ + XcmVersionedMultiAsset: { + _enum: { + V0: 'XcmV0MultiAsset', + V1: 'XcmV1MultiAsset' + } + }, + /** + * Lookup204: orml_tokens::module::Call + **/ + OrmlTokensModuleCall: { + _enum: { + transfer: { + dest: 'MultiAddress', + currencyId: 'PalletForeignAssetsAssetIds', + amount: 'Compact', + }, + transfer_all: { + dest: 'MultiAddress', + currencyId: 'PalletForeignAssetsAssetIds', + keepAlive: 'bool', + }, + transfer_keep_alive: { + dest: 'MultiAddress', + currencyId: 'PalletForeignAssetsAssetIds', + amount: 'Compact', + }, + force_transfer: { + source: 'MultiAddress', + dest: 'MultiAddress', + currencyId: 'PalletForeignAssetsAssetIds', + amount: 'Compact', + }, + set_balance: { + who: 'MultiAddress', + currencyId: 'PalletForeignAssetsAssetIds', + newFree: 'Compact', + newReserved: 'Compact' + } + } + }, + /** + * Lookup205: cumulus_pallet_xcmp_queue::pallet::Call + **/ CumulusPalletXcmpQueueCall: { _enum: { service_overweight: { @@ -1611,7 +1836,7 @@ } }, /** - * Lookup195: pallet_xcm::pallet::Call + * Lookup206: pallet_xcm::pallet::Call **/ PalletXcmCall: { _enum: { @@ -1665,7 +1890,7 @@ } }, /** - * Lookup196: xcm::VersionedXcm + * Lookup207: xcm::VersionedXcm **/ XcmVersionedXcm: { _enum: { @@ -1675,7 +1900,7 @@ } }, /** - * Lookup197: xcm::v0::Xcm + * Lookup208: xcm::v0::Xcm **/ XcmV0Xcm: { _enum: { @@ -1729,7 +1954,7 @@ } }, /** - * Lookup199: xcm::v0::order::Order + * Lookup210: xcm::v0::order::Order **/ XcmV0Order: { _enum: { @@ -1772,7 +1997,7 @@ } }, /** - * Lookup201: xcm::v0::Response + * Lookup212: xcm::v0::Response **/ XcmV0Response: { _enum: { @@ -1780,7 +2005,7 @@ } }, /** - * Lookup202: xcm::v1::Xcm + * Lookup213: xcm::v1::Xcm **/ XcmV1Xcm: { _enum: { @@ -1839,7 +2064,7 @@ } }, /** - * Lookup204: xcm::v1::order::Order + * Lookup215: xcm::v1::order::Order **/ XcmV1Order: { _enum: { @@ -1884,7 +2109,7 @@ } }, /** - * Lookup206: xcm::v1::Response + * Lookup217: xcm::v1::Response **/ XcmV1Response: { _enum: { @@ -1893,11 +2118,11 @@ } }, /** - * Lookup220: cumulus_pallet_xcm::pallet::Call + * Lookup231: cumulus_pallet_xcm::pallet::Call **/ CumulusPalletXcmCall: 'Null', /** - * Lookup221: cumulus_pallet_dmp_queue::pallet::Call + * Lookup232: cumulus_pallet_dmp_queue::pallet::Call **/ CumulusPalletDmpQueueCall: { _enum: { @@ -1908,7 +2133,7 @@ } }, /** - * Lookup222: pallet_inflation::pallet::Call + * Lookup233: pallet_inflation::pallet::Call **/ PalletInflationCall: { _enum: { @@ -1918,7 +2143,7 @@ } }, /** - * Lookup223: pallet_unique::Call + * Lookup234: pallet_unique::Call **/ PalletUniqueCall: { _enum: { @@ -2050,7 +2275,7 @@ } }, /** - * Lookup228: up_data_structs::CollectionMode + * Lookup239: up_data_structs::CollectionMode **/ UpDataStructsCollectionMode: { _enum: { @@ -2060,7 +2285,7 @@ } }, /** - * Lookup229: up_data_structs::CreateCollectionData + * Lookup240: up_data_structs::CreateCollectionData **/ UpDataStructsCreateCollectionData: { mode: 'UpDataStructsCollectionMode', @@ -2075,13 +2300,13 @@ properties: 'Vec' }, /** - * Lookup231: up_data_structs::AccessMode + * Lookup242: up_data_structs::AccessMode **/ UpDataStructsAccessMode: { _enum: ['Normal', 'AllowList'] }, /** - * Lookup233: up_data_structs::CollectionLimits + * Lookup244: up_data_structs::CollectionLimits **/ UpDataStructsCollectionLimits: { accountTokenOwnershipLimit: 'Option', @@ -2095,7 +2320,7 @@ transfersEnabled: 'Option' }, /** - * Lookup235: up_data_structs::SponsoringRateLimit + * Lookup246: up_data_structs::SponsoringRateLimit **/ UpDataStructsSponsoringRateLimit: { _enum: { @@ -2104,7 +2329,7 @@ } }, /** - * Lookup238: up_data_structs::CollectionPermissions + * Lookup249: up_data_structs::CollectionPermissions **/ UpDataStructsCollectionPermissions: { access: 'Option', @@ -2112,7 +2337,7 @@ nesting: 'Option' }, /** - * Lookup240: up_data_structs::NestingPermissions + * Lookup251: up_data_structs::NestingPermissions **/ UpDataStructsNestingPermissions: { tokenOwner: 'bool', @@ -2120,18 +2345,18 @@ restricted: 'Option' }, /** - * Lookup242: up_data_structs::OwnerRestrictedSet + * Lookup253: up_data_structs::OwnerRestrictedSet **/ UpDataStructsOwnerRestrictedSet: 'BTreeSet', /** - * Lookup247: up_data_structs::PropertyKeyPermission + * Lookup258: up_data_structs::PropertyKeyPermission **/ UpDataStructsPropertyKeyPermission: { key: 'Bytes', permission: 'UpDataStructsPropertyPermission' }, /** - * Lookup248: up_data_structs::PropertyPermission + * Lookup259: up_data_structs::PropertyPermission **/ UpDataStructsPropertyPermission: { mutable: 'bool', @@ -2139,14 +2364,14 @@ tokenOwner: 'bool' }, /** - * Lookup251: up_data_structs::Property + * Lookup262: up_data_structs::Property **/ UpDataStructsProperty: { key: 'Bytes', value: 'Bytes' }, /** - * Lookup254: up_data_structs::CreateItemData + * Lookup265: up_data_structs::CreateItemData **/ UpDataStructsCreateItemData: { _enum: { @@ -2156,26 +2381,26 @@ } }, /** - * Lookup255: up_data_structs::CreateNftData + * Lookup266: up_data_structs::CreateNftData **/ UpDataStructsCreateNftData: { properties: 'Vec' }, /** - * Lookup256: up_data_structs::CreateFungibleData + * Lookup267: up_data_structs::CreateFungibleData **/ UpDataStructsCreateFungibleData: { value: 'u128' }, /** - * Lookup257: up_data_structs::CreateReFungibleData + * Lookup268: up_data_structs::CreateReFungibleData **/ UpDataStructsCreateReFungibleData: { pieces: 'u128', properties: 'Vec' }, /** - * Lookup260: up_data_structs::CreateItemExData> + * Lookup271: up_data_structs::CreateItemExData> **/ UpDataStructsCreateItemExData: { _enum: { @@ -2186,14 +2411,14 @@ } }, /** - * Lookup262: up_data_structs::CreateNftExData> + * Lookup273: up_data_structs::CreateNftExData> **/ UpDataStructsCreateNftExData: { properties: 'Vec', owner: 'PalletEvmAccountBasicCrossAccountIdRepr' }, /** - * Lookup269: up_data_structs::CreateRefungibleExSingleOwner> + * Lookup280: up_data_structs::CreateRefungibleExSingleOwner> **/ UpDataStructsCreateRefungibleExSingleOwner: { user: 'PalletEvmAccountBasicCrossAccountIdRepr', @@ -2201,14 +2426,14 @@ properties: 'Vec' }, /** - * Lookup271: up_data_structs::CreateRefungibleExMultipleOwners> + * Lookup282: up_data_structs::CreateRefungibleExMultipleOwners> **/ UpDataStructsCreateRefungibleExMultipleOwners: { users: 'BTreeMap', properties: 'Vec' }, /** - * Lookup272: pallet_unique_scheduler::pallet::Call + * Lookup283: pallet_unique_scheduler::pallet::Call **/ PalletUniqueSchedulerCall: { _enum: { @@ -2232,7 +2457,7 @@ } }, /** - * Lookup274: frame_support::traits::schedule::MaybeHashed + * Lookup285: frame_support::traits::schedule::MaybeHashed **/ FrameSupportScheduleMaybeHashed: { _enum: { @@ -2241,7 +2466,7 @@ } }, /** - * Lookup275: pallet_configuration::pallet::Call + * Lookup286: pallet_configuration::pallet::Call **/ PalletConfigurationCall: { _enum: { @@ -2254,15 +2479,15 @@ } }, /** - * Lookup276: pallet_template_transaction_payment::Call + * Lookup287: pallet_template_transaction_payment::Call **/ PalletTemplateTransactionPaymentCall: 'Null', /** - * Lookup277: pallet_structure::pallet::Call + * Lookup288: pallet_structure::pallet::Call **/ PalletStructureCall: 'Null', /** - * Lookup278: pallet_rmrk_core::pallet::Call + * Lookup289: pallet_rmrk_core::pallet::Call **/ PalletRmrkCoreCall: { _enum: { @@ -2353,7 +2578,7 @@ } }, /** - * Lookup284: rmrk_traits::resource::ResourceTypes, sp_runtime::bounded::bounded_vec::BoundedVec> + * Lookup295: rmrk_traits::resource::ResourceTypes, sp_runtime::bounded::bounded_vec::BoundedVec> **/ RmrkTraitsResourceResourceTypes: { _enum: { @@ -2363,7 +2588,7 @@ } }, /** - * Lookup286: rmrk_traits::resource::BasicResource> + * Lookup297: rmrk_traits::resource::BasicResource> **/ RmrkTraitsResourceBasicResource: { src: 'Option', @@ -2372,7 +2597,7 @@ thumb: 'Option' }, /** - * Lookup288: rmrk_traits::resource::ComposableResource, sp_runtime::bounded::bounded_vec::BoundedVec> + * Lookup299: rmrk_traits::resource::ComposableResource, sp_runtime::bounded::bounded_vec::BoundedVec> **/ RmrkTraitsResourceComposableResource: { parts: 'Vec', @@ -2383,7 +2608,7 @@ thumb: 'Option' }, /** - * Lookup289: rmrk_traits::resource::SlotResource> + * Lookup300: rmrk_traits::resource::SlotResource> **/ RmrkTraitsResourceSlotResource: { base: 'u32', @@ -2394,7 +2619,7 @@ thumb: 'Option' }, /** - * Lookup292: pallet_rmrk_equip::pallet::Call + * Lookup303: pallet_rmrk_equip::pallet::Call **/ PalletRmrkEquipCall: { _enum: { @@ -2415,7 +2640,7 @@ } }, /** - * Lookup295: rmrk_traits::part::PartType, sp_runtime::bounded::bounded_vec::BoundedVec> + * Lookup306: rmrk_traits::part::PartType, sp_runtime::bounded::bounded_vec::BoundedVec> **/ RmrkTraitsPartPartType: { _enum: { @@ -2424,7 +2649,7 @@ } }, /** - * Lookup297: rmrk_traits::part::FixedPart> + * Lookup308: rmrk_traits::part::FixedPart> **/ RmrkTraitsPartFixedPart: { id: 'u32', @@ -2432,7 +2657,7 @@ src: 'Bytes' }, /** - * Lookup298: rmrk_traits::part::SlotPart, sp_runtime::bounded::bounded_vec::BoundedVec> + * Lookup309: rmrk_traits::part::SlotPart, sp_runtime::bounded::bounded_vec::BoundedVec> **/ RmrkTraitsPartSlotPart: { id: 'u32', @@ -2441,7 +2666,7 @@ z: 'u32' }, /** - * Lookup299: rmrk_traits::part::EquippableList> + * Lookup310: rmrk_traits::part::EquippableList> **/ RmrkTraitsPartEquippableList: { _enum: { @@ -2451,7 +2676,7 @@ } }, /** - * Lookup301: rmrk_traits::theme::Theme, sp_runtime::bounded::bounded_vec::BoundedVec>, S>> + * Lookup312: rmrk_traits::theme::Theme, sp_runtime::bounded::bounded_vec::BoundedVec>, S>> **/ RmrkTraitsTheme: { name: 'Bytes', @@ -2459,14 +2684,14 @@ inherit: 'bool' }, /** - * Lookup303: rmrk_traits::theme::ThemeProperty> + * Lookup314: rmrk_traits::theme::ThemeProperty> **/ RmrkTraitsThemeThemeProperty: { key: 'Bytes', value: 'Bytes' }, /** - * Lookup305: pallet_app_promotion::pallet::Call + * Lookup316: pallet_app_promotion::pallet::Call **/ PalletAppPromotionCall: { _enum: { @@ -2495,8 +2720,25 @@ } }, /** - * Lookup307: pallet_evm::pallet::Call + * Lookup318: pallet_foreign_assets::module::Call **/ + PalletForeignAssetsModuleCall: { + _enum: { + register_foreign_asset: { + owner: 'AccountId32', + location: 'XcmVersionedMultiLocation', + metadata: 'PalletForeignAssetsModuleAssetMetadata', + }, + update_foreign_asset: { + foreignAssetId: 'u32', + location: 'XcmVersionedMultiLocation', + metadata: 'PalletForeignAssetsModuleAssetMetadata' + } + } + }, + /** + * Lookup319: pallet_evm::pallet::Call + **/ PalletEvmCall: { _enum: { withdraw: { @@ -2538,7 +2780,7 @@ } }, /** - * Lookup311: pallet_ethereum::pallet::Call + * Lookup323: pallet_ethereum::pallet::Call **/ PalletEthereumCall: { _enum: { @@ -2548,7 +2790,7 @@ } }, /** - * Lookup312: ethereum::transaction::TransactionV2 + * Lookup324: ethereum::transaction::TransactionV2 **/ EthereumTransactionTransactionV2: { _enum: { @@ -2558,7 +2800,7 @@ } }, /** - * Lookup313: ethereum::transaction::LegacyTransaction + * Lookup325: ethereum::transaction::LegacyTransaction **/ EthereumTransactionLegacyTransaction: { nonce: 'U256', @@ -2570,7 +2812,7 @@ signature: 'EthereumTransactionTransactionSignature' }, /** - * Lookup314: ethereum::transaction::TransactionAction + * Lookup326: ethereum::transaction::TransactionAction **/ EthereumTransactionTransactionAction: { _enum: { @@ -2579,7 +2821,7 @@ } }, /** - * Lookup315: ethereum::transaction::TransactionSignature + * Lookup327: ethereum::transaction::TransactionSignature **/ EthereumTransactionTransactionSignature: { v: 'u64', @@ -2587,7 +2829,7 @@ s: 'H256' }, /** - * Lookup317: ethereum::transaction::EIP2930Transaction + * Lookup329: ethereum::transaction::EIP2930Transaction **/ EthereumTransactionEip2930Transaction: { chainId: 'u64', @@ -2603,14 +2845,14 @@ s: 'H256' }, /** - * Lookup319: ethereum::transaction::AccessListItem + * Lookup331: ethereum::transaction::AccessListItem **/ EthereumTransactionAccessListItem: { address: 'H160', storageKeys: 'Vec' }, /** - * Lookup320: ethereum::transaction::EIP1559Transaction + * Lookup332: ethereum::transaction::EIP1559Transaction **/ EthereumTransactionEip1559Transaction: { chainId: 'u64', @@ -2627,7 +2869,7 @@ s: 'H256' }, /** - * Lookup321: pallet_evm_migration::pallet::Call + * Lookup333: pallet_evm_migration::pallet::Call **/ PalletEvmMigrationCall: { _enum: { @@ -2645,39 +2887,73 @@ } }, /** - * Lookup324: pallet_sudo::pallet::Error + * Lookup336: pallet_sudo::pallet::Error **/ PalletSudoError: { _enum: ['RequireSudo'] }, /** - * Lookup326: orml_vesting::module::Error + * Lookup338: orml_vesting::module::Error **/ OrmlVestingModuleError: { _enum: ['ZeroVestingPeriod', 'ZeroVestingPeriodCount', 'InsufficientBalanceToLock', 'TooManyVestingSchedules', 'AmountLow', 'MaxVestingSchedulesExceeded'] }, /** - * Lookup328: cumulus_pallet_xcmp_queue::InboundChannelDetails + * Lookup339: orml_xtokens::module::Error + **/ + OrmlXtokensModuleError: { + _enum: ['AssetHasNoReserve', 'NotCrossChainTransfer', 'InvalidDest', 'NotCrossChainTransferableCurrency', 'UnweighableMessage', 'XcmExecutionFailed', 'CannotReanchor', 'InvalidAncestry', 'InvalidAsset', 'DestinationNotInvertible', 'BadVersion', 'DistinctReserveForAssetAndFee', 'ZeroFee', 'ZeroAmount', 'TooManyAssetsBeingSent', 'AssetIndexNonExistent', 'FeeNotEnough', 'NotSupportedMultiLocation', 'MinXcmFeeNotDefined'] + }, + /** + * Lookup342: orml_tokens::BalanceLock + **/ + OrmlTokensBalanceLock: { + id: '[u8;8]', + amount: 'u128' + }, + /** + * Lookup344: orml_tokens::AccountData + **/ + OrmlTokensAccountData: { + free: 'u128', + reserved: 'u128', + frozen: 'u128' + }, + /** + * Lookup346: orml_tokens::ReserveData **/ + OrmlTokensReserveData: { + id: 'Null', + amount: 'u128' + }, + /** + * Lookup348: orml_tokens::module::Error + **/ + OrmlTokensModuleError: { + _enum: ['BalanceTooLow', 'AmountIntoBalanceFailed', 'LiquidityRestrictions', 'MaxLocksExceeded', 'KeepAlive', 'ExistentialDeposit', 'DeadAccount', 'TooManyReserves'] + }, + /** + * Lookup350: cumulus_pallet_xcmp_queue::InboundChannelDetails + **/ CumulusPalletXcmpQueueInboundChannelDetails: { sender: 'u32', state: 'CumulusPalletXcmpQueueInboundState', messageMetadata: 'Vec<(u32,PolkadotParachainPrimitivesXcmpMessageFormat)>' }, /** - * Lookup329: cumulus_pallet_xcmp_queue::InboundState + * Lookup351: cumulus_pallet_xcmp_queue::InboundState **/ CumulusPalletXcmpQueueInboundState: { _enum: ['Ok', 'Suspended'] }, /** - * Lookup332: polkadot_parachain::primitives::XcmpMessageFormat + * Lookup354: polkadot_parachain::primitives::XcmpMessageFormat **/ PolkadotParachainPrimitivesXcmpMessageFormat: { _enum: ['ConcatenatedVersionedXcm', 'ConcatenatedEncodedBlob', 'Signals'] }, /** - * Lookup335: cumulus_pallet_xcmp_queue::OutboundChannelDetails + * Lookup357: cumulus_pallet_xcmp_queue::OutboundChannelDetails **/ CumulusPalletXcmpQueueOutboundChannelDetails: { recipient: 'u32', @@ -2687,13 +2963,13 @@ lastIndex: 'u16' }, /** - * Lookup336: cumulus_pallet_xcmp_queue::OutboundState + * Lookup358: cumulus_pallet_xcmp_queue::OutboundState **/ CumulusPalletXcmpQueueOutboundState: { _enum: ['Ok', 'Suspended'] }, /** - * Lookup338: cumulus_pallet_xcmp_queue::QueueConfigData + * Lookup360: cumulus_pallet_xcmp_queue::QueueConfigData **/ CumulusPalletXcmpQueueQueueConfigData: { suspendThreshold: 'u32', @@ -2704,29 +2980,29 @@ xcmpMaxIndividualWeight: 'u64' }, /** - * Lookup340: cumulus_pallet_xcmp_queue::pallet::Error + * Lookup362: cumulus_pallet_xcmp_queue::pallet::Error **/ CumulusPalletXcmpQueueError: { _enum: ['FailedToSend', 'BadXcmOrigin', 'BadXcm', 'BadOverweightIndex', 'WeightOverLimit'] }, /** - * Lookup341: pallet_xcm::pallet::Error + * Lookup363: pallet_xcm::pallet::Error **/ PalletXcmError: { _enum: ['Unreachable', 'SendFailure', 'Filtered', 'UnweighableMessage', 'DestinationNotInvertible', 'Empty', 'CannotReanchor', 'TooManyAssets', 'InvalidOrigin', 'BadVersion', 'BadLocation', 'NoSubscription', 'AlreadySubscribed'] }, /** - * Lookup342: cumulus_pallet_xcm::pallet::Error + * Lookup364: cumulus_pallet_xcm::pallet::Error **/ CumulusPalletXcmError: 'Null', /** - * Lookup343: cumulus_pallet_dmp_queue::ConfigData + * Lookup365: cumulus_pallet_dmp_queue::ConfigData **/ CumulusPalletDmpQueueConfigData: { maxIndividual: 'u64' }, /** - * Lookup344: cumulus_pallet_dmp_queue::PageIndexData + * Lookup366: cumulus_pallet_dmp_queue::PageIndexData **/ CumulusPalletDmpQueuePageIndexData: { beginUsed: 'u32', @@ -2734,19 +3010,19 @@ overweightCount: 'u64' }, /** - * Lookup347: cumulus_pallet_dmp_queue::pallet::Error + * Lookup369: cumulus_pallet_dmp_queue::pallet::Error **/ CumulusPalletDmpQueueError: { _enum: ['Unknown', 'OverLimit'] }, /** - * Lookup351: pallet_unique::Error + * Lookup373: pallet_unique::Error **/ PalletUniqueError: { _enum: ['CollectionDecimalPointLimitExceeded', 'ConfirmUnsetSponsorFail', 'EmptyArgument', 'RepartitionCalledOnNonRefungibleCollection'] }, /** - * Lookup354: pallet_unique_scheduler::ScheduledV3, BlockNumber, opal_runtime::OriginCaller, sp_core::crypto::AccountId32> + * Lookup376: pallet_unique_scheduler::ScheduledV3, BlockNumber, opal_runtime::OriginCaller, sp_core::crypto::AccountId32> **/ PalletUniqueSchedulerScheduledV3: { maybeId: 'Option<[u8;16]>', @@ -2756,7 +3032,7 @@ origin: 'OpalRuntimeOriginCaller' }, /** - * Lookup355: opal_runtime::OriginCaller + * Lookup377: opal_runtime::OriginCaller **/ OpalRuntimeOriginCaller: { _enum: { @@ -2865,7 +3141,7 @@ } }, /** - * Lookup356: frame_support::dispatch::RawOrigin + * Lookup378: frame_support::dispatch::RawOrigin **/ FrameSupportDispatchRawOrigin: { _enum: { @@ -2875,7 +3151,7 @@ } }, /** - * Lookup357: pallet_xcm::pallet::Origin + * Lookup379: pallet_xcm::pallet::Origin **/ PalletXcmOrigin: { _enum: { @@ -2884,7 +3160,7 @@ } }, /** - * Lookup358: cumulus_pallet_xcm::pallet::Origin + * Lookup380: cumulus_pallet_xcm::pallet::Origin **/ CumulusPalletXcmOrigin: { _enum: { @@ -2893,7 +3169,7 @@ } }, /** - * Lookup359: pallet_ethereum::RawOrigin + * Lookup381: pallet_ethereum::RawOrigin **/ PalletEthereumRawOrigin: { _enum: { @@ -2901,17 +3177,17 @@ } }, /** - * Lookup360: sp_core::Void + * Lookup382: sp_core::Void **/ SpCoreVoid: 'Null', /** - * Lookup361: pallet_unique_scheduler::pallet::Error + * Lookup383: pallet_unique_scheduler::pallet::Error **/ PalletUniqueSchedulerError: { _enum: ['FailedToSchedule', 'NotFound', 'TargetBlockNumberInPast', 'RescheduleNoChange'] }, /** - * Lookup362: up_data_structs::Collection + * Lookup384: up_data_structs::Collection **/ UpDataStructsCollection: { owner: 'AccountId32', @@ -2922,10 +3198,10 @@ sponsorship: 'UpDataStructsSponsorshipStateAccountId32', limits: 'UpDataStructsCollectionLimits', permissions: 'UpDataStructsCollectionPermissions', - externalCollection: 'bool' + flags: '[u8;1]' }, /** - * Lookup363: up_data_structs::SponsorshipState + * Lookup385: up_data_structs::SponsorshipState **/ UpDataStructsSponsorshipStateAccountId32: { _enum: { @@ -2935,7 +3211,7 @@ } }, /** - * Lookup364: up_data_structs::Properties + * Lookup387: up_data_structs::Properties **/ UpDataStructsProperties: { map: 'UpDataStructsPropertiesMapBoundedVec', @@ -2943,15 +3219,15 @@ spaceLimit: 'u32' }, /** - * Lookup365: up_data_structs::PropertiesMap> + * Lookup388: up_data_structs::PropertiesMap> **/ UpDataStructsPropertiesMapBoundedVec: 'BTreeMap', /** - * Lookup370: up_data_structs::PropertiesMap + * Lookup393: up_data_structs::PropertiesMap **/ UpDataStructsPropertiesMapPropertyPermission: 'BTreeMap', /** - * Lookup377: up_data_structs::CollectionStats + * Lookup400: up_data_structs::CollectionStats **/ UpDataStructsCollectionStats: { created: 'u32', @@ -2959,18 +3235,18 @@ alive: 'u32' }, /** - * Lookup378: up_data_structs::TokenChild + * Lookup401: up_data_structs::TokenChild **/ UpDataStructsTokenChild: { token: 'u32', collection: 'u32' }, /** - * Lookup379: PhantomType::up_data_structs + * Lookup402: PhantomType::up_data_structs **/ PhantomTypeUpDataStructs: '[(UpDataStructsTokenData,UpDataStructsRpcCollection,RmrkTraitsCollectionCollectionInfo,RmrkTraitsNftNftInfo,RmrkTraitsResourceResourceInfo,RmrkTraitsPropertyPropertyInfo,RmrkTraitsBaseBaseInfo,RmrkTraitsPartPartType,RmrkTraitsTheme,RmrkTraitsNftNftChild);0]', /** - * Lookup381: up_data_structs::TokenData> + * Lookup404: up_data_structs::TokenData> **/ UpDataStructsTokenData: { properties: 'Vec', @@ -2978,7 +3254,7 @@ pieces: 'u128' }, /** - * Lookup383: up_data_structs::RpcCollection + * Lookup406: up_data_structs::RpcCollection **/ UpDataStructsRpcCollection: { owner: 'AccountId32', @@ -2991,10 +3267,11 @@ permissions: 'UpDataStructsCollectionPermissions', tokenPropertyPermissions: 'Vec', properties: 'Vec', - readOnly: 'bool' + readOnly: 'bool', + foreign: 'bool' }, /** - * Lookup384: rmrk_traits::collection::CollectionInfo, sp_runtime::bounded::bounded_vec::BoundedVec, sp_core::crypto::AccountId32> + * Lookup407: rmrk_traits::collection::CollectionInfo, sp_runtime::bounded::bounded_vec::BoundedVec, sp_core::crypto::AccountId32> **/ RmrkTraitsCollectionCollectionInfo: { issuer: 'AccountId32', @@ -3004,7 +3281,7 @@ nftsCount: 'u32' }, /** - * Lookup385: rmrk_traits::nft::NftInfo> + * Lookup408: rmrk_traits::nft::NftInfo> **/ RmrkTraitsNftNftInfo: { owner: 'RmrkTraitsNftAccountIdOrCollectionNftTuple', @@ -3014,14 +3291,14 @@ pending: 'bool' }, /** - * Lookup387: rmrk_traits::nft::RoyaltyInfo + * Lookup410: rmrk_traits::nft::RoyaltyInfo **/ RmrkTraitsNftRoyaltyInfo: { recipient: 'AccountId32', amount: 'Permill' }, /** - * Lookup388: rmrk_traits::resource::ResourceInfo, sp_runtime::bounded::bounded_vec::BoundedVec> + * Lookup411: rmrk_traits::resource::ResourceInfo, sp_runtime::bounded::bounded_vec::BoundedVec> **/ RmrkTraitsResourceResourceInfo: { id: 'u32', @@ -3030,14 +3307,14 @@ pendingRemoval: 'bool' }, /** - * Lookup389: rmrk_traits::property::PropertyInfo, sp_runtime::bounded::bounded_vec::BoundedVec> + * Lookup412: rmrk_traits::property::PropertyInfo, sp_runtime::bounded::bounded_vec::BoundedVec> **/ RmrkTraitsPropertyPropertyInfo: { key: 'Bytes', value: 'Bytes' }, /** - * Lookup390: rmrk_traits::base::BaseInfo> + * Lookup413: rmrk_traits::base::BaseInfo> **/ RmrkTraitsBaseBaseInfo: { issuer: 'AccountId32', @@ -3045,86 +3322,92 @@ symbol: 'Bytes' }, /** - * Lookup391: rmrk_traits::nft::NftChild + * Lookup414: rmrk_traits::nft::NftChild **/ RmrkTraitsNftNftChild: { collectionId: 'u32', nftId: 'u32' }, /** - * Lookup393: pallet_common::pallet::Error + * Lookup416: pallet_common::pallet::Error **/ PalletCommonError: { _enum: ['CollectionNotFound', 'MustBeTokenOwner', 'NoPermission', 'CantDestroyNotEmptyCollection', 'PublicMintingNotAllowed', 'AddressNotInAllowlist', 'CollectionNameLimitExceeded', 'CollectionDescriptionLimitExceeded', 'CollectionTokenPrefixLimitExceeded', 'TotalCollectionsLimitExceeded', 'CollectionAdminCountExceeded', 'CollectionLimitBoundsExceeded', 'OwnerPermissionsCantBeReverted', 'TransferNotAllowed', 'AccountTokenLimitExceeded', 'CollectionTokenLimitExceeded', 'MetadataFlagFrozen', 'TokenNotFound', 'TokenValueTooLow', 'ApprovedValueTooLow', 'CantApproveMoreThanOwned', 'AddressIsZero', 'UnsupportedOperation', 'NotSufficientFounds', 'UserIsNotAllowedToNest', 'SourceCollectionIsNotAllowedToNest', 'CollectionFieldSizeExceeded', 'NoSpaceForProperty', 'PropertyLimitReached', 'PropertyKeyIsTooLong', 'InvalidCharacterInPropertyKey', 'EmptyPropertyKey', 'CollectionIsExternal', 'CollectionIsInternal'] }, /** - * Lookup395: pallet_fungible::pallet::Error + * Lookup418: pallet_fungible::pallet::Error **/ PalletFungibleError: { _enum: ['NotFungibleDataUsedToMintFungibleCollectionToken', 'FungibleItemsHaveNoId', 'FungibleItemsDontHaveData', 'FungibleDisallowsNesting', 'SettingPropertiesNotAllowed'] }, /** - * Lookup396: pallet_refungible::ItemData + * Lookup419: pallet_refungible::ItemData **/ PalletRefungibleItemData: { constData: 'Bytes' }, /** - * Lookup401: pallet_refungible::pallet::Error + * Lookup424: pallet_refungible::pallet::Error **/ PalletRefungibleError: { _enum: ['NotRefungibleDataUsedToMintFungibleCollectionToken', 'WrongRefungiblePieces', 'RepartitionWhileNotOwningAllPieces', 'RefungibleDisallowsNesting', 'SettingPropertiesNotAllowed'] }, /** - * Lookup402: pallet_nonfungible::ItemData> + * Lookup425: pallet_nonfungible::ItemData> **/ PalletNonfungibleItemData: { owner: 'PalletEvmAccountBasicCrossAccountIdRepr' }, /** - * Lookup404: up_data_structs::PropertyScope + * Lookup427: up_data_structs::PropertyScope **/ UpDataStructsPropertyScope: { _enum: ['None', 'Rmrk'] }, /** - * Lookup406: pallet_nonfungible::pallet::Error + * Lookup429: pallet_nonfungible::pallet::Error **/ PalletNonfungibleError: { _enum: ['NotNonfungibleDataUsedToMintFungibleCollectionToken', 'NonfungibleItemsHaveNoAmount', 'CantBurnNftWithChildren'] }, /** - * Lookup407: pallet_structure::pallet::Error + * Lookup430: pallet_structure::pallet::Error **/ PalletStructureError: { _enum: ['OuroborosDetected', 'DepthLimit', 'BreadthLimit', 'TokenNotFound'] }, /** - * Lookup408: pallet_rmrk_core::pallet::Error + * Lookup431: pallet_rmrk_core::pallet::Error **/ PalletRmrkCoreError: { _enum: ['CorruptedCollectionType', 'RmrkPropertyKeyIsTooLong', 'RmrkPropertyValueIsTooLong', 'RmrkPropertyIsNotFound', 'UnableToDecodeRmrkData', 'CollectionNotEmpty', 'NoAvailableCollectionId', 'NoAvailableNftId', 'CollectionUnknown', 'NoPermission', 'NonTransferable', 'CollectionFullOrLocked', 'ResourceDoesntExist', 'CannotSendToDescendentOrSelf', 'CannotAcceptNonOwnedNft', 'CannotRejectNonOwnedNft', 'CannotRejectNonPendingNft', 'ResourceNotPending', 'NoAvailableResourceId'] }, /** - * Lookup410: pallet_rmrk_equip::pallet::Error + * Lookup433: pallet_rmrk_equip::pallet::Error **/ PalletRmrkEquipError: { _enum: ['PermissionError', 'NoAvailableBaseId', 'NoAvailablePartId', 'BaseDoesntExist', 'NeedsDefaultThemeFirst', 'PartDoesntExist', 'NoEquippableOnFixedPart'] }, /** - * Lookup416: pallet_app_promotion::pallet::Error + * Lookup439: pallet_app_promotion::pallet::Error **/ PalletAppPromotionError: { _enum: ['AdminNotSet', 'NoPermission', 'NotSufficientFunds', 'PendingForBlockOverflow', 'SponsorNotSet', 'IncorrectLockedBalanceOperation'] }, /** - * Lookup419: pallet_evm::pallet::Error + * Lookup440: pallet_foreign_assets::module::Error + **/ + PalletForeignAssetsModuleError: { + _enum: ['BadLocation', 'MultiLocationExisted', 'AssetIdNotExists', 'AssetIdExisted'] + }, + /** + * Lookup443: pallet_evm::pallet::Error **/ PalletEvmError: { _enum: ['BalanceLow', 'FeeOverflow', 'PaymentOverflow', 'WithdrawFailed', 'GasPriceTooLow', 'InvalidNonce'] }, /** - * Lookup422: fp_rpc::TransactionStatus + * Lookup446: fp_rpc::TransactionStatus **/ FpRpcTransactionStatus: { transactionHash: 'H256', @@ -3136,11 +3419,11 @@ logsBloom: 'EthbloomBloom' }, /** - * Lookup424: ethbloom::Bloom + * Lookup448: ethbloom::Bloom **/ EthbloomBloom: '[u8;256]', /** - * Lookup426: ethereum::receipt::ReceiptV3 + * Lookup450: ethereum::receipt::ReceiptV3 **/ EthereumReceiptReceiptV3: { _enum: { @@ -3150,7 +3433,7 @@ } }, /** - * Lookup427: ethereum::receipt::EIP658ReceiptData + * Lookup451: ethereum::receipt::EIP658ReceiptData **/ EthereumReceiptEip658ReceiptData: { statusCode: 'u8', @@ -3159,7 +3442,7 @@ logs: 'Vec' }, /** - * Lookup428: ethereum::block::Block + * Lookup452: ethereum::block::Block **/ EthereumBlock: { header: 'EthereumHeader', @@ -3167,7 +3450,7 @@ ommers: 'Vec' }, /** - * Lookup429: ethereum::header::Header + * Lookup453: ethereum::header::Header **/ EthereumHeader: { parentHash: 'H256', @@ -3187,23 +3470,23 @@ nonce: 'EthereumTypesHashH64' }, /** - * Lookup430: ethereum_types::hash::H64 + * Lookup454: ethereum_types::hash::H64 **/ EthereumTypesHashH64: '[u8;8]', /** - * Lookup435: pallet_ethereum::pallet::Error + * Lookup459: pallet_ethereum::pallet::Error **/ PalletEthereumError: { _enum: ['InvalidSignature', 'PreLogExists'] }, /** - * Lookup436: pallet_evm_coder_substrate::pallet::Error + * Lookup460: pallet_evm_coder_substrate::pallet::Error **/ PalletEvmCoderSubstrateError: { _enum: ['OutOfGas', 'OutOfFund'] }, /** - * Lookup437: up_data_structs::SponsorshipState> + * Lookup461: up_data_structs::SponsorshipState> **/ UpDataStructsSponsorshipStateBasicCrossAccountIdRepr: { _enum: { @@ -3213,25 +3496,25 @@ } }, /** - * Lookup438: pallet_evm_contract_helpers::SponsoringModeT + * Lookup462: pallet_evm_contract_helpers::SponsoringModeT **/ PalletEvmContractHelpersSponsoringModeT: { _enum: ['Disabled', 'Allowlisted', 'Generous'] }, /** - * Lookup440: pallet_evm_contract_helpers::pallet::Error + * Lookup468: pallet_evm_contract_helpers::pallet::Error **/ PalletEvmContractHelpersError: { - _enum: ['NoPermission', 'NoPendingSponsor'] + _enum: ['NoPermission', 'NoPendingSponsor', 'TooManyMethodsHaveSponsoredLimit'] }, /** - * Lookup441: pallet_evm_migration::pallet::Error + * Lookup469: pallet_evm_migration::pallet::Error **/ PalletEvmMigrationError: { _enum: ['AccountNotEmpty', 'AccountIsNotMigrating'] }, /** - * Lookup443: sp_runtime::MultiSignature + * Lookup471: sp_runtime::MultiSignature **/ SpRuntimeMultiSignature: { _enum: { @@ -3241,43 +3524,43 @@ } }, /** - * Lookup444: sp_core::ed25519::Signature + * Lookup472: sp_core::ed25519::Signature **/ SpCoreEd25519Signature: '[u8;64]', /** - * Lookup446: sp_core::sr25519::Signature + * Lookup474: sp_core::sr25519::Signature **/ SpCoreSr25519Signature: '[u8;64]', /** - * Lookup447: sp_core::ecdsa::Signature + * Lookup475: sp_core::ecdsa::Signature **/ SpCoreEcdsaSignature: '[u8;65]', /** - * Lookup450: frame_system::extensions::check_spec_version::CheckSpecVersion + * Lookup478: frame_system::extensions::check_spec_version::CheckSpecVersion **/ FrameSystemExtensionsCheckSpecVersion: 'Null', /** - * Lookup451: frame_system::extensions::check_genesis::CheckGenesis + * Lookup479: frame_system::extensions::check_genesis::CheckGenesis **/ FrameSystemExtensionsCheckGenesis: 'Null', /** - * Lookup454: frame_system::extensions::check_nonce::CheckNonce + * Lookup482: frame_system::extensions::check_nonce::CheckNonce **/ FrameSystemExtensionsCheckNonce: 'Compact', /** - * Lookup455: frame_system::extensions::check_weight::CheckWeight + * Lookup483: frame_system::extensions::check_weight::CheckWeight **/ FrameSystemExtensionsCheckWeight: 'Null', /** - * Lookup456: pallet_template_transaction_payment::ChargeTransactionPayment + * Lookup484: pallet_template_transaction_payment::ChargeTransactionPayment **/ PalletTemplateTransactionPaymentChargeTransactionPayment: 'Compact', /** - * Lookup457: opal_runtime::Runtime + * Lookup485: opal_runtime::Runtime **/ OpalRuntimeRuntime: 'Null', /** - * Lookup458: pallet_ethereum::FakeTransactionFinalizer + * Lookup486: pallet_ethereum::FakeTransactionFinalizer **/ PalletEthereumFakeTransactionFinalizer: 'Null' }; --- a/tests/src/interfaces/registry.ts +++ b/tests/src/interfaces/registry.ts @@ -5,7 +5,7 @@ // this is required to allow for ambient/previous definitions import '@polkadot/types/types/registry'; -import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletAppPromotionCall, PalletAppPromotionError, PalletAppPromotionEvent, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletConfigurationCall, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersEvent, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUniqueSchedulerCall, PalletUniqueSchedulerError, PalletUniqueSchedulerEvent, PalletUniqueSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipStateAccountId32, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup'; +import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensModuleCall, OrmlTokensModuleError, OrmlTokensModuleEvent, OrmlTokensReserveData, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, OrmlXtokensModuleCall, OrmlXtokensModuleError, OrmlXtokensModuleEvent, PalletAppPromotionCall, PalletAppPromotionError, PalletAppPromotionEvent, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletConfigurationCall, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersEvent, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletForeignAssetsAssetIds, PalletForeignAssetsModuleAssetMetadata, PalletForeignAssetsModuleCall, PalletForeignAssetsModuleError, PalletForeignAssetsModuleEvent, PalletForeignAssetsNativeCurrency, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUniqueSchedulerCall, PalletUniqueSchedulerError, PalletUniqueSchedulerEvent, PalletUniqueSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipStateAccountId32, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAsset, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup'; declare module '@polkadot/types/types/registry' { interface InterfaceTypes { @@ -79,10 +79,19 @@ FrameSystemPhase: FrameSystemPhase; OpalRuntimeOriginCaller: OpalRuntimeOriginCaller; OpalRuntimeRuntime: OpalRuntimeRuntime; + OrmlTokensAccountData: OrmlTokensAccountData; + OrmlTokensBalanceLock: OrmlTokensBalanceLock; + OrmlTokensModuleCall: OrmlTokensModuleCall; + OrmlTokensModuleError: OrmlTokensModuleError; + OrmlTokensModuleEvent: OrmlTokensModuleEvent; + OrmlTokensReserveData: OrmlTokensReserveData; OrmlVestingModuleCall: OrmlVestingModuleCall; OrmlVestingModuleError: OrmlVestingModuleError; OrmlVestingModuleEvent: OrmlVestingModuleEvent; OrmlVestingVestingSchedule: OrmlVestingVestingSchedule; + OrmlXtokensModuleCall: OrmlXtokensModuleCall; + OrmlXtokensModuleError: OrmlXtokensModuleError; + OrmlXtokensModuleEvent: OrmlXtokensModuleEvent; PalletAppPromotionCall: PalletAppPromotionCall; PalletAppPromotionError: PalletAppPromotionError; PalletAppPromotionEvent: PalletAppPromotionEvent; @@ -112,6 +121,12 @@ PalletEvmEvent: PalletEvmEvent; PalletEvmMigrationCall: PalletEvmMigrationCall; PalletEvmMigrationError: PalletEvmMigrationError; + PalletForeignAssetsAssetIds: PalletForeignAssetsAssetIds; + PalletForeignAssetsModuleAssetMetadata: PalletForeignAssetsModuleAssetMetadata; + PalletForeignAssetsModuleCall: PalletForeignAssetsModuleCall; + PalletForeignAssetsModuleError: PalletForeignAssetsModuleError; + PalletForeignAssetsModuleEvent: PalletForeignAssetsModuleEvent; + PalletForeignAssetsNativeCurrency: PalletForeignAssetsNativeCurrency; PalletFungibleError: PalletFungibleError; PalletInflationCall: PalletInflationCall; PalletNonfungibleError: PalletNonfungibleError; @@ -252,6 +267,7 @@ XcmV2TraitsOutcome: XcmV2TraitsOutcome; XcmV2WeightLimit: XcmV2WeightLimit; XcmV2Xcm: XcmV2Xcm; + XcmVersionedMultiAsset: XcmVersionedMultiAsset; XcmVersionedMultiAssets: XcmVersionedMultiAssets; XcmVersionedMultiLocation: XcmVersionedMultiLocation; XcmVersionedXcm: XcmVersionedXcm; --- a/tests/src/interfaces/types-lookup.ts +++ b/tests/src/interfaces/types-lookup.ts @@ -351,7 +351,279 @@ readonly perPeriod: Compact; } - /** @name CumulusPalletXcmpQueueEvent (39) */ + /** @name OrmlXtokensModuleEvent (39) */ + interface OrmlXtokensModuleEvent extends Enum { + readonly isTransferredMultiAssets: boolean; + readonly asTransferredMultiAssets: { + readonly sender: AccountId32; + readonly assets: XcmV1MultiassetMultiAssets; + readonly fee: XcmV1MultiAsset; + readonly dest: XcmV1MultiLocation; + } & Struct; + readonly type: 'TransferredMultiAssets'; + } + + /** @name XcmV1MultiassetMultiAssets (40) */ + interface XcmV1MultiassetMultiAssets extends Vec {} + + /** @name XcmV1MultiAsset (42) */ + interface XcmV1MultiAsset extends Struct { + readonly id: XcmV1MultiassetAssetId; + readonly fun: XcmV1MultiassetFungibility; + } + + /** @name XcmV1MultiassetAssetId (43) */ + interface XcmV1MultiassetAssetId extends Enum { + readonly isConcrete: boolean; + readonly asConcrete: XcmV1MultiLocation; + readonly isAbstract: boolean; + readonly asAbstract: Bytes; + readonly type: 'Concrete' | 'Abstract'; + } + + /** @name XcmV1MultiLocation (44) */ + interface XcmV1MultiLocation extends Struct { + readonly parents: u8; + readonly interior: XcmV1MultilocationJunctions; + } + + /** @name XcmV1MultilocationJunctions (45) */ + interface XcmV1MultilocationJunctions extends Enum { + readonly isHere: boolean; + readonly isX1: boolean; + readonly asX1: XcmV1Junction; + readonly isX2: boolean; + readonly asX2: ITuple<[XcmV1Junction, XcmV1Junction]>; + readonly isX3: boolean; + readonly asX3: ITuple<[XcmV1Junction, XcmV1Junction, XcmV1Junction]>; + readonly isX4: boolean; + readonly asX4: ITuple<[XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction]>; + readonly isX5: boolean; + readonly asX5: ITuple<[XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction]>; + readonly isX6: boolean; + readonly asX6: ITuple<[XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction]>; + readonly isX7: boolean; + readonly asX7: ITuple<[XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction]>; + readonly isX8: boolean; + readonly asX8: ITuple<[XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction]>; + readonly type: 'Here' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8'; + } + + /** @name XcmV1Junction (46) */ + interface XcmV1Junction extends Enum { + readonly isParachain: boolean; + readonly asParachain: Compact; + readonly isAccountId32: boolean; + readonly asAccountId32: { + readonly network: XcmV0JunctionNetworkId; + readonly id: U8aFixed; + } & Struct; + readonly isAccountIndex64: boolean; + readonly asAccountIndex64: { + readonly network: XcmV0JunctionNetworkId; + readonly index: Compact; + } & Struct; + readonly isAccountKey20: boolean; + readonly asAccountKey20: { + readonly network: XcmV0JunctionNetworkId; + readonly key: U8aFixed; + } & Struct; + readonly isPalletInstance: boolean; + readonly asPalletInstance: u8; + readonly isGeneralIndex: boolean; + readonly asGeneralIndex: Compact; + readonly isGeneralKey: boolean; + readonly asGeneralKey: Bytes; + readonly isOnlyChild: boolean; + readonly isPlurality: boolean; + readonly asPlurality: { + readonly id: XcmV0JunctionBodyId; + readonly part: XcmV0JunctionBodyPart; + } & Struct; + readonly type: 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality'; + } + + /** @name XcmV0JunctionNetworkId (48) */ + interface XcmV0JunctionNetworkId extends Enum { + readonly isAny: boolean; + readonly isNamed: boolean; + readonly asNamed: Bytes; + readonly isPolkadot: boolean; + readonly isKusama: boolean; + readonly type: 'Any' | 'Named' | 'Polkadot' | 'Kusama'; + } + + /** @name XcmV0JunctionBodyId (52) */ + interface XcmV0JunctionBodyId extends Enum { + readonly isUnit: boolean; + readonly isNamed: boolean; + readonly asNamed: Bytes; + readonly isIndex: boolean; + readonly asIndex: Compact; + readonly isExecutive: boolean; + readonly isTechnical: boolean; + readonly isLegislative: boolean; + readonly isJudicial: boolean; + readonly type: 'Unit' | 'Named' | 'Index' | 'Executive' | 'Technical' | 'Legislative' | 'Judicial'; + } + + /** @name XcmV0JunctionBodyPart (53) */ + interface XcmV0JunctionBodyPart extends Enum { + readonly isVoice: boolean; + readonly isMembers: boolean; + readonly asMembers: { + readonly count: Compact; + } & Struct; + readonly isFraction: boolean; + readonly asFraction: { + readonly nom: Compact; + readonly denom: Compact; + } & Struct; + readonly isAtLeastProportion: boolean; + readonly asAtLeastProportion: { + readonly nom: Compact; + readonly denom: Compact; + } & Struct; + readonly isMoreThanProportion: boolean; + readonly asMoreThanProportion: { + readonly nom: Compact; + readonly denom: Compact; + } & Struct; + readonly type: 'Voice' | 'Members' | 'Fraction' | 'AtLeastProportion' | 'MoreThanProportion'; + } + + /** @name XcmV1MultiassetFungibility (54) */ + interface XcmV1MultiassetFungibility extends Enum { + readonly isFungible: boolean; + readonly asFungible: Compact; + readonly isNonFungible: boolean; + readonly asNonFungible: XcmV1MultiassetAssetInstance; + readonly type: 'Fungible' | 'NonFungible'; + } + + /** @name XcmV1MultiassetAssetInstance (55) */ + interface XcmV1MultiassetAssetInstance extends Enum { + readonly isUndefined: boolean; + readonly isIndex: boolean; + readonly asIndex: Compact; + readonly isArray4: boolean; + readonly asArray4: U8aFixed; + readonly isArray8: boolean; + readonly asArray8: U8aFixed; + readonly isArray16: boolean; + readonly asArray16: U8aFixed; + readonly isArray32: boolean; + readonly asArray32: U8aFixed; + readonly isBlob: boolean; + readonly asBlob: Bytes; + readonly type: 'Undefined' | 'Index' | 'Array4' | 'Array8' | 'Array16' | 'Array32' | 'Blob'; + } + + /** @name OrmlTokensModuleEvent (58) */ + interface OrmlTokensModuleEvent extends Enum { + readonly isEndowed: boolean; + readonly asEndowed: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isDustLost: boolean; + readonly asDustLost: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isTransfer: boolean; + readonly asTransfer: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly from: AccountId32; + readonly to: AccountId32; + readonly amount: u128; + } & Struct; + readonly isReserved: boolean; + readonly asReserved: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isUnreserved: boolean; + readonly asUnreserved: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isReserveRepatriated: boolean; + readonly asReserveRepatriated: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly from: AccountId32; + readonly to: AccountId32; + readonly amount: u128; + readonly status: FrameSupportTokensMiscBalanceStatus; + } & Struct; + readonly isBalanceSet: boolean; + readonly asBalanceSet: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly who: AccountId32; + readonly free: u128; + readonly reserved: u128; + } & Struct; + readonly isTotalIssuanceSet: boolean; + readonly asTotalIssuanceSet: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly amount: u128; + } & Struct; + readonly isWithdrawn: boolean; + readonly asWithdrawn: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isSlashed: boolean; + readonly asSlashed: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly who: AccountId32; + readonly freeAmount: u128; + readonly reservedAmount: u128; + } & Struct; + readonly isDeposited: boolean; + readonly asDeposited: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isLockSet: boolean; + readonly asLockSet: { + readonly lockId: U8aFixed; + readonly currencyId: PalletForeignAssetsAssetIds; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isLockRemoved: boolean; + readonly asLockRemoved: { + readonly lockId: U8aFixed; + readonly currencyId: PalletForeignAssetsAssetIds; + readonly who: AccountId32; + } & Struct; + readonly type: 'Endowed' | 'DustLost' | 'Transfer' | 'Reserved' | 'Unreserved' | 'ReserveRepatriated' | 'BalanceSet' | 'TotalIssuanceSet' | 'Withdrawn' | 'Slashed' | 'Deposited' | 'LockSet' | 'LockRemoved'; + } + + /** @name PalletForeignAssetsAssetIds (59) */ + interface PalletForeignAssetsAssetIds extends Enum { + readonly isForeignAssetId: boolean; + readonly asForeignAssetId: u32; + readonly isNativeAssetId: boolean; + readonly asNativeAssetId: PalletForeignAssetsNativeCurrency; + readonly type: 'ForeignAssetId' | 'NativeAssetId'; + } + + /** @name PalletForeignAssetsNativeCurrency (60) */ + interface PalletForeignAssetsNativeCurrency extends Enum { + readonly isHere: boolean; + readonly isParent: boolean; + readonly type: 'Here' | 'Parent'; + } + + /** @name CumulusPalletXcmpQueueEvent (61) */ interface CumulusPalletXcmpQueueEvent extends Enum { readonly isSuccess: boolean; readonly asSuccess: { @@ -395,7 +667,7 @@ readonly type: 'Success' | 'Fail' | 'BadVersion' | 'BadFormat' | 'UpwardMessageSent' | 'XcmpMessageSent' | 'OverweightEnqueued' | 'OverweightServiced'; } - /** @name XcmV2TraitsError (41) */ + /** @name XcmV2TraitsError (63) */ interface XcmV2TraitsError extends Enum { readonly isOverflow: boolean; readonly isUnimplemented: boolean; @@ -428,7 +700,7 @@ readonly type: 'Overflow' | 'Unimplemented' | 'UntrustedReserveLocation' | 'UntrustedTeleportLocation' | 'MultiLocationFull' | 'MultiLocationNotInvertible' | 'BadOrigin' | 'InvalidLocation' | 'AssetNotFound' | 'FailedToTransactAsset' | 'NotWithdrawable' | 'LocationCannotHold' | 'ExceedsMaxMessageSize' | 'DestinationUnsupported' | 'Transport' | 'Unroutable' | 'UnknownClaim' | 'FailedToDecode' | 'MaxWeightInvalid' | 'NotHoldingFees' | 'TooExpensive' | 'Trap' | 'UnhandledXcmVersion' | 'WeightLimitReached' | 'Barrier' | 'WeightNotComputable'; } - /** @name PalletXcmEvent (43) */ + /** @name PalletXcmEvent (65) */ interface PalletXcmEvent extends Enum { readonly isAttempted: boolean; readonly asAttempted: XcmV2TraitsOutcome; @@ -465,7 +737,7 @@ readonly type: 'Attempted' | 'Sent' | 'UnexpectedResponse' | 'ResponseReady' | 'Notified' | 'NotifyOverweight' | 'NotifyDispatchError' | 'NotifyDecodeFailed' | 'InvalidResponder' | 'InvalidResponderVersion' | 'ResponseTaken' | 'AssetsTrapped' | 'VersionChangeNotified' | 'SupportedVersionChanged' | 'NotifyTargetSendFail' | 'NotifyTargetMigrationFail'; } - /** @name XcmV2TraitsOutcome (44) */ + /** @name XcmV2TraitsOutcome (66) */ interface XcmV2TraitsOutcome extends Enum { readonly isComplete: boolean; readonly asComplete: u64; @@ -474,123 +746,12 @@ readonly isError: boolean; readonly asError: XcmV2TraitsError; readonly type: 'Complete' | 'Incomplete' | 'Error'; - } - - /** @name XcmV1MultiLocation (45) */ - interface XcmV1MultiLocation extends Struct { - readonly parents: u8; - readonly interior: XcmV1MultilocationJunctions; } - /** @name XcmV1MultilocationJunctions (46) */ - interface XcmV1MultilocationJunctions extends Enum { - readonly isHere: boolean; - readonly isX1: boolean; - readonly asX1: XcmV1Junction; - readonly isX2: boolean; - readonly asX2: ITuple<[XcmV1Junction, XcmV1Junction]>; - readonly isX3: boolean; - readonly asX3: ITuple<[XcmV1Junction, XcmV1Junction, XcmV1Junction]>; - readonly isX4: boolean; - readonly asX4: ITuple<[XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction]>; - readonly isX5: boolean; - readonly asX5: ITuple<[XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction]>; - readonly isX6: boolean; - readonly asX6: ITuple<[XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction]>; - readonly isX7: boolean; - readonly asX7: ITuple<[XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction]>; - readonly isX8: boolean; - readonly asX8: ITuple<[XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction, XcmV1Junction]>; - readonly type: 'Here' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8'; - } - - /** @name XcmV1Junction (47) */ - interface XcmV1Junction extends Enum { - readonly isParachain: boolean; - readonly asParachain: Compact; - readonly isAccountId32: boolean; - readonly asAccountId32: { - readonly network: XcmV0JunctionNetworkId; - readonly id: U8aFixed; - } & Struct; - readonly isAccountIndex64: boolean; - readonly asAccountIndex64: { - readonly network: XcmV0JunctionNetworkId; - readonly index: Compact; - } & Struct; - readonly isAccountKey20: boolean; - readonly asAccountKey20: { - readonly network: XcmV0JunctionNetworkId; - readonly key: U8aFixed; - } & Struct; - readonly isPalletInstance: boolean; - readonly asPalletInstance: u8; - readonly isGeneralIndex: boolean; - readonly asGeneralIndex: Compact; - readonly isGeneralKey: boolean; - readonly asGeneralKey: Bytes; - readonly isOnlyChild: boolean; - readonly isPlurality: boolean; - readonly asPlurality: { - readonly id: XcmV0JunctionBodyId; - readonly part: XcmV0JunctionBodyPart; - } & Struct; - readonly type: 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality'; - } - - /** @name XcmV0JunctionNetworkId (49) */ - interface XcmV0JunctionNetworkId extends Enum { - readonly isAny: boolean; - readonly isNamed: boolean; - readonly asNamed: Bytes; - readonly isPolkadot: boolean; - readonly isKusama: boolean; - readonly type: 'Any' | 'Named' | 'Polkadot' | 'Kusama'; - } - - /** @name XcmV0JunctionBodyId (53) */ - interface XcmV0JunctionBodyId extends Enum { - readonly isUnit: boolean; - readonly isNamed: boolean; - readonly asNamed: Bytes; - readonly isIndex: boolean; - readonly asIndex: Compact; - readonly isExecutive: boolean; - readonly isTechnical: boolean; - readonly isLegislative: boolean; - readonly isJudicial: boolean; - readonly type: 'Unit' | 'Named' | 'Index' | 'Executive' | 'Technical' | 'Legislative' | 'Judicial'; - } - - /** @name XcmV0JunctionBodyPart (54) */ - interface XcmV0JunctionBodyPart extends Enum { - readonly isVoice: boolean; - readonly isMembers: boolean; - readonly asMembers: { - readonly count: Compact; - } & Struct; - readonly isFraction: boolean; - readonly asFraction: { - readonly nom: Compact; - readonly denom: Compact; - } & Struct; - readonly isAtLeastProportion: boolean; - readonly asAtLeastProportion: { - readonly nom: Compact; - readonly denom: Compact; - } & Struct; - readonly isMoreThanProportion: boolean; - readonly asMoreThanProportion: { - readonly nom: Compact; - readonly denom: Compact; - } & Struct; - readonly type: 'Voice' | 'Members' | 'Fraction' | 'AtLeastProportion' | 'MoreThanProportion'; - } - - /** @name XcmV2Xcm (55) */ + /** @name XcmV2Xcm (67) */ interface XcmV2Xcm extends Vec {} - /** @name XcmV2Instruction (57) */ + /** @name XcmV2Instruction (69) */ interface XcmV2Instruction extends Enum { readonly isWithdrawAsset: boolean; readonly asWithdrawAsset: XcmV1MultiassetMultiAssets; @@ -709,53 +870,8 @@ readonly isUnsubscribeVersion: boolean; readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'ClearOrigin' | 'DescendOrigin' | 'ReportError' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution' | 'RefundSurplus' | 'SetErrorHandler' | 'SetAppendix' | 'ClearError' | 'ClaimAsset' | 'Trap' | 'SubscribeVersion' | 'UnsubscribeVersion'; } - - /** @name XcmV1MultiassetMultiAssets (58) */ - interface XcmV1MultiassetMultiAssets extends Vec {} - /** @name XcmV1MultiAsset (60) */ - interface XcmV1MultiAsset extends Struct { - readonly id: XcmV1MultiassetAssetId; - readonly fun: XcmV1MultiassetFungibility; - } - - /** @name XcmV1MultiassetAssetId (61) */ - interface XcmV1MultiassetAssetId extends Enum { - readonly isConcrete: boolean; - readonly asConcrete: XcmV1MultiLocation; - readonly isAbstract: boolean; - readonly asAbstract: Bytes; - readonly type: 'Concrete' | 'Abstract'; - } - - /** @name XcmV1MultiassetFungibility (62) */ - interface XcmV1MultiassetFungibility extends Enum { - readonly isFungible: boolean; - readonly asFungible: Compact; - readonly isNonFungible: boolean; - readonly asNonFungible: XcmV1MultiassetAssetInstance; - readonly type: 'Fungible' | 'NonFungible'; - } - - /** @name XcmV1MultiassetAssetInstance (63) */ - interface XcmV1MultiassetAssetInstance extends Enum { - readonly isUndefined: boolean; - readonly isIndex: boolean; - readonly asIndex: Compact; - readonly isArray4: boolean; - readonly asArray4: U8aFixed; - readonly isArray8: boolean; - readonly asArray8: U8aFixed; - readonly isArray16: boolean; - readonly asArray16: U8aFixed; - readonly isArray32: boolean; - readonly asArray32: U8aFixed; - readonly isBlob: boolean; - readonly asBlob: Bytes; - readonly type: 'Undefined' | 'Index' | 'Array4' | 'Array8' | 'Array16' | 'Array32' | 'Blob'; - } - - /** @name XcmV2Response (66) */ + /** @name XcmV2Response (70) */ interface XcmV2Response extends Enum { readonly isNull: boolean; readonly isAssets: boolean; @@ -767,7 +883,7 @@ readonly type: 'Null' | 'Assets' | 'ExecutionResult' | 'Version'; } - /** @name XcmV0OriginKind (69) */ + /** @name XcmV0OriginKind (73) */ interface XcmV0OriginKind extends Enum { readonly isNative: boolean; readonly isSovereignAccount: boolean; @@ -776,12 +892,12 @@ readonly type: 'Native' | 'SovereignAccount' | 'Superuser' | 'Xcm'; } - /** @name XcmDoubleEncoded (70) */ + /** @name XcmDoubleEncoded (74) */ interface XcmDoubleEncoded extends Struct { readonly encoded: Bytes; } - /** @name XcmV1MultiassetMultiAssetFilter (71) */ + /** @name XcmV1MultiassetMultiAssetFilter (75) */ interface XcmV1MultiassetMultiAssetFilter extends Enum { readonly isDefinite: boolean; readonly asDefinite: XcmV1MultiassetMultiAssets; @@ -790,7 +906,7 @@ readonly type: 'Definite' | 'Wild'; } - /** @name XcmV1MultiassetWildMultiAsset (72) */ + /** @name XcmV1MultiassetWildMultiAsset (76) */ interface XcmV1MultiassetWildMultiAsset extends Enum { readonly isAll: boolean; readonly isAllOf: boolean; @@ -801,14 +917,14 @@ readonly type: 'All' | 'AllOf'; } - /** @name XcmV1MultiassetWildFungibility (73) */ + /** @name XcmV1MultiassetWildFungibility (77) */ interface XcmV1MultiassetWildFungibility extends Enum { readonly isFungible: boolean; readonly isNonFungible: boolean; readonly type: 'Fungible' | 'NonFungible'; } - /** @name XcmV2WeightLimit (74) */ + /** @name XcmV2WeightLimit (78) */ interface XcmV2WeightLimit extends Enum { readonly isUnlimited: boolean; readonly isLimited: boolean; @@ -816,7 +932,7 @@ readonly type: 'Unlimited' | 'Limited'; } - /** @name XcmVersionedMultiAssets (76) */ + /** @name XcmVersionedMultiAssets (80) */ interface XcmVersionedMultiAssets extends Enum { readonly isV0: boolean; readonly asV0: Vec; @@ -825,7 +941,7 @@ readonly type: 'V0' | 'V1'; } - /** @name XcmV0MultiAsset (78) */ + /** @name XcmV0MultiAsset (82) */ interface XcmV0MultiAsset extends Enum { readonly isNone: boolean; readonly isAll: boolean; @@ -870,7 +986,7 @@ readonly type: 'None' | 'All' | 'AllFungible' | 'AllNonFungible' | 'AllAbstractFungible' | 'AllAbstractNonFungible' | 'AllConcreteFungible' | 'AllConcreteNonFungible' | 'AbstractFungible' | 'AbstractNonFungible' | 'ConcreteFungible' | 'ConcreteNonFungible'; } - /** @name XcmV0MultiLocation (79) */ + /** @name XcmV0MultiLocation (83) */ interface XcmV0MultiLocation extends Enum { readonly isNull: boolean; readonly isX1: boolean; @@ -892,7 +1008,7 @@ readonly type: 'Null' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8'; } - /** @name XcmV0Junction (80) */ + /** @name XcmV0Junction (84) */ interface XcmV0Junction extends Enum { readonly isParent: boolean; readonly isParachain: boolean; @@ -927,7 +1043,7 @@ readonly type: 'Parent' | 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality'; } - /** @name XcmVersionedMultiLocation (81) */ + /** @name XcmVersionedMultiLocation (85) */ interface XcmVersionedMultiLocation extends Enum { readonly isV0: boolean; readonly asV0: XcmV0MultiLocation; @@ -936,7 +1052,7 @@ readonly type: 'V0' | 'V1'; } - /** @name CumulusPalletXcmEvent (82) */ + /** @name CumulusPalletXcmEvent (86) */ interface CumulusPalletXcmEvent extends Enum { readonly isInvalidFormat: boolean; readonly asInvalidFormat: U8aFixed; @@ -947,7 +1063,7 @@ readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward'; } - /** @name CumulusPalletDmpQueueEvent (83) */ + /** @name CumulusPalletDmpQueueEvent (87) */ interface CumulusPalletDmpQueueEvent extends Enum { readonly isInvalidFormat: boolean; readonly asInvalidFormat: { @@ -982,7 +1098,7 @@ readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward' | 'WeightExhausted' | 'OverweightEnqueued' | 'OverweightServiced'; } - /** @name PalletUniqueRawEvent (84) */ + /** @name PalletUniqueRawEvent (88) */ interface PalletUniqueRawEvent extends Enum { readonly isCollectionSponsorRemoved: boolean; readonly asCollectionSponsorRemoved: u32; @@ -1007,7 +1123,7 @@ readonly type: 'CollectionSponsorRemoved' | 'CollectionAdminAdded' | 'CollectionOwnedChanged' | 'CollectionSponsorSet' | 'SponsorshipConfirmed' | 'CollectionAdminRemoved' | 'AllowListAddressRemoved' | 'AllowListAddressAdded' | 'CollectionLimitSet' | 'CollectionPermissionSet'; } - /** @name PalletEvmAccountBasicCrossAccountIdRepr (85) */ + /** @name PalletEvmAccountBasicCrossAccountIdRepr (89) */ interface PalletEvmAccountBasicCrossAccountIdRepr extends Enum { readonly isSubstrate: boolean; readonly asSubstrate: AccountId32; @@ -1016,7 +1132,7 @@ readonly type: 'Substrate' | 'Ethereum'; } - /** @name PalletUniqueSchedulerEvent (88) */ + /** @name PalletUniqueSchedulerEvent (92) */ interface PalletUniqueSchedulerEvent extends Enum { readonly isScheduled: boolean; readonly asScheduled: { @@ -1043,14 +1159,14 @@ readonly type: 'Scheduled' | 'Canceled' | 'Dispatched' | 'CallLookupFailed'; } - /** @name FrameSupportScheduleLookupError (91) */ + /** @name FrameSupportScheduleLookupError (95) */ interface FrameSupportScheduleLookupError extends Enum { readonly isUnknown: boolean; readonly isBadFormat: boolean; readonly type: 'Unknown' | 'BadFormat'; } - /** @name PalletCommonEvent (92) */ + /** @name PalletCommonEvent (96) */ interface PalletCommonEvent extends Enum { readonly isCollectionCreated: boolean; readonly asCollectionCreated: ITuple<[u32, u8, AccountId32]>; @@ -1077,14 +1193,14 @@ readonly type: 'CollectionCreated' | 'CollectionDestroyed' | 'ItemCreated' | 'ItemDestroyed' | 'Transfer' | 'Approved' | 'CollectionPropertySet' | 'CollectionPropertyDeleted' | 'TokenPropertySet' | 'TokenPropertyDeleted' | 'PropertyPermissionSet'; } - /** @name PalletStructureEvent (95) */ + /** @name PalletStructureEvent (99) */ interface PalletStructureEvent extends Enum { readonly isExecuted: boolean; readonly asExecuted: Result; readonly type: 'Executed'; } - /** @name PalletRmrkCoreEvent (96) */ + /** @name PalletRmrkCoreEvent (100) */ interface PalletRmrkCoreEvent extends Enum { readonly isCollectionCreated: boolean; readonly asCollectionCreated: { @@ -1174,7 +1290,7 @@ readonly type: 'CollectionCreated' | 'CollectionDestroyed' | 'IssuerChanged' | 'CollectionLocked' | 'NftMinted' | 'NftBurned' | 'NftSent' | 'NftAccepted' | 'NftRejected' | 'PropertySet' | 'ResourceAdded' | 'ResourceRemoval' | 'ResourceAccepted' | 'ResourceRemovalAccepted' | 'PrioritySet'; } - /** @name RmrkTraitsNftAccountIdOrCollectionNftTuple (97) */ + /** @name RmrkTraitsNftAccountIdOrCollectionNftTuple (101) */ interface RmrkTraitsNftAccountIdOrCollectionNftTuple extends Enum { readonly isAccountId: boolean; readonly asAccountId: AccountId32; @@ -1183,7 +1299,7 @@ readonly type: 'AccountId' | 'CollectionAndNftTuple'; } - /** @name PalletRmrkEquipEvent (102) */ + /** @name PalletRmrkEquipEvent (106) */ interface PalletRmrkEquipEvent extends Enum { readonly isBaseCreated: boolean; readonly asBaseCreated: { @@ -1198,7 +1314,7 @@ readonly type: 'BaseCreated' | 'EquippablesUpdated'; } - /** @name PalletAppPromotionEvent (103) */ + /** @name PalletAppPromotionEvent (107) */ interface PalletAppPromotionEvent extends Enum { readonly isStakingRecalculation: boolean; readonly asStakingRecalculation: ITuple<[AccountId32, u128, u128]>; @@ -1211,7 +1327,42 @@ readonly type: 'StakingRecalculation' | 'Stake' | 'Unstake' | 'SetAdmin'; } - /** @name PalletEvmEvent (104) */ + /** @name PalletForeignAssetsModuleEvent (108) */ + interface PalletForeignAssetsModuleEvent extends Enum { + readonly isForeignAssetRegistered: boolean; + readonly asForeignAssetRegistered: { + readonly assetId: u32; + readonly assetAddress: XcmV1MultiLocation; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly isForeignAssetUpdated: boolean; + readonly asForeignAssetUpdated: { + readonly assetId: u32; + readonly assetAddress: XcmV1MultiLocation; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly isAssetRegistered: boolean; + readonly asAssetRegistered: { + readonly assetId: PalletForeignAssetsAssetIds; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly isAssetUpdated: boolean; + readonly asAssetUpdated: { + readonly assetId: PalletForeignAssetsAssetIds; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly type: 'ForeignAssetRegistered' | 'ForeignAssetUpdated' | 'AssetRegistered' | 'AssetUpdated'; + } + + /** @name PalletForeignAssetsModuleAssetMetadata (109) */ + interface PalletForeignAssetsModuleAssetMetadata extends Struct { + readonly name: Bytes; + readonly symbol: Bytes; + readonly decimals: u8; + readonly minimalBalance: u128; + } + + /** @name PalletEvmEvent (110) */ interface PalletEvmEvent extends Enum { readonly isLog: boolean; readonly asLog: EthereumLog; @@ -1230,21 +1381,21 @@ readonly type: 'Log' | 'Created' | 'CreatedFailed' | 'Executed' | 'ExecutedFailed' | 'BalanceDeposit' | 'BalanceWithdraw'; } - /** @name EthereumLog (105) */ + /** @name EthereumLog (111) */ interface EthereumLog extends Struct { readonly address: H160; readonly topics: Vec; readonly data: Bytes; } - /** @name PalletEthereumEvent (109) */ + /** @name PalletEthereumEvent (115) */ interface PalletEthereumEvent extends Enum { readonly isExecuted: boolean; readonly asExecuted: ITuple<[H160, H160, H256, EvmCoreErrorExitReason]>; readonly type: 'Executed'; } - /** @name EvmCoreErrorExitReason (110) */ + /** @name EvmCoreErrorExitReason (116) */ interface EvmCoreErrorExitReason extends Enum { readonly isSucceed: boolean; readonly asSucceed: EvmCoreErrorExitSucceed; @@ -1257,7 +1408,7 @@ readonly type: 'Succeed' | 'Error' | 'Revert' | 'Fatal'; } - /** @name EvmCoreErrorExitSucceed (111) */ + /** @name EvmCoreErrorExitSucceed (117) */ interface EvmCoreErrorExitSucceed extends Enum { readonly isStopped: boolean; readonly isReturned: boolean; @@ -1265,7 +1416,7 @@ readonly type: 'Stopped' | 'Returned' | 'Suicided'; } - /** @name EvmCoreErrorExitError (112) */ + /** @name EvmCoreErrorExitError (118) */ interface EvmCoreErrorExitError extends Enum { readonly isStackUnderflow: boolean; readonly isStackOverflow: boolean; @@ -1286,13 +1437,13 @@ readonly type: 'StackUnderflow' | 'StackOverflow' | 'InvalidJump' | 'InvalidRange' | 'DesignatedInvalid' | 'CallTooDeep' | 'CreateCollision' | 'CreateContractLimit' | 'OutOfOffset' | 'OutOfGas' | 'OutOfFund' | 'PcUnderflow' | 'CreateEmpty' | 'Other' | 'InvalidCode'; } - /** @name EvmCoreErrorExitRevert (115) */ + /** @name EvmCoreErrorExitRevert (121) */ interface EvmCoreErrorExitRevert extends Enum { readonly isReverted: boolean; readonly type: 'Reverted'; } - /** @name EvmCoreErrorExitFatal (116) */ + /** @name EvmCoreErrorExitFatal (122) */ interface EvmCoreErrorExitFatal extends Enum { readonly isNotSupported: boolean; readonly isUnhandledInterrupt: boolean; @@ -1303,7 +1454,7 @@ readonly type: 'NotSupported' | 'UnhandledInterrupt' | 'CallErrorAsFatal' | 'Other'; } - /** @name PalletEvmContractHelpersEvent (117) */ + /** @name PalletEvmContractHelpersEvent (123) */ interface PalletEvmContractHelpersEvent extends Enum { readonly isContractSponsorSet: boolean; readonly asContractSponsorSet: ITuple<[H160, AccountId32]>; @@ -1314,7 +1465,7 @@ readonly type: 'ContractSponsorSet' | 'ContractSponsorshipConfirmed' | 'ContractSponsorRemoved'; } - /** @name FrameSystemPhase (118) */ + /** @name FrameSystemPhase (124) */ interface FrameSystemPhase extends Enum { readonly isApplyExtrinsic: boolean; readonly asApplyExtrinsic: u32; @@ -1323,13 +1474,13 @@ readonly type: 'ApplyExtrinsic' | 'Finalization' | 'Initialization'; } - /** @name FrameSystemLastRuntimeUpgradeInfo (120) */ + /** @name FrameSystemLastRuntimeUpgradeInfo (126) */ interface FrameSystemLastRuntimeUpgradeInfo extends Struct { readonly specVersion: Compact; readonly specName: Text; } - /** @name FrameSystemCall (121) */ + /** @name FrameSystemCall (127) */ interface FrameSystemCall extends Enum { readonly isFillBlock: boolean; readonly asFillBlock: { @@ -1371,21 +1522,21 @@ readonly type: 'FillBlock' | 'Remark' | 'SetHeapPages' | 'SetCode' | 'SetCodeWithoutChecks' | 'SetStorage' | 'KillStorage' | 'KillPrefix' | 'RemarkWithEvent'; } - /** @name FrameSystemLimitsBlockWeights (126) */ + /** @name FrameSystemLimitsBlockWeights (132) */ interface FrameSystemLimitsBlockWeights extends Struct { readonly baseBlock: u64; readonly maxBlock: u64; readonly perClass: FrameSupportWeightsPerDispatchClassWeightsPerClass; } - /** @name FrameSupportWeightsPerDispatchClassWeightsPerClass (127) */ + /** @name FrameSupportWeightsPerDispatchClassWeightsPerClass (133) */ interface FrameSupportWeightsPerDispatchClassWeightsPerClass extends Struct { readonly normal: FrameSystemLimitsWeightsPerClass; readonly operational: FrameSystemLimitsWeightsPerClass; readonly mandatory: FrameSystemLimitsWeightsPerClass; } - /** @name FrameSystemLimitsWeightsPerClass (128) */ + /** @name FrameSystemLimitsWeightsPerClass (134) */ interface FrameSystemLimitsWeightsPerClass extends Struct { readonly baseExtrinsic: u64; readonly maxExtrinsic: Option; @@ -1393,25 +1544,25 @@ readonly reserved: Option; } - /** @name FrameSystemLimitsBlockLength (130) */ + /** @name FrameSystemLimitsBlockLength (136) */ interface FrameSystemLimitsBlockLength extends Struct { readonly max: FrameSupportWeightsPerDispatchClassU32; } - /** @name FrameSupportWeightsPerDispatchClassU32 (131) */ + /** @name FrameSupportWeightsPerDispatchClassU32 (137) */ interface FrameSupportWeightsPerDispatchClassU32 extends Struct { readonly normal: u32; readonly operational: u32; readonly mandatory: u32; } - /** @name FrameSupportWeightsRuntimeDbWeight (132) */ + /** @name FrameSupportWeightsRuntimeDbWeight (138) */ interface FrameSupportWeightsRuntimeDbWeight extends Struct { readonly read: u64; readonly write: u64; } - /** @name SpVersionRuntimeVersion (133) */ + /** @name SpVersionRuntimeVersion (139) */ interface SpVersionRuntimeVersion extends Struct { readonly specName: Text; readonly implName: Text; @@ -1423,7 +1574,7 @@ readonly stateVersion: u8; } - /** @name FrameSystemError (138) */ + /** @name FrameSystemError (144) */ interface FrameSystemError extends Enum { readonly isInvalidSpecName: boolean; readonly isSpecVersionNeedsToIncrease: boolean; @@ -1434,7 +1585,7 @@ readonly type: 'InvalidSpecName' | 'SpecVersionNeedsToIncrease' | 'FailedToExtractRuntimeVersion' | 'NonDefaultComposite' | 'NonZeroRefCount' | 'CallFiltered'; } - /** @name PolkadotPrimitivesV2PersistedValidationData (139) */ + /** @name PolkadotPrimitivesV2PersistedValidationData (145) */ interface PolkadotPrimitivesV2PersistedValidationData extends Struct { readonly parentHead: Bytes; readonly relayParentNumber: u32; @@ -1442,18 +1593,18 @@ readonly maxPovSize: u32; } - /** @name PolkadotPrimitivesV2UpgradeRestriction (142) */ + /** @name PolkadotPrimitivesV2UpgradeRestriction (148) */ interface PolkadotPrimitivesV2UpgradeRestriction extends Enum { readonly isPresent: boolean; readonly type: 'Present'; } - /** @name SpTrieStorageProof (143) */ + /** @name SpTrieStorageProof (149) */ interface SpTrieStorageProof extends Struct { readonly trieNodes: BTreeSet; } - /** @name CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot (145) */ + /** @name CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot (151) */ interface CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot extends Struct { readonly dmqMqcHead: H256; readonly relayDispatchQueueSize: ITuple<[u32, u32]>; @@ -1461,7 +1612,7 @@ readonly egressChannels: Vec>; } - /** @name PolkadotPrimitivesV2AbridgedHrmpChannel (148) */ + /** @name PolkadotPrimitivesV2AbridgedHrmpChannel (154) */ interface PolkadotPrimitivesV2AbridgedHrmpChannel extends Struct { readonly maxCapacity: u32; readonly maxTotalSize: u32; @@ -1471,7 +1622,7 @@ readonly mqcHead: Option; } - /** @name PolkadotPrimitivesV2AbridgedHostConfiguration (149) */ + /** @name PolkadotPrimitivesV2AbridgedHostConfiguration (155) */ interface PolkadotPrimitivesV2AbridgedHostConfiguration extends Struct { readonly maxCodeSize: u32; readonly maxHeadDataSize: u32; @@ -1484,13 +1635,13 @@ readonly validationUpgradeDelay: u32; } - /** @name PolkadotCorePrimitivesOutboundHrmpMessage (155) */ + /** @name PolkadotCorePrimitivesOutboundHrmpMessage (161) */ interface PolkadotCorePrimitivesOutboundHrmpMessage extends Struct { readonly recipient: u32; readonly data: Bytes; } - /** @name CumulusPalletParachainSystemCall (156) */ + /** @name CumulusPalletParachainSystemCall (162) */ interface CumulusPalletParachainSystemCall extends Enum { readonly isSetValidationData: boolean; readonly asSetValidationData: { @@ -1511,7 +1662,7 @@ readonly type: 'SetValidationData' | 'SudoSendUpwardMessage' | 'AuthorizeUpgrade' | 'EnactAuthorizedUpgrade'; } - /** @name CumulusPrimitivesParachainInherentParachainInherentData (157) */ + /** @name CumulusPrimitivesParachainInherentParachainInherentData (163) */ interface CumulusPrimitivesParachainInherentParachainInherentData extends Struct { readonly validationData: PolkadotPrimitivesV2PersistedValidationData; readonly relayChainState: SpTrieStorageProof; @@ -1519,19 +1670,19 @@ readonly horizontalMessages: BTreeMap>; } - /** @name PolkadotCorePrimitivesInboundDownwardMessage (159) */ + /** @name PolkadotCorePrimitivesInboundDownwardMessage (165) */ interface PolkadotCorePrimitivesInboundDownwardMessage extends Struct { readonly sentAt: u32; readonly msg: Bytes; } - /** @name PolkadotCorePrimitivesInboundHrmpMessage (162) */ + /** @name PolkadotCorePrimitivesInboundHrmpMessage (168) */ interface PolkadotCorePrimitivesInboundHrmpMessage extends Struct { readonly sentAt: u32; readonly data: Bytes; } - /** @name CumulusPalletParachainSystemError (165) */ + /** @name CumulusPalletParachainSystemError (171) */ interface CumulusPalletParachainSystemError extends Enum { readonly isOverlappingUpgrades: boolean; readonly isProhibitedByPolkadot: boolean; @@ -1544,14 +1695,14 @@ readonly type: 'OverlappingUpgrades' | 'ProhibitedByPolkadot' | 'TooBig' | 'ValidationDataNotAvailable' | 'HostConfigurationNotAvailable' | 'NotScheduled' | 'NothingAuthorized' | 'Unauthorized'; } - /** @name PalletBalancesBalanceLock (167) */ + /** @name PalletBalancesBalanceLock (173) */ interface PalletBalancesBalanceLock extends Struct { readonly id: U8aFixed; readonly amount: u128; readonly reasons: PalletBalancesReasons; } - /** @name PalletBalancesReasons (168) */ + /** @name PalletBalancesReasons (174) */ interface PalletBalancesReasons extends Enum { readonly isFee: boolean; readonly isMisc: boolean; @@ -1559,20 +1710,20 @@ readonly type: 'Fee' | 'Misc' | 'All'; } - /** @name PalletBalancesReserveData (171) */ + /** @name PalletBalancesReserveData (177) */ interface PalletBalancesReserveData extends Struct { readonly id: U8aFixed; readonly amount: u128; } - /** @name PalletBalancesReleases (173) */ + /** @name PalletBalancesReleases (179) */ interface PalletBalancesReleases extends Enum { readonly isV100: boolean; readonly isV200: boolean; readonly type: 'V100' | 'V200'; } - /** @name PalletBalancesCall (174) */ + /** @name PalletBalancesCall (180) */ interface PalletBalancesCall extends Enum { readonly isTransfer: boolean; readonly asTransfer: { @@ -1609,7 +1760,7 @@ readonly type: 'Transfer' | 'SetBalance' | 'ForceTransfer' | 'TransferKeepAlive' | 'TransferAll' | 'ForceUnreserve'; } - /** @name PalletBalancesError (177) */ + /** @name PalletBalancesError (183) */ interface PalletBalancesError extends Enum { readonly isVestingBalance: boolean; readonly isLiquidityRestrictions: boolean; @@ -1622,7 +1773,7 @@ readonly type: 'VestingBalance' | 'LiquidityRestrictions' | 'InsufficientBalance' | 'ExistentialDeposit' | 'KeepAlive' | 'ExistingVestingSchedule' | 'DeadAccount' | 'TooManyReserves'; } - /** @name PalletTimestampCall (179) */ + /** @name PalletTimestampCall (185) */ interface PalletTimestampCall extends Enum { readonly isSet: boolean; readonly asSet: { @@ -1631,14 +1782,14 @@ readonly type: 'Set'; } - /** @name PalletTransactionPaymentReleases (181) */ + /** @name PalletTransactionPaymentReleases (187) */ interface PalletTransactionPaymentReleases extends Enum { readonly isV1Ancient: boolean; readonly isV2: boolean; readonly type: 'V1Ancient' | 'V2'; } - /** @name PalletTreasuryProposal (182) */ + /** @name PalletTreasuryProposal (188) */ interface PalletTreasuryProposal extends Struct { readonly proposer: AccountId32; readonly value: u128; @@ -1646,7 +1797,7 @@ readonly bond: u128; } - /** @name PalletTreasuryCall (185) */ + /** @name PalletTreasuryCall (191) */ interface PalletTreasuryCall extends Enum { readonly isProposeSpend: boolean; readonly asProposeSpend: { @@ -1673,10 +1824,10 @@ readonly type: 'ProposeSpend' | 'RejectProposal' | 'ApproveProposal' | 'Spend' | 'RemoveApproval'; } - /** @name FrameSupportPalletId (188) */ + /** @name FrameSupportPalletId (194) */ interface FrameSupportPalletId extends U8aFixed {} - /** @name PalletTreasuryError (189) */ + /** @name PalletTreasuryError (195) */ interface PalletTreasuryError extends Enum { readonly isInsufficientProposersBalance: boolean; readonly isInvalidIndex: boolean; @@ -1686,7 +1837,7 @@ readonly type: 'InsufficientProposersBalance' | 'InvalidIndex' | 'TooManyApprovals' | 'InsufficientPermission' | 'ProposalNotApproved'; } - /** @name PalletSudoCall (190) */ + /** @name PalletSudoCall (196) */ interface PalletSudoCall extends Enum { readonly isSudo: boolean; readonly asSudo: { @@ -1709,7 +1860,7 @@ readonly type: 'Sudo' | 'SudoUncheckedWeight' | 'SetKey' | 'SudoAs'; } - /** @name OrmlVestingModuleCall (192) */ + /** @name OrmlVestingModuleCall (198) */ interface OrmlVestingModuleCall extends Enum { readonly isClaim: boolean; readonly isVestedTransfer: boolean; @@ -1729,7 +1880,100 @@ readonly type: 'Claim' | 'VestedTransfer' | 'UpdateVestingSchedules' | 'ClaimFor'; } - /** @name CumulusPalletXcmpQueueCall (194) */ + /** @name OrmlXtokensModuleCall (200) */ + interface OrmlXtokensModuleCall extends Enum { + readonly isTransfer: boolean; + readonly asTransfer: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly amount: u128; + readonly dest: XcmVersionedMultiLocation; + readonly destWeight: u64; + } & Struct; + readonly isTransferMultiasset: boolean; + readonly asTransferMultiasset: { + readonly asset: XcmVersionedMultiAsset; + readonly dest: XcmVersionedMultiLocation; + readonly destWeight: u64; + } & Struct; + readonly isTransferWithFee: boolean; + readonly asTransferWithFee: { + readonly currencyId: PalletForeignAssetsAssetIds; + readonly amount: u128; + readonly fee: u128; + readonly dest: XcmVersionedMultiLocation; + readonly destWeight: u64; + } & Struct; + readonly isTransferMultiassetWithFee: boolean; + readonly asTransferMultiassetWithFee: { + readonly asset: XcmVersionedMultiAsset; + readonly fee: XcmVersionedMultiAsset; + readonly dest: XcmVersionedMultiLocation; + readonly destWeight: u64; + } & Struct; + readonly isTransferMulticurrencies: boolean; + readonly asTransferMulticurrencies: { + readonly currencies: Vec>; + readonly feeItem: u32; + readonly dest: XcmVersionedMultiLocation; + readonly destWeight: u64; + } & Struct; + readonly isTransferMultiassets: boolean; + readonly asTransferMultiassets: { + readonly assets: XcmVersionedMultiAssets; + readonly feeItem: u32; + readonly dest: XcmVersionedMultiLocation; + readonly destWeight: u64; + } & Struct; + readonly type: 'Transfer' | 'TransferMultiasset' | 'TransferWithFee' | 'TransferMultiassetWithFee' | 'TransferMulticurrencies' | 'TransferMultiassets'; + } + + /** @name XcmVersionedMultiAsset (201) */ + interface XcmVersionedMultiAsset extends Enum { + readonly isV0: boolean; + readonly asV0: XcmV0MultiAsset; + readonly isV1: boolean; + readonly asV1: XcmV1MultiAsset; + readonly type: 'V0' | 'V1'; + } + + /** @name OrmlTokensModuleCall (204) */ + interface OrmlTokensModuleCall extends Enum { + readonly isTransfer: boolean; + readonly asTransfer: { + readonly dest: MultiAddress; + readonly currencyId: PalletForeignAssetsAssetIds; + readonly amount: Compact; + } & Struct; + readonly isTransferAll: boolean; + readonly asTransferAll: { + readonly dest: MultiAddress; + readonly currencyId: PalletForeignAssetsAssetIds; + readonly keepAlive: bool; + } & Struct; + readonly isTransferKeepAlive: boolean; + readonly asTransferKeepAlive: { + readonly dest: MultiAddress; + readonly currencyId: PalletForeignAssetsAssetIds; + readonly amount: Compact; + } & Struct; + readonly isForceTransfer: boolean; + readonly asForceTransfer: { + readonly source: MultiAddress; + readonly dest: MultiAddress; + readonly currencyId: PalletForeignAssetsAssetIds; + readonly amount: Compact; + } & Struct; + readonly isSetBalance: boolean; + readonly asSetBalance: { + readonly who: MultiAddress; + readonly currencyId: PalletForeignAssetsAssetIds; + readonly newFree: Compact; + readonly newReserved: Compact; + } & Struct; + readonly type: 'Transfer' | 'TransferAll' | 'TransferKeepAlive' | 'ForceTransfer' | 'SetBalance'; + } + + /** @name CumulusPalletXcmpQueueCall (205) */ interface CumulusPalletXcmpQueueCall extends Enum { readonly isServiceOverweight: boolean; readonly asServiceOverweight: { @@ -1765,7 +2009,7 @@ readonly type: 'ServiceOverweight' | 'SuspendXcmExecution' | 'ResumeXcmExecution' | 'UpdateSuspendThreshold' | 'UpdateDropThreshold' | 'UpdateResumeThreshold' | 'UpdateThresholdWeight' | 'UpdateWeightRestrictDecay' | 'UpdateXcmpMaxIndividualWeight'; } - /** @name PalletXcmCall (195) */ + /** @name PalletXcmCall (206) */ interface PalletXcmCall extends Enum { readonly isSend: boolean; readonly asSend: { @@ -1827,7 +2071,7 @@ readonly type: 'Send' | 'TeleportAssets' | 'ReserveTransferAssets' | 'Execute' | 'ForceXcmVersion' | 'ForceDefaultXcmVersion' | 'ForceSubscribeVersionNotify' | 'ForceUnsubscribeVersionNotify' | 'LimitedReserveTransferAssets' | 'LimitedTeleportAssets'; } - /** @name XcmVersionedXcm (196) */ + /** @name XcmVersionedXcm (207) */ interface XcmVersionedXcm extends Enum { readonly isV0: boolean; readonly asV0: XcmV0Xcm; @@ -1838,7 +2082,7 @@ readonly type: 'V0' | 'V1' | 'V2'; } - /** @name XcmV0Xcm (197) */ + /** @name XcmV0Xcm (208) */ interface XcmV0Xcm extends Enum { readonly isWithdrawAsset: boolean; readonly asWithdrawAsset: { @@ -1901,7 +2145,7 @@ readonly type: 'WithdrawAsset' | 'ReserveAssetDeposit' | 'TeleportAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'RelayedFrom'; } - /** @name XcmV0Order (199) */ + /** @name XcmV0Order (210) */ interface XcmV0Order extends Enum { readonly isNull: boolean; readonly isDepositAsset: boolean; @@ -1949,14 +2193,14 @@ readonly type: 'Null' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution'; } - /** @name XcmV0Response (201) */ + /** @name XcmV0Response (212) */ interface XcmV0Response extends Enum { readonly isAssets: boolean; readonly asAssets: Vec; readonly type: 'Assets'; } - /** @name XcmV1Xcm (202) */ + /** @name XcmV1Xcm (213) */ interface XcmV1Xcm extends Enum { readonly isWithdrawAsset: boolean; readonly asWithdrawAsset: { @@ -2025,7 +2269,7 @@ readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'RelayedFrom' | 'SubscribeVersion' | 'UnsubscribeVersion'; } - /** @name XcmV1Order (204) */ + /** @name XcmV1Order (215) */ interface XcmV1Order extends Enum { readonly isNoop: boolean; readonly isDepositAsset: boolean; @@ -2075,7 +2319,7 @@ readonly type: 'Noop' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution'; } - /** @name XcmV1Response (206) */ + /** @name XcmV1Response (217) */ interface XcmV1Response extends Enum { readonly isAssets: boolean; readonly asAssets: XcmV1MultiassetMultiAssets; @@ -2084,10 +2328,10 @@ readonly type: 'Assets' | 'Version'; } - /** @name CumulusPalletXcmCall (220) */ + /** @name CumulusPalletXcmCall (231) */ type CumulusPalletXcmCall = Null; - /** @name CumulusPalletDmpQueueCall (221) */ + /** @name CumulusPalletDmpQueueCall (232) */ interface CumulusPalletDmpQueueCall extends Enum { readonly isServiceOverweight: boolean; readonly asServiceOverweight: { @@ -2097,7 +2341,7 @@ readonly type: 'ServiceOverweight'; } - /** @name PalletInflationCall (222) */ + /** @name PalletInflationCall (233) */ interface PalletInflationCall extends Enum { readonly isStartInflation: boolean; readonly asStartInflation: { @@ -2106,7 +2350,7 @@ readonly type: 'StartInflation'; } - /** @name PalletUniqueCall (223) */ + /** @name PalletUniqueCall (234) */ interface PalletUniqueCall extends Enum { readonly isCreateCollection: boolean; readonly asCreateCollection: { @@ -2264,7 +2508,7 @@ readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition'; } - /** @name UpDataStructsCollectionMode (228) */ + /** @name UpDataStructsCollectionMode (239) */ interface UpDataStructsCollectionMode extends Enum { readonly isNft: boolean; readonly isFungible: boolean; @@ -2273,7 +2517,7 @@ readonly type: 'Nft' | 'Fungible' | 'ReFungible'; } - /** @name UpDataStructsCreateCollectionData (229) */ + /** @name UpDataStructsCreateCollectionData (240) */ interface UpDataStructsCreateCollectionData extends Struct { readonly mode: UpDataStructsCollectionMode; readonly access: Option; @@ -2287,14 +2531,14 @@ readonly properties: Vec; } - /** @name UpDataStructsAccessMode (231) */ + /** @name UpDataStructsAccessMode (242) */ interface UpDataStructsAccessMode extends Enum { readonly isNormal: boolean; readonly isAllowList: boolean; readonly type: 'Normal' | 'AllowList'; } - /** @name UpDataStructsCollectionLimits (233) */ + /** @name UpDataStructsCollectionLimits (244) */ interface UpDataStructsCollectionLimits extends Struct { readonly accountTokenOwnershipLimit: Option; readonly sponsoredDataSize: Option; @@ -2307,7 +2551,7 @@ readonly transfersEnabled: Option; } - /** @name UpDataStructsSponsoringRateLimit (235) */ + /** @name UpDataStructsSponsoringRateLimit (246) */ interface UpDataStructsSponsoringRateLimit extends Enum { readonly isSponsoringDisabled: boolean; readonly isBlocks: boolean; @@ -2315,43 +2559,43 @@ readonly type: 'SponsoringDisabled' | 'Blocks'; } - /** @name UpDataStructsCollectionPermissions (238) */ + /** @name UpDataStructsCollectionPermissions (249) */ interface UpDataStructsCollectionPermissions extends Struct { readonly access: Option; readonly mintMode: Option; readonly nesting: Option; } - /** @name UpDataStructsNestingPermissions (240) */ + /** @name UpDataStructsNestingPermissions (251) */ interface UpDataStructsNestingPermissions extends Struct { readonly tokenOwner: bool; readonly collectionAdmin: bool; readonly restricted: Option; } - /** @name UpDataStructsOwnerRestrictedSet (242) */ + /** @name UpDataStructsOwnerRestrictedSet (253) */ interface UpDataStructsOwnerRestrictedSet extends BTreeSet {} - /** @name UpDataStructsPropertyKeyPermission (247) */ + /** @name UpDataStructsPropertyKeyPermission (258) */ interface UpDataStructsPropertyKeyPermission extends Struct { readonly key: Bytes; readonly permission: UpDataStructsPropertyPermission; } - /** @name UpDataStructsPropertyPermission (248) */ + /** @name UpDataStructsPropertyPermission (259) */ interface UpDataStructsPropertyPermission extends Struct { readonly mutable: bool; readonly collectionAdmin: bool; readonly tokenOwner: bool; } - /** @name UpDataStructsProperty (251) */ + /** @name UpDataStructsProperty (262) */ interface UpDataStructsProperty extends Struct { readonly key: Bytes; readonly value: Bytes; } - /** @name UpDataStructsCreateItemData (254) */ + /** @name UpDataStructsCreateItemData (265) */ interface UpDataStructsCreateItemData extends Enum { readonly isNft: boolean; readonly asNft: UpDataStructsCreateNftData; @@ -2362,23 +2606,23 @@ readonly type: 'Nft' | 'Fungible' | 'ReFungible'; } - /** @name UpDataStructsCreateNftData (255) */ + /** @name UpDataStructsCreateNftData (266) */ interface UpDataStructsCreateNftData extends Struct { readonly properties: Vec; } - /** @name UpDataStructsCreateFungibleData (256) */ + /** @name UpDataStructsCreateFungibleData (267) */ interface UpDataStructsCreateFungibleData extends Struct { readonly value: u128; } - /** @name UpDataStructsCreateReFungibleData (257) */ + /** @name UpDataStructsCreateReFungibleData (268) */ interface UpDataStructsCreateReFungibleData extends Struct { readonly pieces: u128; readonly properties: Vec; } - /** @name UpDataStructsCreateItemExData (260) */ + /** @name UpDataStructsCreateItemExData (271) */ interface UpDataStructsCreateItemExData extends Enum { readonly isNft: boolean; readonly asNft: Vec; @@ -2391,26 +2635,26 @@ readonly type: 'Nft' | 'Fungible' | 'RefungibleMultipleItems' | 'RefungibleMultipleOwners'; } - /** @name UpDataStructsCreateNftExData (262) */ + /** @name UpDataStructsCreateNftExData (273) */ interface UpDataStructsCreateNftExData extends Struct { readonly properties: Vec; readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; } - /** @name UpDataStructsCreateRefungibleExSingleOwner (269) */ + /** @name UpDataStructsCreateRefungibleExSingleOwner (280) */ interface UpDataStructsCreateRefungibleExSingleOwner extends Struct { readonly user: PalletEvmAccountBasicCrossAccountIdRepr; readonly pieces: u128; readonly properties: Vec; } - /** @name UpDataStructsCreateRefungibleExMultipleOwners (271) */ + /** @name UpDataStructsCreateRefungibleExMultipleOwners (282) */ interface UpDataStructsCreateRefungibleExMultipleOwners extends Struct { readonly users: BTreeMap; readonly properties: Vec; } - /** @name PalletUniqueSchedulerCall (272) */ + /** @name PalletUniqueSchedulerCall (283) */ interface PalletUniqueSchedulerCall extends Enum { readonly isScheduleNamed: boolean; readonly asScheduleNamed: { @@ -2435,7 +2679,7 @@ readonly type: 'ScheduleNamed' | 'CancelNamed' | 'ScheduleNamedAfter'; } - /** @name FrameSupportScheduleMaybeHashed (274) */ + /** @name FrameSupportScheduleMaybeHashed (285) */ interface FrameSupportScheduleMaybeHashed extends Enum { readonly isValue: boolean; readonly asValue: Call; @@ -2444,7 +2688,7 @@ readonly type: 'Value' | 'Hash'; } - /** @name PalletConfigurationCall (275) */ + /** @name PalletConfigurationCall (286) */ interface PalletConfigurationCall extends Enum { readonly isSetWeightToFeeCoefficientOverride: boolean; readonly asSetWeightToFeeCoefficientOverride: { @@ -2457,13 +2701,13 @@ readonly type: 'SetWeightToFeeCoefficientOverride' | 'SetMinGasPriceOverride'; } - /** @name PalletTemplateTransactionPaymentCall (276) */ + /** @name PalletTemplateTransactionPaymentCall (287) */ type PalletTemplateTransactionPaymentCall = Null; - /** @name PalletStructureCall (277) */ + /** @name PalletStructureCall (288) */ type PalletStructureCall = Null; - /** @name PalletRmrkCoreCall (278) */ + /** @name PalletRmrkCoreCall (289) */ interface PalletRmrkCoreCall extends Enum { readonly isCreateCollection: boolean; readonly asCreateCollection: { @@ -2569,7 +2813,7 @@ readonly type: 'CreateCollection' | 'DestroyCollection' | 'ChangeCollectionIssuer' | 'LockCollection' | 'MintNft' | 'BurnNft' | 'Send' | 'AcceptNft' | 'RejectNft' | 'AcceptResource' | 'AcceptResourceRemoval' | 'SetProperty' | 'SetPriority' | 'AddBasicResource' | 'AddComposableResource' | 'AddSlotResource' | 'RemoveResource'; } - /** @name RmrkTraitsResourceResourceTypes (284) */ + /** @name RmrkTraitsResourceResourceTypes (295) */ interface RmrkTraitsResourceResourceTypes extends Enum { readonly isBasic: boolean; readonly asBasic: RmrkTraitsResourceBasicResource; @@ -2580,7 +2824,7 @@ readonly type: 'Basic' | 'Composable' | 'Slot'; } - /** @name RmrkTraitsResourceBasicResource (286) */ + /** @name RmrkTraitsResourceBasicResource (297) */ interface RmrkTraitsResourceBasicResource extends Struct { readonly src: Option; readonly metadata: Option; @@ -2588,7 +2832,7 @@ readonly thumb: Option; } - /** @name RmrkTraitsResourceComposableResource (288) */ + /** @name RmrkTraitsResourceComposableResource (299) */ interface RmrkTraitsResourceComposableResource extends Struct { readonly parts: Vec; readonly base: u32; @@ -2598,7 +2842,7 @@ readonly thumb: Option; } - /** @name RmrkTraitsResourceSlotResource (289) */ + /** @name RmrkTraitsResourceSlotResource (300) */ interface RmrkTraitsResourceSlotResource extends Struct { readonly base: u32; readonly src: Option; @@ -2608,7 +2852,7 @@ readonly thumb: Option; } - /** @name PalletRmrkEquipCall (292) */ + /** @name PalletRmrkEquipCall (303) */ interface PalletRmrkEquipCall extends Enum { readonly isCreateBase: boolean; readonly asCreateBase: { @@ -2630,7 +2874,7 @@ readonly type: 'CreateBase' | 'ThemeAdd' | 'Equippable'; } - /** @name RmrkTraitsPartPartType (295) */ + /** @name RmrkTraitsPartPartType (306) */ interface RmrkTraitsPartPartType extends Enum { readonly isFixedPart: boolean; readonly asFixedPart: RmrkTraitsPartFixedPart; @@ -2639,14 +2883,14 @@ readonly type: 'FixedPart' | 'SlotPart'; } - /** @name RmrkTraitsPartFixedPart (297) */ + /** @name RmrkTraitsPartFixedPart (308) */ interface RmrkTraitsPartFixedPart extends Struct { readonly id: u32; readonly z: u32; readonly src: Bytes; } - /** @name RmrkTraitsPartSlotPart (298) */ + /** @name RmrkTraitsPartSlotPart (309) */ interface RmrkTraitsPartSlotPart extends Struct { readonly id: u32; readonly equippable: RmrkTraitsPartEquippableList; @@ -2654,7 +2898,7 @@ readonly z: u32; } - /** @name RmrkTraitsPartEquippableList (299) */ + /** @name RmrkTraitsPartEquippableList (310) */ interface RmrkTraitsPartEquippableList extends Enum { readonly isAll: boolean; readonly isEmpty: boolean; @@ -2663,20 +2907,20 @@ readonly type: 'All' | 'Empty' | 'Custom'; } - /** @name RmrkTraitsTheme (301) */ + /** @name RmrkTraitsTheme (312) */ interface RmrkTraitsTheme extends Struct { readonly name: Bytes; readonly properties: Vec; readonly inherit: bool; } - /** @name RmrkTraitsThemeThemeProperty (303) */ + /** @name RmrkTraitsThemeThemeProperty (314) */ interface RmrkTraitsThemeThemeProperty extends Struct { readonly key: Bytes; readonly value: Bytes; } - /** @name PalletAppPromotionCall (305) */ + /** @name PalletAppPromotionCall (316) */ interface PalletAppPromotionCall extends Enum { readonly isSetAdminAddress: boolean; readonly asSetAdminAddress: { @@ -2710,7 +2954,24 @@ readonly type: 'SetAdminAddress' | 'Stake' | 'Unstake' | 'SponsorCollection' | 'StopSponsoringCollection' | 'SponsorContract' | 'StopSponsoringContract' | 'PayoutStakers'; } - /** @name PalletEvmCall (307) */ + /** @name PalletForeignAssetsModuleCall (318) */ + interface PalletForeignAssetsModuleCall extends Enum { + readonly isRegisterForeignAsset: boolean; + readonly asRegisterForeignAsset: { + readonly owner: AccountId32; + readonly location: XcmVersionedMultiLocation; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly isUpdateForeignAsset: boolean; + readonly asUpdateForeignAsset: { + readonly foreignAssetId: u32; + readonly location: XcmVersionedMultiLocation; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly type: 'RegisterForeignAsset' | 'UpdateForeignAsset'; + } + + /** @name PalletEvmCall (319) */ interface PalletEvmCall extends Enum { readonly isWithdraw: boolean; readonly asWithdraw: { @@ -2755,7 +3016,7 @@ readonly type: 'Withdraw' | 'Call' | 'Create' | 'Create2'; } - /** @name PalletEthereumCall (311) */ + /** @name PalletEthereumCall (323) */ interface PalletEthereumCall extends Enum { readonly isTransact: boolean; readonly asTransact: { @@ -2764,7 +3025,7 @@ readonly type: 'Transact'; } - /** @name EthereumTransactionTransactionV2 (312) */ + /** @name EthereumTransactionTransactionV2 (324) */ interface EthereumTransactionTransactionV2 extends Enum { readonly isLegacy: boolean; readonly asLegacy: EthereumTransactionLegacyTransaction; @@ -2775,7 +3036,7 @@ readonly type: 'Legacy' | 'Eip2930' | 'Eip1559'; } - /** @name EthereumTransactionLegacyTransaction (313) */ + /** @name EthereumTransactionLegacyTransaction (325) */ interface EthereumTransactionLegacyTransaction extends Struct { readonly nonce: U256; readonly gasPrice: U256; @@ -2786,7 +3047,7 @@ readonly signature: EthereumTransactionTransactionSignature; } - /** @name EthereumTransactionTransactionAction (314) */ + /** @name EthereumTransactionTransactionAction (326) */ interface EthereumTransactionTransactionAction extends Enum { readonly isCall: boolean; readonly asCall: H160; @@ -2794,14 +3055,14 @@ readonly type: 'Call' | 'Create'; } - /** @name EthereumTransactionTransactionSignature (315) */ + /** @name EthereumTransactionTransactionSignature (327) */ interface EthereumTransactionTransactionSignature extends Struct { readonly v: u64; readonly r: H256; readonly s: H256; } - /** @name EthereumTransactionEip2930Transaction (317) */ + /** @name EthereumTransactionEip2930Transaction (329) */ interface EthereumTransactionEip2930Transaction extends Struct { readonly chainId: u64; readonly nonce: U256; @@ -2816,13 +3077,13 @@ readonly s: H256; } - /** @name EthereumTransactionAccessListItem (319) */ + /** @name EthereumTransactionAccessListItem (331) */ interface EthereumTransactionAccessListItem extends Struct { readonly address: H160; readonly storageKeys: Vec; } - /** @name EthereumTransactionEip1559Transaction (320) */ + /** @name EthereumTransactionEip1559Transaction (332) */ interface EthereumTransactionEip1559Transaction extends Struct { readonly chainId: u64; readonly nonce: U256; @@ -2838,7 +3099,7 @@ readonly s: H256; } - /** @name PalletEvmMigrationCall (321) */ + /** @name PalletEvmMigrationCall (333) */ interface PalletEvmMigrationCall extends Enum { readonly isBegin: boolean; readonly asBegin: { @@ -2857,13 +3118,13 @@ readonly type: 'Begin' | 'SetData' | 'Finish'; } - /** @name PalletSudoError (324) */ + /** @name PalletSudoError (336) */ interface PalletSudoError extends Enum { readonly isRequireSudo: boolean; readonly type: 'RequireSudo'; } - /** @name OrmlVestingModuleError (326) */ + /** @name OrmlVestingModuleError (338) */ interface OrmlVestingModuleError extends Enum { readonly isZeroVestingPeriod: boolean; readonly isZeroVestingPeriodCount: boolean; @@ -2874,21 +3135,77 @@ readonly type: 'ZeroVestingPeriod' | 'ZeroVestingPeriodCount' | 'InsufficientBalanceToLock' | 'TooManyVestingSchedules' | 'AmountLow' | 'MaxVestingSchedulesExceeded'; } - /** @name CumulusPalletXcmpQueueInboundChannelDetails (328) */ + /** @name OrmlXtokensModuleError (339) */ + interface OrmlXtokensModuleError extends Enum { + readonly isAssetHasNoReserve: boolean; + readonly isNotCrossChainTransfer: boolean; + readonly isInvalidDest: boolean; + readonly isNotCrossChainTransferableCurrency: boolean; + readonly isUnweighableMessage: boolean; + readonly isXcmExecutionFailed: boolean; + readonly isCannotReanchor: boolean; + readonly isInvalidAncestry: boolean; + readonly isInvalidAsset: boolean; + readonly isDestinationNotInvertible: boolean; + readonly isBadVersion: boolean; + readonly isDistinctReserveForAssetAndFee: boolean; + readonly isZeroFee: boolean; + readonly isZeroAmount: boolean; + readonly isTooManyAssetsBeingSent: boolean; + readonly isAssetIndexNonExistent: boolean; + readonly isFeeNotEnough: boolean; + readonly isNotSupportedMultiLocation: boolean; + readonly isMinXcmFeeNotDefined: boolean; + readonly type: 'AssetHasNoReserve' | 'NotCrossChainTransfer' | 'InvalidDest' | 'NotCrossChainTransferableCurrency' | 'UnweighableMessage' | 'XcmExecutionFailed' | 'CannotReanchor' | 'InvalidAncestry' | 'InvalidAsset' | 'DestinationNotInvertible' | 'BadVersion' | 'DistinctReserveForAssetAndFee' | 'ZeroFee' | 'ZeroAmount' | 'TooManyAssetsBeingSent' | 'AssetIndexNonExistent' | 'FeeNotEnough' | 'NotSupportedMultiLocation' | 'MinXcmFeeNotDefined'; + } + + /** @name OrmlTokensBalanceLock (342) */ + interface OrmlTokensBalanceLock extends Struct { + readonly id: U8aFixed; + readonly amount: u128; + } + + /** @name OrmlTokensAccountData (344) */ + interface OrmlTokensAccountData extends Struct { + readonly free: u128; + readonly reserved: u128; + readonly frozen: u128; + } + + /** @name OrmlTokensReserveData (346) */ + interface OrmlTokensReserveData extends Struct { + readonly id: Null; + readonly amount: u128; + } + + /** @name OrmlTokensModuleError (348) */ + interface OrmlTokensModuleError extends Enum { + readonly isBalanceTooLow: boolean; + readonly isAmountIntoBalanceFailed: boolean; + readonly isLiquidityRestrictions: boolean; + readonly isMaxLocksExceeded: boolean; + readonly isKeepAlive: boolean; + readonly isExistentialDeposit: boolean; + readonly isDeadAccount: boolean; + readonly isTooManyReserves: boolean; + readonly type: 'BalanceTooLow' | 'AmountIntoBalanceFailed' | 'LiquidityRestrictions' | 'MaxLocksExceeded' | 'KeepAlive' | 'ExistentialDeposit' | 'DeadAccount' | 'TooManyReserves'; + } + + /** @name CumulusPalletXcmpQueueInboundChannelDetails (350) */ interface CumulusPalletXcmpQueueInboundChannelDetails extends Struct { readonly sender: u32; readonly state: CumulusPalletXcmpQueueInboundState; readonly messageMetadata: Vec>; } - /** @name CumulusPalletXcmpQueueInboundState (329) */ + /** @name CumulusPalletXcmpQueueInboundState (351) */ interface CumulusPalletXcmpQueueInboundState extends Enum { readonly isOk: boolean; readonly isSuspended: boolean; readonly type: 'Ok' | 'Suspended'; } - /** @name PolkadotParachainPrimitivesXcmpMessageFormat (332) */ + /** @name PolkadotParachainPrimitivesXcmpMessageFormat (354) */ interface PolkadotParachainPrimitivesXcmpMessageFormat extends Enum { readonly isConcatenatedVersionedXcm: boolean; readonly isConcatenatedEncodedBlob: boolean; @@ -2896,7 +3213,7 @@ readonly type: 'ConcatenatedVersionedXcm' | 'ConcatenatedEncodedBlob' | 'Signals'; } - /** @name CumulusPalletXcmpQueueOutboundChannelDetails (335) */ + /** @name CumulusPalletXcmpQueueOutboundChannelDetails (357) */ interface CumulusPalletXcmpQueueOutboundChannelDetails extends Struct { readonly recipient: u32; readonly state: CumulusPalletXcmpQueueOutboundState; @@ -2905,14 +3222,14 @@ readonly lastIndex: u16; } - /** @name CumulusPalletXcmpQueueOutboundState (336) */ + /** @name CumulusPalletXcmpQueueOutboundState (358) */ interface CumulusPalletXcmpQueueOutboundState extends Enum { readonly isOk: boolean; readonly isSuspended: boolean; readonly type: 'Ok' | 'Suspended'; } - /** @name CumulusPalletXcmpQueueQueueConfigData (338) */ + /** @name CumulusPalletXcmpQueueQueueConfigData (360) */ interface CumulusPalletXcmpQueueQueueConfigData extends Struct { readonly suspendThreshold: u32; readonly dropThreshold: u32; @@ -2922,7 +3239,7 @@ readonly xcmpMaxIndividualWeight: u64; } - /** @name CumulusPalletXcmpQueueError (340) */ + /** @name CumulusPalletXcmpQueueError (362) */ interface CumulusPalletXcmpQueueError extends Enum { readonly isFailedToSend: boolean; readonly isBadXcmOrigin: boolean; @@ -2932,7 +3249,7 @@ readonly type: 'FailedToSend' | 'BadXcmOrigin' | 'BadXcm' | 'BadOverweightIndex' | 'WeightOverLimit'; } - /** @name PalletXcmError (341) */ + /** @name PalletXcmError (363) */ interface PalletXcmError extends Enum { readonly isUnreachable: boolean; readonly isSendFailure: boolean; @@ -2950,29 +3267,29 @@ readonly type: 'Unreachable' | 'SendFailure' | 'Filtered' | 'UnweighableMessage' | 'DestinationNotInvertible' | 'Empty' | 'CannotReanchor' | 'TooManyAssets' | 'InvalidOrigin' | 'BadVersion' | 'BadLocation' | 'NoSubscription' | 'AlreadySubscribed'; } - /** @name CumulusPalletXcmError (342) */ + /** @name CumulusPalletXcmError (364) */ type CumulusPalletXcmError = Null; - /** @name CumulusPalletDmpQueueConfigData (343) */ + /** @name CumulusPalletDmpQueueConfigData (365) */ interface CumulusPalletDmpQueueConfigData extends Struct { readonly maxIndividual: u64; } - /** @name CumulusPalletDmpQueuePageIndexData (344) */ + /** @name CumulusPalletDmpQueuePageIndexData (366) */ interface CumulusPalletDmpQueuePageIndexData extends Struct { readonly beginUsed: u32; readonly endUsed: u32; readonly overweightCount: u64; } - /** @name CumulusPalletDmpQueueError (347) */ + /** @name CumulusPalletDmpQueueError (369) */ interface CumulusPalletDmpQueueError extends Enum { readonly isUnknown: boolean; readonly isOverLimit: boolean; readonly type: 'Unknown' | 'OverLimit'; } - /** @name PalletUniqueError (351) */ + /** @name PalletUniqueError (373) */ interface PalletUniqueError extends Enum { readonly isCollectionDecimalPointLimitExceeded: boolean; readonly isConfirmUnsetSponsorFail: boolean; @@ -2981,7 +3298,7 @@ readonly type: 'CollectionDecimalPointLimitExceeded' | 'ConfirmUnsetSponsorFail' | 'EmptyArgument' | 'RepartitionCalledOnNonRefungibleCollection'; } - /** @name PalletUniqueSchedulerScheduledV3 (354) */ + /** @name PalletUniqueSchedulerScheduledV3 (376) */ interface PalletUniqueSchedulerScheduledV3 extends Struct { readonly maybeId: Option; readonly priority: u8; @@ -2990,7 +3307,7 @@ readonly origin: OpalRuntimeOriginCaller; } - /** @name OpalRuntimeOriginCaller (355) */ + /** @name OpalRuntimeOriginCaller (377) */ interface OpalRuntimeOriginCaller extends Enum { readonly isSystem: boolean; readonly asSystem: FrameSupportDispatchRawOrigin; @@ -3004,7 +3321,7 @@ readonly type: 'System' | 'Void' | 'PolkadotXcm' | 'CumulusXcm' | 'Ethereum'; } - /** @name FrameSupportDispatchRawOrigin (356) */ + /** @name FrameSupportDispatchRawOrigin (378) */ interface FrameSupportDispatchRawOrigin extends Enum { readonly isRoot: boolean; readonly isSigned: boolean; @@ -3013,7 +3330,7 @@ readonly type: 'Root' | 'Signed' | 'None'; } - /** @name PalletXcmOrigin (357) */ + /** @name PalletXcmOrigin (379) */ interface PalletXcmOrigin extends Enum { readonly isXcm: boolean; readonly asXcm: XcmV1MultiLocation; @@ -3022,7 +3339,7 @@ readonly type: 'Xcm' | 'Response'; } - /** @name CumulusPalletXcmOrigin (358) */ + /** @name CumulusPalletXcmOrigin (380) */ interface CumulusPalletXcmOrigin extends Enum { readonly isRelay: boolean; readonly isSiblingParachain: boolean; @@ -3030,17 +3347,17 @@ readonly type: 'Relay' | 'SiblingParachain'; } - /** @name PalletEthereumRawOrigin (359) */ + /** @name PalletEthereumRawOrigin (381) */ interface PalletEthereumRawOrigin extends Enum { readonly isEthereumTransaction: boolean; readonly asEthereumTransaction: H160; readonly type: 'EthereumTransaction'; } - /** @name SpCoreVoid (360) */ + /** @name SpCoreVoid (382) */ type SpCoreVoid = Null; - /** @name PalletUniqueSchedulerError (361) */ + /** @name PalletUniqueSchedulerError (383) */ interface PalletUniqueSchedulerError extends Enum { readonly isFailedToSchedule: boolean; readonly isNotFound: boolean; @@ -3049,7 +3366,7 @@ readonly type: 'FailedToSchedule' | 'NotFound' | 'TargetBlockNumberInPast' | 'RescheduleNoChange'; } - /** @name UpDataStructsCollection (362) */ + /** @name UpDataStructsCollection (384) */ interface UpDataStructsCollection extends Struct { readonly owner: AccountId32; readonly mode: UpDataStructsCollectionMode; @@ -3059,10 +3376,10 @@ readonly sponsorship: UpDataStructsSponsorshipStateAccountId32; readonly limits: UpDataStructsCollectionLimits; readonly permissions: UpDataStructsCollectionPermissions; - readonly externalCollection: bool; + readonly flags: U8aFixed; } - /** @name UpDataStructsSponsorshipStateAccountId32 (363) */ + /** @name UpDataStructsSponsorshipStateAccountId32 (385) */ interface UpDataStructsSponsorshipStateAccountId32 extends Enum { readonly isDisabled: boolean; readonly isUnconfirmed: boolean; @@ -3072,43 +3389,43 @@ readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed'; } - /** @name UpDataStructsProperties (364) */ + /** @name UpDataStructsProperties (387) */ interface UpDataStructsProperties extends Struct { readonly map: UpDataStructsPropertiesMapBoundedVec; readonly consumedSpace: u32; readonly spaceLimit: u32; } - /** @name UpDataStructsPropertiesMapBoundedVec (365) */ + /** @name UpDataStructsPropertiesMapBoundedVec (388) */ interface UpDataStructsPropertiesMapBoundedVec extends BTreeMap {} - /** @name UpDataStructsPropertiesMapPropertyPermission (370) */ + /** @name UpDataStructsPropertiesMapPropertyPermission (393) */ interface UpDataStructsPropertiesMapPropertyPermission extends BTreeMap {} - /** @name UpDataStructsCollectionStats (377) */ + /** @name UpDataStructsCollectionStats (400) */ interface UpDataStructsCollectionStats extends Struct { readonly created: u32; readonly destroyed: u32; readonly alive: u32; } - /** @name UpDataStructsTokenChild (378) */ + /** @name UpDataStructsTokenChild (401) */ interface UpDataStructsTokenChild extends Struct { readonly token: u32; readonly collection: u32; } - /** @name PhantomTypeUpDataStructs (379) */ + /** @name PhantomTypeUpDataStructs (402) */ interface PhantomTypeUpDataStructs extends Vec> {} - /** @name UpDataStructsTokenData (381) */ + /** @name UpDataStructsTokenData (404) */ interface UpDataStructsTokenData extends Struct { readonly properties: Vec; readonly owner: Option; readonly pieces: u128; } - /** @name UpDataStructsRpcCollection (383) */ + /** @name UpDataStructsRpcCollection (406) */ interface UpDataStructsRpcCollection extends Struct { readonly owner: AccountId32; readonly mode: UpDataStructsCollectionMode; @@ -3121,9 +3438,10 @@ readonly tokenPropertyPermissions: Vec; readonly properties: Vec; readonly readOnly: bool; + readonly foreign: bool; } - /** @name RmrkTraitsCollectionCollectionInfo (384) */ + /** @name RmrkTraitsCollectionCollectionInfo (407) */ interface RmrkTraitsCollectionCollectionInfo extends Struct { readonly issuer: AccountId32; readonly metadata: Bytes; @@ -3132,7 +3450,7 @@ readonly nftsCount: u32; } - /** @name RmrkTraitsNftNftInfo (385) */ + /** @name RmrkTraitsNftNftInfo (408) */ interface RmrkTraitsNftNftInfo extends Struct { readonly owner: RmrkTraitsNftAccountIdOrCollectionNftTuple; readonly royalty: Option; @@ -3141,13 +3459,13 @@ readonly pending: bool; } - /** @name RmrkTraitsNftRoyaltyInfo (387) */ + /** @name RmrkTraitsNftRoyaltyInfo (410) */ interface RmrkTraitsNftRoyaltyInfo extends Struct { readonly recipient: AccountId32; readonly amount: Permill; } - /** @name RmrkTraitsResourceResourceInfo (388) */ + /** @name RmrkTraitsResourceResourceInfo (411) */ interface RmrkTraitsResourceResourceInfo extends Struct { readonly id: u32; readonly resource: RmrkTraitsResourceResourceTypes; @@ -3155,26 +3473,26 @@ readonly pendingRemoval: bool; } - /** @name RmrkTraitsPropertyPropertyInfo (389) */ + /** @name RmrkTraitsPropertyPropertyInfo (412) */ interface RmrkTraitsPropertyPropertyInfo extends Struct { readonly key: Bytes; readonly value: Bytes; } - /** @name RmrkTraitsBaseBaseInfo (390) */ + /** @name RmrkTraitsBaseBaseInfo (413) */ interface RmrkTraitsBaseBaseInfo extends Struct { readonly issuer: AccountId32; readonly baseType: Bytes; readonly symbol: Bytes; } - /** @name RmrkTraitsNftNftChild (391) */ + /** @name RmrkTraitsNftNftChild (414) */ interface RmrkTraitsNftNftChild extends Struct { readonly collectionId: u32; readonly nftId: u32; } - /** @name PalletCommonError (393) */ + /** @name PalletCommonError (416) */ interface PalletCommonError extends Enum { readonly isCollectionNotFound: boolean; readonly isMustBeTokenOwner: boolean; @@ -3213,7 +3531,7 @@ readonly type: 'CollectionNotFound' | 'MustBeTokenOwner' | 'NoPermission' | 'CantDestroyNotEmptyCollection' | 'PublicMintingNotAllowed' | 'AddressNotInAllowlist' | 'CollectionNameLimitExceeded' | 'CollectionDescriptionLimitExceeded' | 'CollectionTokenPrefixLimitExceeded' | 'TotalCollectionsLimitExceeded' | 'CollectionAdminCountExceeded' | 'CollectionLimitBoundsExceeded' | 'OwnerPermissionsCantBeReverted' | 'TransferNotAllowed' | 'AccountTokenLimitExceeded' | 'CollectionTokenLimitExceeded' | 'MetadataFlagFrozen' | 'TokenNotFound' | 'TokenValueTooLow' | 'ApprovedValueTooLow' | 'CantApproveMoreThanOwned' | 'AddressIsZero' | 'UnsupportedOperation' | 'NotSufficientFounds' | 'UserIsNotAllowedToNest' | 'SourceCollectionIsNotAllowedToNest' | 'CollectionFieldSizeExceeded' | 'NoSpaceForProperty' | 'PropertyLimitReached' | 'PropertyKeyIsTooLong' | 'InvalidCharacterInPropertyKey' | 'EmptyPropertyKey' | 'CollectionIsExternal' | 'CollectionIsInternal'; } - /** @name PalletFungibleError (395) */ + /** @name PalletFungibleError (418) */ interface PalletFungibleError extends Enum { readonly isNotFungibleDataUsedToMintFungibleCollectionToken: boolean; readonly isFungibleItemsHaveNoId: boolean; @@ -3223,12 +3541,12 @@ readonly type: 'NotFungibleDataUsedToMintFungibleCollectionToken' | 'FungibleItemsHaveNoId' | 'FungibleItemsDontHaveData' | 'FungibleDisallowsNesting' | 'SettingPropertiesNotAllowed'; } - /** @name PalletRefungibleItemData (396) */ + /** @name PalletRefungibleItemData (419) */ interface PalletRefungibleItemData extends Struct { readonly constData: Bytes; } - /** @name PalletRefungibleError (401) */ + /** @name PalletRefungibleError (424) */ interface PalletRefungibleError extends Enum { readonly isNotRefungibleDataUsedToMintFungibleCollectionToken: boolean; readonly isWrongRefungiblePieces: boolean; @@ -3238,19 +3556,19 @@ readonly type: 'NotRefungibleDataUsedToMintFungibleCollectionToken' | 'WrongRefungiblePieces' | 'RepartitionWhileNotOwningAllPieces' | 'RefungibleDisallowsNesting' | 'SettingPropertiesNotAllowed'; } - /** @name PalletNonfungibleItemData (402) */ + /** @name PalletNonfungibleItemData (425) */ interface PalletNonfungibleItemData extends Struct { readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; } - /** @name UpDataStructsPropertyScope (404) */ + /** @name UpDataStructsPropertyScope (427) */ interface UpDataStructsPropertyScope extends Enum { readonly isNone: boolean; readonly isRmrk: boolean; readonly type: 'None' | 'Rmrk'; } - /** @name PalletNonfungibleError (406) */ + /** @name PalletNonfungibleError (429) */ interface PalletNonfungibleError extends Enum { readonly isNotNonfungibleDataUsedToMintFungibleCollectionToken: boolean; readonly isNonfungibleItemsHaveNoAmount: boolean; @@ -3258,7 +3576,7 @@ readonly type: 'NotNonfungibleDataUsedToMintFungibleCollectionToken' | 'NonfungibleItemsHaveNoAmount' | 'CantBurnNftWithChildren'; } - /** @name PalletStructureError (407) */ + /** @name PalletStructureError (430) */ interface PalletStructureError extends Enum { readonly isOuroborosDetected: boolean; readonly isDepthLimit: boolean; @@ -3267,7 +3585,7 @@ readonly type: 'OuroborosDetected' | 'DepthLimit' | 'BreadthLimit' | 'TokenNotFound'; } - /** @name PalletRmrkCoreError (408) */ + /** @name PalletRmrkCoreError (431) */ interface PalletRmrkCoreError extends Enum { readonly isCorruptedCollectionType: boolean; readonly isRmrkPropertyKeyIsTooLong: boolean; @@ -3291,7 +3609,7 @@ readonly type: 'CorruptedCollectionType' | 'RmrkPropertyKeyIsTooLong' | 'RmrkPropertyValueIsTooLong' | 'RmrkPropertyIsNotFound' | 'UnableToDecodeRmrkData' | 'CollectionNotEmpty' | 'NoAvailableCollectionId' | 'NoAvailableNftId' | 'CollectionUnknown' | 'NoPermission' | 'NonTransferable' | 'CollectionFullOrLocked' | 'ResourceDoesntExist' | 'CannotSendToDescendentOrSelf' | 'CannotAcceptNonOwnedNft' | 'CannotRejectNonOwnedNft' | 'CannotRejectNonPendingNft' | 'ResourceNotPending' | 'NoAvailableResourceId'; } - /** @name PalletRmrkEquipError (410) */ + /** @name PalletRmrkEquipError (433) */ interface PalletRmrkEquipError extends Enum { readonly isPermissionError: boolean; readonly isNoAvailableBaseId: boolean; @@ -3303,7 +3621,7 @@ readonly type: 'PermissionError' | 'NoAvailableBaseId' | 'NoAvailablePartId' | 'BaseDoesntExist' | 'NeedsDefaultThemeFirst' | 'PartDoesntExist' | 'NoEquippableOnFixedPart'; } - /** @name PalletAppPromotionError (416) */ + /** @name PalletAppPromotionError (439) */ interface PalletAppPromotionError extends Enum { readonly isAdminNotSet: boolean; readonly isNoPermission: boolean; @@ -3314,7 +3632,16 @@ readonly type: 'AdminNotSet' | 'NoPermission' | 'NotSufficientFunds' | 'PendingForBlockOverflow' | 'SponsorNotSet' | 'IncorrectLockedBalanceOperation'; } - /** @name PalletEvmError (419) */ + /** @name PalletForeignAssetsModuleError (440) */ + interface PalletForeignAssetsModuleError extends Enum { + readonly isBadLocation: boolean; + readonly isMultiLocationExisted: boolean; + readonly isAssetIdNotExists: boolean; + readonly isAssetIdExisted: boolean; + readonly type: 'BadLocation' | 'MultiLocationExisted' | 'AssetIdNotExists' | 'AssetIdExisted'; + } + + /** @name PalletEvmError (443) */ interface PalletEvmError extends Enum { readonly isBalanceLow: boolean; readonly isFeeOverflow: boolean; @@ -3325,7 +3652,7 @@ readonly type: 'BalanceLow' | 'FeeOverflow' | 'PaymentOverflow' | 'WithdrawFailed' | 'GasPriceTooLow' | 'InvalidNonce'; } - /** @name FpRpcTransactionStatus (422) */ + /** @name FpRpcTransactionStatus (446) */ interface FpRpcTransactionStatus extends Struct { readonly transactionHash: H256; readonly transactionIndex: u32; @@ -3336,10 +3663,10 @@ readonly logsBloom: EthbloomBloom; } - /** @name EthbloomBloom (424) */ + /** @name EthbloomBloom (448) */ interface EthbloomBloom extends U8aFixed {} - /** @name EthereumReceiptReceiptV3 (426) */ + /** @name EthereumReceiptReceiptV3 (450) */ interface EthereumReceiptReceiptV3 extends Enum { readonly isLegacy: boolean; readonly asLegacy: EthereumReceiptEip658ReceiptData; @@ -3350,7 +3677,7 @@ readonly type: 'Legacy' | 'Eip2930' | 'Eip1559'; } - /** @name EthereumReceiptEip658ReceiptData (427) */ + /** @name EthereumReceiptEip658ReceiptData (451) */ interface EthereumReceiptEip658ReceiptData extends Struct { readonly statusCode: u8; readonly usedGas: U256; @@ -3358,14 +3685,14 @@ readonly logs: Vec; } - /** @name EthereumBlock (428) */ + /** @name EthereumBlock (452) */ interface EthereumBlock extends Struct { readonly header: EthereumHeader; readonly transactions: Vec; readonly ommers: Vec; } - /** @name EthereumHeader (429) */ + /** @name EthereumHeader (453) */ interface EthereumHeader extends Struct { readonly parentHash: H256; readonly ommersHash: H256; @@ -3384,24 +3711,24 @@ readonly nonce: EthereumTypesHashH64; } - /** @name EthereumTypesHashH64 (430) */ + /** @name EthereumTypesHashH64 (454) */ interface EthereumTypesHashH64 extends U8aFixed {} - /** @name PalletEthereumError (435) */ + /** @name PalletEthereumError (459) */ interface PalletEthereumError extends Enum { readonly isInvalidSignature: boolean; readonly isPreLogExists: boolean; readonly type: 'InvalidSignature' | 'PreLogExists'; } - /** @name PalletEvmCoderSubstrateError (436) */ + /** @name PalletEvmCoderSubstrateError (460) */ interface PalletEvmCoderSubstrateError extends Enum { readonly isOutOfGas: boolean; readonly isOutOfFund: boolean; readonly type: 'OutOfGas' | 'OutOfFund'; } - /** @name UpDataStructsSponsorshipStateBasicCrossAccountIdRepr (437) */ + /** @name UpDataStructsSponsorshipStateBasicCrossAccountIdRepr (461) */ interface UpDataStructsSponsorshipStateBasicCrossAccountIdRepr extends Enum { readonly isDisabled: boolean; readonly isUnconfirmed: boolean; @@ -3411,7 +3738,7 @@ readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed'; } - /** @name PalletEvmContractHelpersSponsoringModeT (438) */ + /** @name PalletEvmContractHelpersSponsoringModeT (462) */ interface PalletEvmContractHelpersSponsoringModeT extends Enum { readonly isDisabled: boolean; readonly isAllowlisted: boolean; @@ -3419,21 +3746,22 @@ readonly type: 'Disabled' | 'Allowlisted' | 'Generous'; } - /** @name PalletEvmContractHelpersError (440) */ + /** @name PalletEvmContractHelpersError (468) */ interface PalletEvmContractHelpersError extends Enum { readonly isNoPermission: boolean; readonly isNoPendingSponsor: boolean; - readonly type: 'NoPermission' | 'NoPendingSponsor'; + readonly isTooManyMethodsHaveSponsoredLimit: boolean; + readonly type: 'NoPermission' | 'NoPendingSponsor' | 'TooManyMethodsHaveSponsoredLimit'; } - /** @name PalletEvmMigrationError (441) */ + /** @name PalletEvmMigrationError (469) */ interface PalletEvmMigrationError extends Enum { readonly isAccountNotEmpty: boolean; readonly isAccountIsNotMigrating: boolean; readonly type: 'AccountNotEmpty' | 'AccountIsNotMigrating'; } - /** @name SpRuntimeMultiSignature (443) */ + /** @name SpRuntimeMultiSignature (471) */ interface SpRuntimeMultiSignature extends Enum { readonly isEd25519: boolean; readonly asEd25519: SpCoreEd25519Signature; @@ -3444,34 +3772,34 @@ readonly type: 'Ed25519' | 'Sr25519' | 'Ecdsa'; } - /** @name SpCoreEd25519Signature (444) */ + /** @name SpCoreEd25519Signature (472) */ interface SpCoreEd25519Signature extends U8aFixed {} - /** @name SpCoreSr25519Signature (446) */ + /** @name SpCoreSr25519Signature (474) */ interface SpCoreSr25519Signature extends U8aFixed {} - /** @name SpCoreEcdsaSignature (447) */ + /** @name SpCoreEcdsaSignature (475) */ interface SpCoreEcdsaSignature extends U8aFixed {} - /** @name FrameSystemExtensionsCheckSpecVersion (450) */ + /** @name FrameSystemExtensionsCheckSpecVersion (478) */ type FrameSystemExtensionsCheckSpecVersion = Null; - /** @name FrameSystemExtensionsCheckGenesis (451) */ + /** @name FrameSystemExtensionsCheckGenesis (479) */ type FrameSystemExtensionsCheckGenesis = Null; - /** @name FrameSystemExtensionsCheckNonce (454) */ + /** @name FrameSystemExtensionsCheckNonce (482) */ interface FrameSystemExtensionsCheckNonce extends Compact {} - /** @name FrameSystemExtensionsCheckWeight (455) */ + /** @name FrameSystemExtensionsCheckWeight (483) */ type FrameSystemExtensionsCheckWeight = Null; - /** @name PalletTemplateTransactionPaymentChargeTransactionPayment (456) */ + /** @name PalletTemplateTransactionPaymentChargeTransactionPayment (484) */ interface PalletTemplateTransactionPaymentChargeTransactionPayment extends Compact {} - /** @name OpalRuntimeRuntime (457) */ + /** @name OpalRuntimeRuntime (485) */ type OpalRuntimeRuntime = Null; - /** @name PalletEthereumFakeTransactionFinalizer (458) */ + /** @name PalletEthereumFakeTransactionFinalizer (486) */ type PalletEthereumFakeTransactionFinalizer = Null; } // declare module --- a/tests/src/mintModes.test.ts +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {IKeyringPair} from '@polkadot/types/types'; -import usingApi from './substrate/substrate-api'; -import { - addToAllowListExpectSuccess, - createCollectionExpectSuccess, - createItemExpectFailure, - createItemExpectSuccess, - enableAllowListExpectSuccess, - setMintPermissionExpectSuccess, - addCollectionAdminExpectSuccess, - disableAllowListExpectSuccess, -} from './util/helpers'; - -describe('Integration Test public minting', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); - }); - }); - - it('If the AllowList mode is enabled, then the address added to the allowlist and not the owner or administrator can create tokens', async () => { - await usingApi(async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await enableAllowListExpectSuccess(alice, collectionId); - await setMintPermissionExpectSuccess(alice, collectionId, true); - await addToAllowListExpectSuccess(alice, collectionId, bob.address); - - await createItemExpectSuccess(bob, collectionId, 'NFT'); - }); - }); - - it('If the AllowList mode is enabled, address not included in allowlist that is regular user cannot create tokens', async () => { - await usingApi(async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await enableAllowListExpectSuccess(alice, collectionId); - await setMintPermissionExpectSuccess(alice, collectionId, true); - await createItemExpectFailure(bob, collectionId, 'NFT'); - }); - }); - - it('If the AllowList mode is enabled, address not included in allowlist that is admin can create tokens', async () => { - await usingApi(async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await enableAllowListExpectSuccess(alice, collectionId); - await setMintPermissionExpectSuccess(alice, collectionId, true); - await addCollectionAdminExpectSuccess(alice, collectionId, bob.address); - await createItemExpectSuccess(bob, collectionId, 'NFT'); - }); - }); - - it('If the AllowList mode is enabled, address not included in allowlist that is owner can create tokens', async () => { - await usingApi(async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await enableAllowListExpectSuccess(alice, collectionId); - await setMintPermissionExpectSuccess(alice, collectionId, true); - await createItemExpectSuccess(alice, collectionId, 'NFT'); - }); - }); - - it('If the AllowList mode is disabled, owner can create tokens', async () => { - await usingApi(async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await disableAllowListExpectSuccess(alice, collectionId); - await setMintPermissionExpectSuccess(alice, collectionId, true); - await createItemExpectSuccess(alice, collectionId, 'NFT'); - }); - }); - - it('If the AllowList mode is disabled, collection admin can create tokens', async () => { - await usingApi(async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await disableAllowListExpectSuccess(alice, collectionId); - await setMintPermissionExpectSuccess(alice, collectionId, true); - await addCollectionAdminExpectSuccess(alice, collectionId, bob.address); - await createItemExpectSuccess(bob, collectionId, 'NFT'); - }); - }); - - it('If the AllowList mode is disabled, regular user can`t create tokens', async () => { - await usingApi(async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await disableAllowListExpectSuccess(alice, collectionId); - await setMintPermissionExpectSuccess(alice, collectionId, true); - await createItemExpectFailure(bob, collectionId, 'NFT'); - }); - }); -}); - -describe('Integration Test private minting', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); - }); - }); - - it('Address that is the not owner or not admin cannot create tokens', async () => { - await usingApi(async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await enableAllowListExpectSuccess(alice, collectionId); - await setMintPermissionExpectSuccess(alice, collectionId, false); - await addToAllowListExpectSuccess(alice, collectionId, bob.address); - await createItemExpectFailure(bob, collectionId, 'NFT'); - }); - }); - - it('Address that is collection owner can create tokens', async () => { - await usingApi(async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await disableAllowListExpectSuccess(alice, collectionId); - await setMintPermissionExpectSuccess(alice, collectionId, false); - await createItemExpectSuccess(alice, collectionId, 'NFT'); - }); - }); - - it('Address that is admin can create tokens', async () => { - await usingApi(async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await disableAllowListExpectSuccess(alice, collectionId); - await setMintPermissionExpectSuccess(alice, collectionId, false); - await addCollectionAdminExpectSuccess(alice, collectionId, bob.address); - await createItemExpectSuccess(bob, collectionId, 'NFT'); - }); - }); -}); --- a/tests/src/pallet-presence.test.ts +++ b/tests/src/pallet-presence.test.ts @@ -45,6 +45,8 @@ 'nonfungible', 'charging', 'configuration', + 'tokens', + 'xtokens', ]; // Pallets that depend on consensus and governance configuration @@ -61,13 +63,20 @@ const refungible = 'refungible'; const scheduler = 'scheduler'; + const foreignAssets = 'foreignassets'; const rmrkPallets = ['rmrkcore', 'rmrkequip']; const appPromotion = 'apppromotion'; if (chain.eq('OPAL by UNIQUE')) { - requiredPallets.push(refungible, scheduler, appPromotion, ...rmrkPallets); + requiredPallets.push( + refungible, + scheduler, + foreignAssets, + appPromotion, + ...rmrkPallets, + ); } else if (chain.eq('QUARTZ by UNIQUE')) { - // Insert Quartz additional pallets here + requiredPallets.push(refungible); } else if (chain.eq('UNIQUE')) { // Insert Unique additional pallets here } --- a/tests/src/refungible.test.ts +++ b/tests/src/refungible.test.ts @@ -17,17 +17,18 @@ import {IKeyringPair} from '@polkadot/types/types'; import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from './util/playgrounds'; -let alice: IKeyringPair; -let bob: IKeyringPair; const MAX_REFUNGIBLE_PIECES = 1_000_000_000_000_000_000_000n; describe('integration test: Refungible functionality:', async () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + before(async function() { await usingPlaygrounds(async (helper, privateKey) => { requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - alice = privateKey('//Alice'); - bob = privateKey('//Bob'); + const donor = privateKey('//Alice'); + [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor); }); }); @@ -209,36 +210,38 @@ const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); const token = await collection.mintToken(alice, 100n); await token.repartition(alice, 200n); - const chainEvents = helper.chainLog.slice(-1)[0].events.map((x: any) => x.event); - expect(chainEvents).to.include.deep.members([{ - method: 'ItemCreated', + const chainEvents = helper.chainLog.slice(-1)[0].events; + expect(chainEvents).to.deep.include({ section: 'common', - index: '0x4202', - data: [ - helper.api!.createType('u32', collection.collectionId).toHuman(), - helper.api!.createType('u32', token.tokenId).toHuman(), - {Substrate: alice.address}, - '100', + method: 'ItemCreated', + index: [66, 2], + data: [ + collection.collectionId, + token.tokenId, + {substrate: alice.address}, + 100n, ], - }]); + phase: {applyExtrinsic: 2}, + }); }); itSub('Repartition with decreased amount', async ({helper}) => { const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); const token = await collection.mintToken(alice, 100n); await token.repartition(alice, 50n); - const chainEvents = helper.chainLog.slice(-1)[0].events.map((x: any) => x.event); - expect(chainEvents).to.include.deep.members([{ + const chainEvents = helper.chainLog.slice(-1)[0].events; + expect(chainEvents).to.deep.include({ method: 'ItemDestroyed', section: 'common', - index: '0x4203', - data: [ - helper.api!.createType('u32', collection.collectionId).toHuman(), - helper.api!.createType('u32', token.tokenId).toHuman(), - {Substrate: alice.address}, - '50', + index: [66, 3], + data: [ + collection.collectionId, + token.tokenId, + {substrate: alice.address}, + 50n, ], - }]); + phase: {applyExtrinsic: 2}, + }); }); itSub('Create new collection with properties', async ({helper}) => { --- a/tests/src/removeFromAllowList.test.ts +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {IKeyringPair} from '@polkadot/types/types'; -import {itSub, usingPlaygrounds, expect} from './util/playgrounds'; - -describe('Integration Test removeFromAllowList', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = privateKey('//Alice'); - [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); - }); - }); - - itSub('ensure bob is not in allowlist after removal', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'RemoveFromAllowList-1', tokenPrefix: 'RFAL'}); - - const collectionInfo = await collection.getData(); - expect(collectionInfo!.raw.permissions.access).to.not.equal('AllowList'); - - await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); - await collection.addToAllowList(alice, {Substrate: bob.address}); - expect(await collection.getAllowList()).to.deep.contains({Substrate: bob.address}); - - await collection.removeFromAllowList(alice, {Substrate: bob.address}); - expect(await collection.getAllowList()).to.be.empty; - }); - - itSub('allows removal from collection with unset allowlist status', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'RemoveFromAllowList-2', tokenPrefix: 'RFAL'}); - - await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); - await collection.addToAllowList(alice, {Substrate: bob.address}); - expect(await collection.getAllowList()).to.deep.contains({Substrate: bob.address}); - - await collection.setPermissions(alice, {access: 'Normal'}); - - await collection.removeFromAllowList(alice, {Substrate: bob.address}); - expect(await collection.getAllowList()).to.be.empty; - }); -}); - -describe('Negative Integration Test removeFromAllowList', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = privateKey('//Alice'); - [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); - }); - }); - - itSub('fails on removal from not existing collection', async ({helper}) => { - const nonExistentCollectionId = (1 << 32) - 1; - await expect(helper.collection.removeFromAllowList(alice, nonExistentCollectionId, {Substrate: alice.address})) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('fails on removal from removed collection', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'RemoveFromAllowList-3', tokenPrefix: 'RFAL'}); - await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); - await collection.addToAllowList(alice, {Substrate: bob.address}); - - await collection.burn(alice); - await expect(collection.removeFromAllowList(alice, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); -}); - -describe('Integration Test removeFromAllowList with collection admin permissions', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = privateKey('//Alice'); - [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); - }); - }); - - itSub('ensure address is not in allowlist after removal', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'RemoveFromAllowList-4', tokenPrefix: 'RFAL'}); - - await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); - await collection.addAdmin(alice, {Substrate: bob.address}); - - await collection.addToAllowList(bob, {Substrate: charlie.address}); - await collection.removeFromAllowList(bob, {Substrate: charlie.address}); - - expect(await collection.getAllowList()).to.be.empty; - }); - - itSub('Collection admin allowed to remove from allowlist with unset allowlist status', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'RemoveFromAllowList-5', tokenPrefix: 'RFAL'}); - - await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); - await collection.addAdmin(alice, {Substrate: bob.address}); - await collection.addToAllowList(alice, {Substrate: charlie.address}); - - await collection.setPermissions(bob, {access: 'Normal'}); - await collection.removeFromAllowList(bob, {Substrate: charlie.address}); - - expect(await collection.getAllowList()).to.be.empty; - }); - - itSub('Regular user can`t remove from allowlist', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'RemoveFromAllowList-6', tokenPrefix: 'RFAL'}); - - await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); - await collection.addToAllowList(alice, {Substrate: charlie.address}); - - await expect(collection.removeFromAllowList(bob, {Substrate: charlie.address})) - .to.be.rejectedWith(/common\.NoPermission/); - expect(await collection.getAllowList()).to.deep.contain({Substrate: charlie.address}); - }); -}); --- a/tests/src/setContractSponsoringRateLimit.test.ts +++ b/tests/src/setContractSponsoringRateLimit.test.ts @@ -25,7 +25,7 @@ setContractSponsoringRateLimitExpectSuccess, } from './util/helpers'; -// todo:playgrounds postponed skipped test +// todo:playgrounds skipped~postponed test describe.skip('Integration Test setContractSponsoringRateLimit', () => { it('ensure sponsored contract can\'t be called twice without pause for free', async () => { await usingApi(async (api, privateKeyWrapper) => { --- a/tests/src/setMintPermission.test.ts +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {IKeyringPair} from '@polkadot/types/types'; -import {itSub, usingPlaygrounds, expect} from './util/playgrounds'; - -describe('Integration Test setMintPermission', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = privateKey('//Alice'); - [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); - }); - }); - - itSub('ensure allow-listed non-privileged address can mint tokens', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetMintPermission-1', description: '', tokenPrefix: 'SMP'}); - await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); - await collection.addToAllowList(alice, {Substrate: bob.address}); - - await expect(collection.mintToken(bob, {Substrate: bob.address})).to.not.be.rejected; - }); - - itSub('can be enabled twice', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetMintPermission-2', description: '', tokenPrefix: 'SMP'}); - expect((await collection.getData())?.raw.permissions.access).to.not.equal('AllowList'); - - await collection.setPermissions(alice, {mintMode: true}); - await collection.setPermissions(alice, {mintMode: true}); - expect((await collection.getData())?.raw.permissions.mintMode).to.be.true; - }); - - itSub('can be disabled twice', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetMintPermission-3', description: '', tokenPrefix: 'SMP'}); - expect((await collection.getData())?.raw.permissions.access).to.equal('Normal'); - - await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); - expect((await collection.getData())?.raw.permissions.access).to.equal('AllowList'); - expect((await collection.getData())?.raw.permissions.mintMode).to.equal(true); - - await collection.setPermissions(alice, {access: 'Normal', mintMode: false}); - await collection.setPermissions(alice, {access: 'Normal', mintMode: false}); - expect((await collection.getData())?.raw.permissions.access).to.equal('Normal'); - expect((await collection.getData())?.raw.permissions.mintMode).to.equal(false); - }); - - itSub('Collection admin success on set', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetMintPermission-4', description: '', tokenPrefix: 'SMP'}); - await collection.addAdmin(alice, {Substrate: bob.address}); - await collection.setPermissions(bob, {access: 'AllowList', mintMode: true}); - - expect((await collection.getData())?.raw.permissions.access).to.equal('AllowList'); - expect((await collection.getData())?.raw.permissions.mintMode).to.equal(true); - }); -}); - -describe('Negative Integration Test setMintPermission', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = privateKey('//Alice'); - [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); - }); - }); - - itSub('fails on not existing collection', async ({helper}) => { - const collectionId = (1 << 32) - 1; - await expect(helper.collection.setPermissions(alice, collectionId, {mintMode: true})) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('fails on removed collection', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetMintPermission-Neg-1', tokenPrefix: 'SMP'}); - await collection.burn(alice); - - await expect(collection.setPermissions(alice, {mintMode: true})) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('fails when non-owner tries to set mint status', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetMintPermission-Neg-2', tokenPrefix: 'SMP'}); - - await expect(collection.setPermissions(bob, {mintMode: true})) - .to.be.rejectedWith(/common\.NoPermission/); - }); - - itSub('ensure non-allow-listed non-privileged address can\'t mint tokens', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetMintPermission-Neg-3', tokenPrefix: 'SMP'}); - await collection.setPermissions(alice, {mintMode: true}); - - await expect(collection.mintToken(bob, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.AddressNotInAllowlist/); - }); -}); --- /dev/null +++ b/tests/src/setPermissions.test.ts @@ -0,0 +1,107 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {IKeyringPair} from '@polkadot/types/types'; +import {itSub, usingPlaygrounds, expect} from './util/playgrounds'; + +describe('Integration Test: Set Permissions', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); + }); + }); + + itSub('can all be enabled twice', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-1', tokenPrefix: 'SP'}); + expect((await collection.getData())?.raw.permissions.access).to.not.equal('AllowList'); + + await collection.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true, restricted: [1, 2]}}); + await collection.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true, restricted: [1, 2]}}); + + const permissions = (await collection.getData())?.raw.permissions; + expect(permissions).to.be.deep.equal({ + access: 'AllowList', + mintMode: true, + nesting: {collectionAdmin: true, tokenOwner: true, restricted: [1, 2]}, + }); + }); + + itSub('can all be disabled twice', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-2', tokenPrefix: 'SP'}); + expect((await collection.getData())?.raw.permissions.access).to.equal('Normal'); + + await collection.setPermissions(alice, {access: 'AllowList', nesting: {collectionAdmin: false, tokenOwner: true, restricted: [1, 2]}}); + expect((await collection.getData())?.raw.permissions).to.be.deep.equal({ + access: 'AllowList', + mintMode: false, + nesting: {collectionAdmin: false, tokenOwner: true, restricted: [1, 2]}, + }); + + await collection.setPermissions(alice, {access: 'Normal', mintMode: false, nesting: {}}); + await collection.setPermissions(alice, {access: 'Normal', mintMode: false, nesting: {}}); + expect((await collection.getData())?.raw.permissions).to.be.deep.equal({ + access: 'Normal', + mintMode: false, + nesting: {collectionAdmin: false, tokenOwner: false, restricted: null}, + }); + }); + + itSub('collection admin can set permissions', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-2', tokenPrefix: 'SP'}); + await collection.addAdmin(alice, {Substrate: bob.address}); + await collection.setPermissions(bob, {access: 'AllowList', mintMode: true}); + + expect((await collection.getData())?.raw.permissions.access).to.equal('AllowList'); + expect((await collection.getData())?.raw.permissions.mintMode).to.equal(true); + }); +}); + +describe('Negative Integration Test: Set Permissions', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); + }); + }); + + itSub('fails on not existing collection', async ({helper}) => { + const collectionId = (1 << 32) - 1; + await expect(helper.collection.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: true})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('fails on removed collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-Neg-1', tokenPrefix: 'SP'}); + await collection.burn(alice); + + await expect(collection.setPermissions(alice, {access: 'AllowList', mintMode: true})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('fails when non-owner tries to set permissions', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-Neg-2', tokenPrefix: 'SP'}); + + await expect(collection.setPermissions(bob, {access: 'AllowList', mintMode: true})) + .to.be.rejectedWith(/common\.NoPermission/); + }); +}); \ No newline at end of file --- a/tests/src/setPublicAccessMode.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -// https://unique-network.readthedocs.io/en/latest/jsapi.html#setschemaversion -import {IKeyringPair} from '@polkadot/types/types'; -import {itSub, usingPlaygrounds, expect} from './util/playgrounds'; - -describe('Integration Test setPublicAccessMode(): ', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = privateKey('//Alice'); - [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); - }); - }); - - itSub('Runs extrinsic with collection id parameters, sets the allowlist mode for the collection', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'PublicAccess-1', tokenPrefix: 'TF'}); - await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); - await collection.addToAllowList(alice, {Substrate: bob.address}); - - await expect(collection.mintToken(bob, {Substrate: bob.address})).to.be.not.rejected; - }); - - itSub('Allowlisted collection limits', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'PublicAccess-2', tokenPrefix: 'TF'}); - await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); - - await expect(collection.mintToken(bob, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.AddressNotInAllowlist/); - }); - - itSub('setPublicAccessMode by collection admin', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'PublicAccess-Neg-4', tokenPrefix: 'TF'}); - await collection.addAdmin(alice, {Substrate: bob.address}); - - await expect(collection.setPermissions(bob, {access: 'AllowList'})).to.be.not.rejected; - }); -}); - -describe('Negative Integration Test ext. setPublicAccessMode(): ', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = privateKey('//Alice'); - [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); - }); - }); - - itSub('Sets a non-existent collection', async ({helper}) => { - const collectionId = (1 << 32) - 1; - await expect(helper.collection.setPermissions(alice, collectionId, {access: 'AllowList'})) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('Sets the collection that has been deleted', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'PublicAccess-Neg-1', tokenPrefix: 'TF'}); - await collection.burn(alice); - - await expect(collection.setPermissions(alice, {access: 'AllowList'})) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('Re-sets the list mode already set in quantity', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'PublicAccess-Neg-2', tokenPrefix: 'TF'}); - await collection.setPermissions(alice, {access: 'AllowList'}); - await collection.setPermissions(alice, {access: 'AllowList'}); - }); - - itSub('Executes method as a malefactor', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'PublicAccess-Neg-3', tokenPrefix: 'TF'}); - - await expect(collection.setPermissions(bob, {access: 'AllowList'})) - .to.be.rejectedWith(/common\.NoPermission/); - }); -}); --- a/tests/src/substrate/substrate-api.ts +++ b/tests/src/substrate/substrate-api.ts @@ -110,6 +110,9 @@ if (status.isBroadcast) { return TransactionStatus.NotReady; } + if (status.isRetracted) { + return TransactionStatus.NotReady; + } if (status.isInBlock || status.isFinalized) { if(events.filter(e => e.event.data.method === 'ExtrinsicFailed').length > 0) { return TransactionStatus.Fail; @@ -148,18 +151,32 @@ }); } +/** + * @deprecated use `executeTransaction` instead + */ export function submitTransactionAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic): Promise { /* eslint no-async-promise-executor: "off" */ return new Promise(async (resolve, reject) => { try { - await transaction.signAndSend(sender, ({events = [], status}) => { + await transaction.signAndSend(sender, ({events = [], status, dispatchError}) => { const transactionStatus = getTransactionStatus(events, status); if (transactionStatus === TransactionStatus.Success) { resolve(events); } else if (transactionStatus === TransactionStatus.Fail) { - console.log(`Something went wrong with transaction. Status: ${status}`); + let moduleError = null; + + if (dispatchError) { + if (dispatchError.isModule) { + const modErr = dispatchError.asModule; + const errorMeta = dispatchError.registry.findMetaError(modErr); + + moduleError = JSON.stringify(errorMeta, null, 4); + } + } + + console.log(`Something went wrong with transaction. Status: ${status}\nModule error: ${moduleError}`); reject(events); } }); --- /dev/null +++ b/tests/src/tx-version-presence.test.ts @@ -0,0 +1,32 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {Metadata} from '@polkadot/types'; +import {itSub, usingPlaygrounds, expect} from './util/playgrounds'; + +let metadata: Metadata; + +describe('TxVersion is present', () => { + before(async () => { + await usingPlaygrounds(async helper => { + metadata = await helper.api!.rpc.state.getMetadata(); + }); + }); + + itSub('Signed extension CheckTxVersion is present', async () => { + expect(metadata.asLatest.extrinsic.signedExtensions.map(se => se.identifier.toString())).to.include('CheckTxVersion'); + }); +}); --- a/tests/src/util/helpers.ts +++ b/tests/src/util/helpers.ts @@ -16,7 +16,7 @@ import '../interfaces/augment-api-rpc'; import '../interfaces/augment-api-query'; -import {ApiPromise} from '@polkadot/api'; +import {ApiPromise, Keyring} from '@polkadot/api'; import type {AccountId, EventRecord, Event, BlockNumber} from '@polkadot/types/interfaces'; import type {GenericEventData} from '@polkadot/types'; import {AnyTuple, IEvent, IKeyringPair} from '@polkadot/types/types'; @@ -63,14 +63,14 @@ export async function isQuartz(): Promise { return usingApi(async api => { const chain = await api.rpc.system.chain(); - + return chain.eq('QUARTZ'); }); } let modulesNames: any; export function getModuleNames(api: ApiPromise): string[] { - if (typeof modulesNames === 'undefined') + if (typeof modulesNames === 'undefined') modulesNames = api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase()); return modulesNames; } @@ -105,6 +105,19 @@ return api.registry.createType('AccountId', '0x' + number.toString(16).padStart(64, '0')).toJSON(); } +export function bigIntToDecimals(number: bigint, decimals = 18): string { + const numberStr = number.toString(); + const dotPos = numberStr.length - decimals; + + if (dotPos <= 0) { + return '0.' + '0'.repeat(Math.abs(dotPos)) + numberStr; + } else { + const intPart = numberStr.substring(0, dotPos); + const fractPart = numberStr.substring(dotPos); + return intPart + '.' + fractPart; + } +} + export function normalizeAccountId(input: string | AccountId | CrossAccountId | IKeyringPair): CrossAccountId { if (typeof input === 'string') { if (input.length >= 47) { @@ -291,7 +304,7 @@ export function getCreateItemsResult(events: EventRecord[]): CreateItemResult[] { const results: CreateItemResult[] = []; - + const genericResult = getGenericResult(events, 'common', 'ItemCreated', (data) => { const collectionId = parseInt(data[0].toString(), 10); const itemId = parseInt(data[1].toString(), 10); @@ -316,15 +329,15 @@ export function getCreateItemResult(events: EventRecord[]): CreateItemResult { const genericResult = getGenericResult(events, 'common', 'ItemCreated', (data) => data.map(function(value) { return value.toJSON(); })); - - if (genericResult.data == null) + + if (genericResult.data == null) return { success: genericResult.success, collectionId: 0, itemId: 0, amount: 0, }; - else + else return { success: genericResult.success, collectionId: genericResult.data[0] as number, @@ -336,7 +349,7 @@ export function getDestroyItemsResult(events: EventRecord[]): DestroyItemResult[] { const results: DestroyItemResult[] = []; - + const genericResult = getGenericResult(events, 'common', 'ItemDestroyed', (data) => { const collectionId = parseInt(data[0].toString(), 10); const itemId = parseInt(data[1].toString(), 10); @@ -1102,6 +1115,19 @@ return balance; } +export async function paraSiblingSovereignAccount(paraid: number): Promise { + return usingApi(async api => { + // We are getting a *sibling* parachain sovereign account, + // so we need a sibling prefix: encoded(b"sibl") == 0x7369626c + const siblingPrefix = '0x7369626c'; + + const encodedParaId = api.createType('u32', paraid).toHex(true).substring(2); + const suffix = '000000000000000000000000000000000000000000000000'; + + return siblingPrefix + encodedParaId + suffix; + }); +} + export async function transferBalanceTo(api: ApiPromise, source: IKeyringPair, target: string, amount = 1000n * UNIQUE) { const tx = api.tx.balances.transfer(target, amount); const events = await submitTransactionAsync(source, tx); @@ -1125,9 +1151,9 @@ expect(blockNumber).to.be.greaterThan(0); const scheduleTx = api.tx.scheduler.scheduleNamed( // schedule scheduledId, - expectedBlockNumber, - repetitions > 1 ? [period, repetitions] : null, - 0, + expectedBlockNumber, + repetitions > 1 ? [period, repetitions] : null, + 0, {Value: operationTx as any}, ); @@ -1152,13 +1178,13 @@ expect(blockNumber).to.be.greaterThan(0); const scheduleTx = api.tx.scheduler.scheduleNamed( // schedule scheduledId, - expectedBlockNumber, - repetitions <= 1 ? null : [period, repetitions], - 0, + expectedBlockNumber, + repetitions <= 1 ? null : [period, repetitions], + 0, {Value: operationTx as any}, ); - //const events = + //const events = await expect(submitTransactionExpectFailAsync(sender, scheduleTx)).to.be.rejected; //expect(getGenericResult(events).success).to.be.false; }); @@ -1222,7 +1248,7 @@ const transferTx = api.tx.balances.transfer(recipient.address, amount); const balanceBefore = await getFreeBalance(recipient); - + await scheduleExpectSuccess(transferTx, sender, blockSchedule, scheduledId, period, repetitions); expect(await getFreeBalance(recipient)).to.be.equal(balanceBefore); @@ -1738,6 +1764,12 @@ return (await api.rpc.unique.collectionById(collectionId)).unwrap(); } +export const describe_xcm = ( + process.env.RUN_XCM_TESTS + ? describe + : describe.skip +); + export async function waitNewBlocks(blocksCount = 1): Promise { await usingApi(async (api) => { const promise = new Promise(async (resolve) => { @@ -1754,6 +1786,45 @@ }); } +export async function waitEvent( + api: ApiPromise, + maxBlocksToWait: number, + eventSection: string, + eventMethod: string, +): Promise { + + const promise = new Promise(async (resolve) => { + const unsubscribe = await api.rpc.chain.subscribeNewHeads(async header => { + const blockNumber = header.number.toHuman(); + const blockHash = header.hash; + const eventIdStr = `${eventSection}.${eventMethod}`; + const waitLimitStr = `wait blocks remaining: ${maxBlocksToWait}`; + + console.log(`[Block #${blockNumber}] Waiting for event \`${eventIdStr}\` (${waitLimitStr})`); + + const apiAt = await api.at(blockHash); + const eventRecords = await apiAt.query.system.events(); + + const neededEvent = eventRecords.find(r => { + return r.event.section == eventSection && r.event.method == eventMethod; + }); + + if (neededEvent) { + unsubscribe(); + resolve(neededEvent); + } else if (maxBlocksToWait > 0) { + maxBlocksToWait--; + } else { + console.log(`Event \`${eventIdStr}\` is NOT found`); + + unsubscribe(); + resolve(null); + } + }); + }); + return promise; +} + export async function repartitionRFT( api: ApiPromise, collectionId: number, @@ -1782,6 +1853,11 @@ itApi.only = (name: string, cb: (apis: { api: ApiPromise, privateKeyWrapper: (account: string) => IKeyringPair }) => any) => itApi(name, cb, {only: true}); itApi.skip = (name: string, cb: (apis: { api: ApiPromise, privateKeyWrapper: (account: string) => IKeyringPair }) => any) => itApi(name, cb, {skip: true}); +let accountSeed = 10000; +export function generateKeyringPair(keyring: Keyring) { + const privateKey = `0xDEADBEEF${(Date.now() + (accountSeed++)).toString(16).padStart(64 - 8, '0')}`; + return keyring.addFromUri(privateKey); +} export async function expectSubstrateEventsAtBlock(api: ApiPromise, blockNumber: AnyNumber | BlockNumber, section: string, methods: string[], dryRun = false) { const blockHash = await api.rpc.chain.getBlockHash(blockNumber); @@ -1800,4 +1876,4 @@ expect(subEvents).to.be.like(events); } return subEvents; -} \ No newline at end of file +} --- a/tests/src/util/playgrounds/index.ts +++ b/tests/src/util/playgrounds/index.ts @@ -13,14 +13,14 @@ chai.use(chaiAsPromised); export const expect = chai.expect; -export const usingPlaygrounds = async (code: (helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair) => Promise) => { +export const usingPlaygrounds = async (code: (helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair) => Promise, url: string = config.substrateUrl) => { const silentConsole = new SilentConsole(); silentConsole.enable(); const helper = new DevUniqueHelper(new SilentLogger()); try { - await helper.connect(config.substrateUrl); + await helper.connect(url); const ss58Format = helper.chain.getChainProperties().ss58Format; const privateKey = (seed: string) => helper.util.fromSeed(seed, ss58Format); await code(helper, privateKey); --- a/tests/src/util/playgrounds/types.ts +++ b/tests/src/util/playgrounds/types.ts @@ -3,22 +3,32 @@ import {IKeyringPair} from '@polkadot/types/types'; -export interface IChainEvent { - data: any; - method: string; +export interface IEvent { section: string; + method: string; + index: [number, number] | string; + data: any[]; + phase: {applyExtrinsic: number} | 'Initialization', } export interface ITransactionResult { - status: 'Fail' | 'Success'; - result: { - events: { - event: IChainEvent - }[]; - }, - moduleError?: string; + status: 'Fail' | 'Success'; + result: { + events: { + phase: any, // {ApplyExtrinsic: number} | 'Initialization', + event: IEvent; + }[]; + }, + moduleError?: string; } +export interface ISubscribeBlockEventsData { + number: number; + hash: string; + timestamp: number; + events: IEvent[]; +} + export interface ILogger { log: (msg: any, level?: string) => void; level: { @@ -147,6 +157,11 @@ feeFrozen: bigint } +export interface IStakingInfo { + block: bigint, + amount: bigint, +} + export type TSubstrateAccount = string; export type TEthereumAccount = string; export type TApiAllowedListeners = 'connected' | 'disconnected' | 'error' | 'ready' | 'decorated'; --- a/tests/src/util/playgrounds/unique.dev.ts +++ b/tests/src/util/playgrounds/unique.dev.ts @@ -60,11 +60,13 @@ */ arrange: ArrangeGroup; wait: WaitGroup; + admin: AdminGroup; constructor(logger: { log: (msg: any, level: any) => void, level: any }) { super(logger); this.arrange = new ArrangeGroup(this); this.wait = new WaitGroup(this); + this.admin = new AdminGroup(this); } async connect(wsEndpoint: string, _listeners?: any): Promise { @@ -120,6 +122,7 @@ */ createAccounts = async (balances: bigint[], donor: IKeyringPair): Promise => { let nonce = await this.helper.chain.getNonce(donor.address); + const wait = new WaitGroup(this.helper); const ss58Format = this.helper.chain.getChainProperties().ss58Format; const tokenNominal = this.helper.balance.getOneTokenNominal(); const transactions = []; @@ -129,7 +132,7 @@ accounts.push(recipient); if (balance !== 0n) { const tx = this.helper.constructApiCall('api.tx.balances.transfer', [{Id: recipient.address}, balance * tokenNominal]); - transactions.push(this.helper.signTransaction(donor, tx, 'account generation', {nonce})); + transactions.push(this.helper.signTransaction(donor, tx, {nonce}, 'account generation')); nonce++; } } @@ -154,7 +157,7 @@ for (let index = 0; index < 5; index++) { accountsCreated = await checkBalances(); if(accountsCreated) break; - + await wait.newBlocks(1); } if (!accountsCreated) throw Error('Accounts generation failed'); @@ -180,7 +183,7 @@ accounts.push(recepient); if (withBalance !== 0n) { const tx = this.helper.constructApiCall('api.tx.balances.transfer', [{Id: recepient.address}, withBalance * tokenNominal]); - transactions.push(this.helper.signTransaction(donor, tx, 'account generation', {nonce})); + transactions.push(this.helper.signTransaction(donor, tx, {nonce}, 'account generation')); nonce++; } } @@ -281,3 +284,22 @@ }); } } + +class AdminGroup { + helper: UniqueHelper; + + constructor(helper: UniqueHelper) { + this.helper = helper; + } + + async payoutStakers(signer: IKeyringPair, stakersToPayout: number) { + const payoutResult = await this.helper.executeExtrinsic(signer, 'api.tx.appPromotion.payoutStakers', [stakersToPayout], true); + return payoutResult.result.events.filter(e => e.event.method === 'StakingRecalculation').map(e => { + return { + staker: e.event.data[0].toString(), + stake: e.event.data[1].toBigInt(), + payout: e.event.data[2].toBigInt(), + }; + }); + } +} --- a/tests/src/util/playgrounds/unique.ts +++ b/tests/src/util/playgrounds/unique.ts @@ -6,10 +6,10 @@ /* eslint-disable no-prototype-builtins */ import {ApiPromise, WsProvider, Keyring} from '@polkadot/api'; -import {ApiInterfaceEvents} from '@polkadot/api/types'; +import {ApiInterfaceEvents, SignerOptions} from '@polkadot/api/types'; import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm} from '@polkadot/util-crypto'; import {IKeyringPair} from '@polkadot/types/types'; -import {IApiListeners, IBlock, IChainEvent, IChainProperties, ICollectionCreationOptions, ICollectionLimits, ICollectionPermissions, ICrossAccountId, ICrossAccountIdLower, ILogger, INestingPermissions, IProperty, ISubstrateBalance, IToken, ITokenPropertyPermission, ITransactionResult, IUniqueHelperLog, TApiAllowedListeners, TEthereumAccount, TSigner, TSubstrateAccount, TUniqueNetworks} from './types'; +import {IApiListeners, IBlock, IEvent, IChainProperties, ICollectionCreationOptions, ICollectionLimits, ICollectionPermissions, ICrossAccountId, ICrossAccountIdLower, ILogger, INestingPermissions, IProperty, IStakingInfo, ISubstrateBalance, IToken, ITokenPropertyPermission, ITransactionResult, IUniqueHelperLog, TApiAllowedListeners, TEthereumAccount, TSigner, TSubstrateAccount, TUniqueNetworks} from './types'; export const crossAccountIdFromLower = (lowerAddress: ICrossAccountIdLower): ICrossAccountId => { const address = {} as ICrossAccountId; @@ -149,7 +149,7 @@ return {success, tokens}; } - static findCollectionInEvents(events: {event: IChainEvent}[], collectionId: number, expectedSection: string, expectedMethod: string) { + static findCollectionInEvents(events: {event: IEvent}[], collectionId: number, expectedSection: string, expectedMethod: string) { let eventId = null; events.forEach(({event: {data, method, section}}) => { if ((section === expectedSection) && (method === expectedMethod)) { @@ -163,7 +163,7 @@ return eventId === collectionId; } - static isTokenTransferSuccess(events: {event: IChainEvent}[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) { + static isTokenTransferSuccess(events: {event: IEvent}[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) { const normalizeAddress = (address: string | ICrossAccountId) => { if(typeof address === 'string') return address; const obj = {} as any; @@ -195,11 +195,64 @@ } } +class UniqueEventHelper { + private static extractIndex(index: any): [number, number] | string { + if(index.toRawType() === '[u8;2]') return [index[0], index[1]]; + return index.toJSON(); + } + + private static extractSub(data: any, subTypes: any): {[key: string]: any} { + let obj: any = {}; + let index = 0; + + if (data.entries) { + for(const [key, value] of data.entries()) { + obj[key] = this.extractData(value, subTypes[index]); + index++; + } + } else obj = data.toJSON(); + + return obj; + } + + private static extractData(data: any, type: any): any { + if (['u16', 'u32'].indexOf(type.type) > -1) return data.toNumber(); + if (['u64', 'u128', 'u256'].indexOf(type.type) > -1) return data.toBigInt(); + if(type.hasOwnProperty('sub')) return this.extractSub(data, type.sub); + return data.toHuman(); + } + public static extractEvents(records: ITransactionResult): IEvent[] { + const parsedEvents: IEvent[] = []; + + records.result.events.forEach((record) => { + const {event, phase} = record; + const types = (event as any).typeDef; + + const eventData: IEvent = { + section: event.section.toString(), + method: event.method.toString(), + index: this.extractIndex(event.index), + data: [], + phase: phase.toJSON(), + }; + + event.data.forEach((val: any, index: number) => { + eventData.data.push(this.extractData(val, types[index])); + }); + + parsedEvents.push(eventData); + }); + + return parsedEvents; + } +} + class ChainHelperBase { transactionStatus = UniqueUtil.transactionStatus; chainLogType = UniqueUtil.chainLogType; util: typeof UniqueUtil; + eventHelper: typeof UniqueEventHelper; logger: ILogger; api: ApiPromise | null; forcedNetwork: TUniqueNetworks | null; @@ -208,6 +261,7 @@ constructor(logger?: ILogger) { this.util = UniqueUtil; + this.eventHelper = UniqueEventHelper; if (typeof logger == 'undefined') logger = this.util.getDefaultLogger(); this.logger = logger; this.api = null; @@ -290,7 +344,7 @@ return {api, network}; } - getTransactionStatus(data: {events: {event: IChainEvent}[], status: any}) { + getTransactionStatus(data: {events: {event: IEvent}[], status: any}) { const {events, status} = data; if (status.isReady) { return this.transactionStatus.NOT_READY; @@ -299,11 +353,11 @@ return this.transactionStatus.NOT_READY; } if (status.isInBlock || status.isFinalized) { - const errors = events.filter(e => e.event.data.method === 'ExtrinsicFailed'); + const errors = events.filter(e => e.event.method === 'ExtrinsicFailed'); if (errors.length > 0) { return this.transactionStatus.FAIL; } - if (events.filter(e => e.event.data.method === 'ExtrinsicSuccess').length > 0) { + if (events.filter(e => e.event.method === 'ExtrinsicSuccess').length > 0) { return this.transactionStatus.SUCCESS; } } @@ -311,7 +365,7 @@ return this.transactionStatus.FAIL; } - signTransaction(sender: TSigner, transaction: any, label = 'transaction', options: any = null) { + signTransaction(sender: TSigner, transaction: any, options: Partial | null = null, label = 'transaction') { const sign = (callback: any) => { if(options !== null) return transaction.signAndSend(sender, options, callback); return transaction.signAndSend(sender, callback); @@ -332,13 +386,16 @@ if (result.hasOwnProperty('dispatchError')) { const dispatchError = result['dispatchError']; - if (dispatchError && dispatchError.isModule) { - const modErr = dispatchError.asModule; - const errorMeta = dispatchError.registry.findMetaError(modErr); + if (dispatchError) { + if (dispatchError.isModule) { + const modErr = dispatchError.asModule; + const errorMeta = dispatchError.registry.findMetaError(modErr); - moduleError = `${errorMeta.section}.${errorMeta.name}`; - } - else { + moduleError = `${errorMeta.section}.${errorMeta.name}`; + } else { + moduleError = dispatchError.toHuman(); + } + } else { this.logger.log(result, this.logger.level.ERROR); } } @@ -364,16 +421,16 @@ return call(...params); } - async executeExtrinsic(sender: TSigner, extrinsic: string, params: any[], expectSuccess=false/*, failureMessage='expected success'*/) { + async executeExtrinsic(sender: TSigner, extrinsic: string, params: any[], expectSuccess=true, options: Partial|null = null/*, failureMessage='expected success'*/) { if(this.api === null) throw Error('API not initialized'); if(!extrinsic.startsWith('api.tx.')) throw Error(`${extrinsic} is not transaction`); const startTime = (new Date()).getTime(); let result: ITransactionResult; - let events = []; + let events: IEvent[] = []; try { - result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), extrinsic) as ITransactionResult; - events = result.result.events.map((x: any) => x.toHuman()); + result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), options, extrinsic) as ITransactionResult; + events = this.eventHelper.extractEvents(result); } catch(e) { if(!(e as object).hasOwnProperty('status')) throw e; @@ -2029,21 +2086,54 @@ return 1; } + /** + * Get total staked amount for address + * @param address substrate or ethereum address + * @returns total staked amount + */ async getTotalStaked(address?: ICrossAccountId): Promise { if (address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt(); return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt(); } - async getTotalStakedPerBlock(address: ICrossAccountId): Promise { - return (await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address])).map(([block, amount]: any[]) => [block.toBigInt(), amount.toBigInt()]); + /** + * Get total staked per block + * @param address substrate or ethereum address + * @returns array of stakes. `block` – the number of the block in which the stake was made. `amount` - the number of tokens staked in the block + */ + async getTotalStakedPerBlock(address: ICrossAccountId): Promise { + const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]); + return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => { + return { + block: block.toBigInt(), + amount: amount.toBigInt(), + }; + }); } + /** + * Get total pending unstake amount for address + * @param address substrate or ethereum address + * @returns total pending unstake amount + */ async getPendingUnstake(address: ICrossAccountId): Promise { return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt(); } - async getPendingUnstakePerBlock(address: ICrossAccountId): Promise { - return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address])).map(([block, amount]: any[]) => [block.toBigInt(), amount.toBigInt()]); + /** + * Get pending unstake amount per block for address + * @param address substrate or ethereum address + * @returns array of pending stakes. `block` – the number of the block in which the unstake was made. `amount` - the number of tokens unstaked in the block + */ + async getPendingUnstakePerBlock(address: ICrossAccountId): Promise { + const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]); + const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => { + return { + block: block.toBigInt(), + amount: amount.toBigInt(), + }; + }); + return result; } } --- /dev/null +++ b/tests/src/xcm/xcmOpal.test.ts @@ -0,0 +1,527 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; + +import {WsProvider} from '@polkadot/api'; +import {ApiOptions} from '@polkadot/api/types'; +import {IKeyringPair} from '@polkadot/types/types'; +import usingApi, {executeTransaction} from './../substrate/substrate-api'; +import {bigIntToDecimals, describe_xcm, getGenericResult, paraSiblingSovereignAccount} from './../util/helpers'; +import waitNewBlocks from './../substrate/wait-new-blocks'; +import {normalizeAccountId} from './../util/helpers'; +import getBalance from './../substrate/get-balance'; + + +chai.use(chaiAsPromised); +const expect = chai.expect; + +const STATEMINE_CHAIN = 1000; +const UNIQUE_CHAIN = 2095; + +const RELAY_PORT = '9844'; +const UNIQUE_PORT = '9944'; +const STATEMINE_PORT = '9948'; +const STATEMINE_PALLET_INSTANCE = 50; +const ASSET_ID = 100; +const ASSET_METADATA_DECIMALS = 18; +const ASSET_METADATA_NAME = 'USDT'; +const ASSET_METADATA_DESCRIPTION = 'USDT'; +const ASSET_METADATA_MINIMAL_BALANCE = 1; + +const WESTMINT_DECIMALS = 12; + +const TRANSFER_AMOUNT = 1_000_000_000_000_000_000n; + +// 10,000.00 (ten thousands) USDT +const ASSET_AMOUNT = 1_000_000_000_000_000_000_000n; + +describe_xcm('[XCM] Integration test: Exchanging USDT with Westmint', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + let balanceStmnBefore: bigint; + let balanceStmnAfter: bigint; + + let balanceOpalBefore: bigint; + let balanceOpalAfter: bigint; + let balanceOpalFinal: bigint; + + let balanceBobBefore: bigint; + let balanceBobAfter: bigint; + let balanceBobFinal: bigint; + + let balanceBobRelayTokenBefore: bigint; + let balanceBobRelayTokenAfter: bigint; + + + before(async () => { + await usingApi(async (api, privateKeyWrapper) => { + alice = privateKeyWrapper('//Alice'); + bob = privateKeyWrapper('//Bob'); // funds donor + }); + + const statemineApiOptions: ApiOptions = { + provider: new WsProvider('ws://127.0.0.1:' + STATEMINE_PORT), + }; + + const uniqueApiOptions: ApiOptions = { + provider: new WsProvider('ws://127.0.0.1:' + UNIQUE_PORT), + }; + + const relayApiOptions: ApiOptions = { + provider: new WsProvider('ws://127.0.0.1:' + RELAY_PORT), + }; + + await usingApi(async (api) => { + + // 350.00 (three hundred fifty) DOT + const fundingAmount = 3_500_000_000_000; + + const tx = api.tx.assets.create(ASSET_ID, alice.addressRaw, ASSET_METADATA_MINIMAL_BALANCE); + const events = await executeTransaction(api, alice, tx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + + // set metadata + const tx2 = api.tx.assets.setMetadata(ASSET_ID, ASSET_METADATA_NAME, ASSET_METADATA_DESCRIPTION, ASSET_METADATA_DECIMALS); + const events2 = await executeTransaction(api, alice, tx2); + const result2 = getGenericResult(events2); + expect(result2.success).to.be.true; + + // mint some amount of asset + const tx3 = api.tx.assets.mint(ASSET_ID, alice.addressRaw, ASSET_AMOUNT); + const events3 = await executeTransaction(api, alice, tx3); + const result3 = getGenericResult(events3); + expect(result3.success).to.be.true; + + // funding parachain sovereing account (Parachain: 2095) + const parachainSovereingAccount = await paraSiblingSovereignAccount(UNIQUE_CHAIN); + const tx4 = api.tx.balances.transfer(parachainSovereingAccount, fundingAmount); + const events4 = await executeTransaction(api, bob, tx4); + const result4 = getGenericResult(events4); + expect(result4.success).to.be.true; + + }, statemineApiOptions); + + + await usingApi(async (api) => { + + const location = { + V1: { + parents: 1, + interior: {X3: [ + { + Parachain: STATEMINE_CHAIN, + }, + { + PalletInstance: STATEMINE_PALLET_INSTANCE, + }, + { + GeneralIndex: ASSET_ID, + }, + ]}, + }, + }; + + const metadata = + { + name: ASSET_ID, + symbol: ASSET_METADATA_NAME, + decimals: ASSET_METADATA_DECIMALS, + minimalBalance: ASSET_METADATA_MINIMAL_BALANCE, + }; + //registerForeignAsset(owner, location, metadata) + const tx = api.tx.foreignAssets.registerForeignAsset(alice.addressRaw, location, metadata); + const sudoTx = api.tx.sudo.sudo(tx as any); + const events = await executeTransaction(api, alice, sudoTx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + + [balanceOpalBefore] = await getBalance(api, [alice.address]); + + }, uniqueApiOptions); + + + // Providing the relay currency to the unique sender account + await usingApi(async (api) => { + const destination = { + V1: { + parents: 0, + interior: {X1: { + Parachain: UNIQUE_CHAIN, + }, + }, + }}; + + const beneficiary = { + V1: { + parents: 0, + interior: {X1: { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }}, + }, + }; + + const assets = { + V1: [ + { + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: 50_000_000_000_000_000n, + }, + }, + ], + }; + + const feeAssetItem = 0; + + const weightLimit = { + Limited: 5_000_000_000, + }; + + const tx = api.tx.xcmPallet.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit); + const events = await executeTransaction(api, alice, tx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + }, relayApiOptions); + + }); + + it('Should connect and send USDT from Westmint to Opal', async () => { + + const statemineApiOptions: ApiOptions = { + provider: new WsProvider('ws://127.0.0.1:' + STATEMINE_PORT), + }; + + const uniqueApiOptions: ApiOptions = { + provider: new WsProvider('ws://127.0.0.1:' + UNIQUE_PORT), + }; + + await usingApi(async (api) => { + + const dest = { + V1: { + parents: 1, + interior: {X1: { + Parachain: UNIQUE_CHAIN, + }, + }, + }}; + + const beneficiary = { + V1: { + parents: 0, + interior: {X1: { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }}, + }, + }; + + const assets = { + V1: [ + { + id: { + Concrete: { + parents: 0, + interior: { + X2: [ + { + PalletInstance: STATEMINE_PALLET_INSTANCE, + }, + { + GeneralIndex: ASSET_ID, + }, + ]}, + }, + }, + fun: { + Fungible: TRANSFER_AMOUNT, + }, + }, + ], + }; + + const feeAssetItem = 0; + + const weightLimit = { + Limited: 5000000000, + }; + + [balanceStmnBefore] = await getBalance(api, [alice.address]); + + const tx = api.tx.polkadotXcm.limitedReserveTransferAssets(dest, beneficiary, assets, feeAssetItem, weightLimit); + const events = await executeTransaction(api, alice, tx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + + [balanceStmnAfter] = await getBalance(api, [alice.address]); + + // common good parachain take commission in it native token + console.log( + 'Opal to Westmint transaction fees on Westmint: %s WND', + bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, WESTMINT_DECIMALS), + ); + expect(balanceStmnBefore > balanceStmnAfter).to.be.true; + + }, statemineApiOptions); + + + // ensure that asset has been delivered + await usingApi(async (api) => { + await waitNewBlocks(api, 3); + // expext collection id will be with id 1 + const free = (await api.query.fungible.balance(1, normalizeAccountId(alice.address))).toBigInt(); + + [balanceOpalAfter] = await getBalance(api, [alice.address]); + + // commission has not paid in USDT token + expect(free == TRANSFER_AMOUNT).to.be.true; + console.log( + 'Opal to Westmint transaction fees on Opal: %s USDT', + bigIntToDecimals(TRANSFER_AMOUNT - free), + ); + // ... and parachain native token + expect(balanceOpalAfter == balanceOpalBefore).to.be.true; + console.log( + 'Opal to Westmint transaction fees on Opal: %s WND', + bigIntToDecimals(balanceOpalAfter - balanceOpalBefore, WESTMINT_DECIMALS), + ); + + }, uniqueApiOptions); + + }); + + it('Should connect and send USDT from Unique to Statemine back', async () => { + + const uniqueApiOptions: ApiOptions = { + provider: new WsProvider('ws://127.0.0.1:' + UNIQUE_PORT), + }; + + const statemineApiOptions: ApiOptions = { + provider: new WsProvider('ws://127.0.0.1:' + STATEMINE_PORT), + }; + + await usingApi(async (api) => { + const destination = { + V1: { + parents: 1, + interior: {X2: [ + { + Parachain: STATEMINE_CHAIN, + }, + { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }, + ]}, + }, + }; + + const currencies: [any, bigint][] = [ + [ + { + ForeignAssetId: 0, + }, + //10_000_000_000_000_000n, + TRANSFER_AMOUNT, + ], + [ + { + NativeAssetId: 'Parent', + }, + 400_000_000_000_000n, + ], + ]; + + const feeItem = 1; + const destWeight = 500000000000; + + const tx = api.tx.xTokens.transferMulticurrencies(currencies, feeItem, destination, destWeight); + const events = await executeTransaction(api, alice, tx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + + // the commission has been paid in parachain native token + [balanceOpalFinal] = await getBalance(api, [alice.address]); + expect(balanceOpalAfter > balanceOpalFinal).to.be.true; + }, uniqueApiOptions); + + await usingApi(async (api) => { + await waitNewBlocks(api, 3); + + // The USDT token never paid fees. Its amount not changed from begin value. + // Also check that xcm transfer has been succeeded + const free = ((await api.query.assets.account(100, alice.address)).toHuman()) as any; + expect(BigInt(free.balance.replace(/,/g, '')) == ASSET_AMOUNT).to.be.true; + }, statemineApiOptions); + }); + + it('Should connect and send Relay token to Unique', async () => { + + const uniqueApiOptions: ApiOptions = { + provider: new WsProvider('ws://127.0.0.1:' + UNIQUE_PORT), + }; + + const uniqueApiOptions2: ApiOptions = { + provider: new WsProvider('ws://127.0.0.1:' + UNIQUE_PORT), + }; + + const relayApiOptions: ApiOptions = { + provider: new WsProvider('ws://127.0.0.1:' + RELAY_PORT), + }; + + const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n; + + await usingApi(async (api) => { + [balanceBobBefore] = await getBalance(api, [bob.address]); + balanceBobRelayTokenBefore = BigInt(((await api.query.tokens.accounts(bob.addressRaw, {NativeAssetId: 'Parent'})).toJSON() as any).free); + + }, uniqueApiOptions); + + // Providing the relay currency to the unique sender account + await usingApi(async (api) => { + const destination = { + V1: { + parents: 0, + interior: {X1: { + Parachain: UNIQUE_CHAIN, + }, + }, + }}; + + const beneficiary = { + V1: { + parents: 0, + interior: {X1: { + AccountId32: { + network: 'Any', + id: bob.addressRaw, + }, + }}, + }, + }; + + const assets = { + V1: [ + { + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: TRANSFER_AMOUNT_RELAY, + }, + }, + ], + }; + + const feeAssetItem = 0; + + const weightLimit = { + Limited: 5_000_000_000, + }; + + const tx = api.tx.xcmPallet.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit); + const events = await executeTransaction(api, bob, tx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + }, relayApiOptions); + + + await usingApi(async (api) => { + await waitNewBlocks(api, 3); + + [balanceBobAfter] = await getBalance(api, [bob.address]); + balanceBobRelayTokenAfter = BigInt(((await api.query.tokens.accounts(bob.addressRaw, {NativeAssetId: 'Parent'})).toJSON() as any).free); + const wndFee = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore; + console.log( + 'Relay (Westend) to Opal transaction fees: %s OPL', + bigIntToDecimals(balanceBobAfter - balanceBobBefore), + ); + console.log( + 'Relay (Westend) to Opal transaction fees: %s WND', + bigIntToDecimals(wndFee, WESTMINT_DECIMALS), + ); + expect(balanceBobBefore == balanceBobAfter).to.be.true; + expect(balanceBobRelayTokenBefore < balanceBobRelayTokenAfter).to.be.true; + }, uniqueApiOptions2); + + }); + + it('Should connect and send Relay token back', async () => { + const uniqueApiOptions: ApiOptions = { + provider: new WsProvider('ws://127.0.0.1:' + UNIQUE_PORT), + }; + + await usingApi(async (api) => { + const destination = { + V1: { + parents: 1, + interior: {X2: [ + { + Parachain: STATEMINE_CHAIN, + }, + { + AccountId32: { + network: 'Any', + id: bob.addressRaw, + }, + }, + ]}, + }, + }; + + const currencies: any = [ + [ + { + NativeAssetId: 'Parent', + }, + 50_000_000_000_000_000n, + ], + ]; + + const feeItem = 0; + const destWeight = 500000000000; + + const tx = api.tx.xTokens.transferMulticurrencies(currencies, feeItem, destination, destWeight); + const events = await executeTransaction(api, bob, tx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + + [balanceBobFinal] = await getBalance(api, [bob.address]); + console.log('Relay (Westend) to Opal transaction fees: %s OPL', balanceBobAfter - balanceBobFinal); + + }, uniqueApiOptions); + }); + +}); --- /dev/null +++ b/tests/src/xcm/xcmQuartz.test.ts @@ -0,0 +1,799 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; + +import {WsProvider, Keyring} from '@polkadot/api'; +import {ApiOptions} from '@polkadot/api/types'; +import {IKeyringPair} from '@polkadot/types/types'; +import usingApi, {submitTransactionAsync} from '../substrate/substrate-api'; +import {getGenericResult, generateKeyringPair, waitEvent, describe_xcm, bigIntToDecimals} from '../util/helpers'; +import {MultiLocation} from '@polkadot/types/interfaces'; +import {blake2AsHex} from '@polkadot/util-crypto'; +import waitNewBlocks from '../substrate/wait-new-blocks'; +import getBalance from '../substrate/get-balance'; +import {XcmV2TraitsOutcome, XcmV2TraitsError} from '../interfaces'; + +chai.use(chaiAsPromised); +const expect = chai.expect; + +const QUARTZ_CHAIN = 2095; +const KARURA_CHAIN = 2000; +const MOONRIVER_CHAIN = 2023; + +const RELAY_PORT = 9844; +const KARURA_PORT = 9946; +const MOONRIVER_PORT = 9947; + +const KARURA_DECIMALS = 12; + +const TRANSFER_AMOUNT = 2000000000000000000000000n; + +function parachainApiOptions(port: number): ApiOptions { + return { + provider: new WsProvider('ws://127.0.0.1:' + port.toString()), + }; +} + +function karuraOptions(): ApiOptions { + return parachainApiOptions(KARURA_PORT); +} + +function moonriverOptions(): ApiOptions { + return parachainApiOptions(MOONRIVER_PORT); +} + +function relayOptions(): ApiOptions { + return parachainApiOptions(RELAY_PORT); +} + +describe_xcm('[XCM] Integration test: Exchanging tokens with Karura', () => { + let alice: IKeyringPair; + let randomAccount: IKeyringPair; + + let balanceQuartzTokenInit: bigint; + let balanceQuartzTokenMiddle: bigint; + let balanceQuartzTokenFinal: bigint; + let balanceKaruraTokenInit: bigint; + let balanceKaruraTokenMiddle: bigint; + let balanceKaruraTokenFinal: bigint; + let balanceQuartzForeignTokenInit: bigint; + let balanceQuartzForeignTokenMiddle: bigint; + let balanceQuartzForeignTokenFinal: bigint; + + before(async () => { + await usingApi(async (api, privateKeyWrapper) => { + const keyringSr25519 = new Keyring({type: 'sr25519'}); + + alice = privateKeyWrapper('//Alice'); + randomAccount = generateKeyringPair(keyringSr25519); + }); + + // Karura side + await usingApi( + async (api) => { + const destination = { + V0: { + X2: [ + 'Parent', + { + Parachain: QUARTZ_CHAIN, + }, + ], + }, + }; + + const metadata = { + name: 'QTZ', + symbol: 'QTZ', + decimals: 18, + minimalBalance: 1, + }; + + const tx = api.tx.assetRegistry.registerForeignAsset(destination, metadata); + const sudoTx = api.tx.sudo.sudo(tx as any); + const events = await submitTransactionAsync(alice, sudoTx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + + const tx1 = api.tx.balances.transfer(randomAccount.address, 10000000000000n); + const events1 = await submitTransactionAsync(alice, tx1); + const result1 = getGenericResult(events1); + expect(result1.success).to.be.true; + + [balanceKaruraTokenInit] = await getBalance(api, [randomAccount.address]); + { + const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any; + balanceQuartzForeignTokenInit = BigInt(free); + } + }, + karuraOptions(), + ); + + // Quartz side + await usingApi(async (api) => { + const tx0 = api.tx.balances.transfer(randomAccount.address, 10n * TRANSFER_AMOUNT); + const events0 = await submitTransactionAsync(alice, tx0); + const result0 = getGenericResult(events0); + expect(result0.success).to.be.true; + + [balanceQuartzTokenInit] = await getBalance(api, [randomAccount.address]); + }); + }); + + it('Should connect and send QTZ to Karura', async () => { + + // Quartz side + await usingApi(async (api) => { + + const destination = { + V0: { + X2: [ + 'Parent', + { + Parachain: KARURA_CHAIN, + }, + ], + }, + }; + + const beneficiary = { + V0: { + X1: { + AccountId32: { + network: 'Any', + id: randomAccount.addressRaw, + }, + }, + }, + }; + + const assets = { + V1: [ + { + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: TRANSFER_AMOUNT, + }, + }, + ], + }; + + const feeAssetItem = 0; + + const weightLimit = { + Limited: 5000000000, + }; + + const tx = api.tx.polkadotXcm.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit); + const events = await submitTransactionAsync(randomAccount, tx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + + [balanceQuartzTokenMiddle] = await getBalance(api, [randomAccount.address]); + + const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT; + console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', bigIntToDecimals(qtzFees)); + expect(qtzFees > 0n).to.be.true; + }); + + // Karura side + await usingApi( + async (api) => { + await waitNewBlocks(api, 3); + const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any; + balanceQuartzForeignTokenMiddle = BigInt(free); + + [balanceKaruraTokenMiddle] = await getBalance(api, [randomAccount.address]); + + const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle; + const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit; + + console.log( + '[Quartz -> Karura] transaction fees on Karura: %s KAR', + bigIntToDecimals(karFees, KARURA_DECIMALS), + ); + console.log('[Quartz -> Karura] income %s QTZ', bigIntToDecimals(qtzIncomeTransfer)); + expect(karFees == 0n).to.be.true; + expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true; + }, + karuraOptions(), + ); + }); + + it('Should connect to Karura and send QTZ back', async () => { + + // Karura side + await usingApi( + async (api) => { + const destination = { + V1: { + parents: 1, + interior: { + X2: [ + {Parachain: QUARTZ_CHAIN}, + { + AccountId32: { + network: 'Any', + id: randomAccount.addressRaw, + }, + }, + ], + }, + }, + }; + + const id = { + ForeignAsset: 0, + }; + + const destWeight = 50000000; + + const tx = api.tx.xTokens.transfer(id as any, TRANSFER_AMOUNT, destination, destWeight); + const events = await submitTransactionAsync(randomAccount, tx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + + [balanceKaruraTokenFinal] = await getBalance(api, [randomAccount.address]); + { + const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, id)).toJSON() as any; + balanceQuartzForeignTokenFinal = BigInt(free); + } + + const karFees = balanceKaruraTokenMiddle - balanceKaruraTokenFinal; + const qtzOutcomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenFinal; + + console.log( + '[Karura -> Quartz] transaction fees on Karura: %s KAR', + bigIntToDecimals(karFees, KARURA_DECIMALS), + ); + console.log('[Karura -> Quartz] outcome %s QTZ', bigIntToDecimals(qtzOutcomeTransfer)); + + expect(karFees > 0).to.be.true; + expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true; + }, + karuraOptions(), + ); + + // Quartz side + await usingApi(async (api) => { + await waitNewBlocks(api, 3); + + [balanceQuartzTokenFinal] = await getBalance(api, [randomAccount.address]); + const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle; + expect(actuallyDelivered > 0).to.be.true; + + console.log('[Karura -> Quartz] actually delivered %s QTZ', bigIntToDecimals(actuallyDelivered)); + + const qtzFees = TRANSFER_AMOUNT - actuallyDelivered; + console.log('[Karura -> Quartz] transaction fees on Quartz: %s QTZ', bigIntToDecimals(qtzFees)); + expect(qtzFees == 0n).to.be.true; + }); + }); +}); + +// These tests are relevant only when the foreign asset pallet is disabled +describe_xcm('[XCM] Integration test: Quartz rejects non-native tokens', () => { + let alice: IKeyringPair; + + before(async () => { + await usingApi(async (api, privateKeyWrapper) => { + alice = privateKeyWrapper('//Alice'); + }); + }); + + it('Quartz rejects tokens from the Relay', async () => { + await usingApi(async (api) => { + const destination = { + V1: { + parents: 0, + interior: {X1: { + Parachain: QUARTZ_CHAIN, + }, + }, + }}; + + const beneficiary = { + V1: { + parents: 0, + interior: {X1: { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }}, + }, + }; + + const assets = { + V1: [ + { + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: 50_000_000_000_000_000n, + }, + }, + ], + }; + + const feeAssetItem = 0; + + const weightLimit = { + Limited: 5_000_000_000, + }; + + const tx = api.tx.xcmPallet.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit); + const events = await submitTransactionAsync(alice, tx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + }, relayOptions()); + + await usingApi(async api => { + const maxWaitBlocks = 3; + const dmpQueueExecutedDownward = await waitEvent( + api, + maxWaitBlocks, + 'dmpQueue', + 'ExecutedDownward', + ); + + expect( + dmpQueueExecutedDownward != null, + '[Relay] dmpQueue.ExecutedDownward event is expected', + ).to.be.true; + + const event = dmpQueueExecutedDownward!.event; + const outcome = event.data[1] as XcmV2TraitsOutcome; + + expect( + outcome.isIncomplete, + '[Relay] The outcome of the XCM should be `Incomplete`', + ).to.be.true; + + const incomplete = outcome.asIncomplete; + expect( + incomplete[1].toString() == 'AssetNotFound', + '[Relay] The XCM error should be `AssetNotFound`', + ).to.be.true; + }); + }); + + it('Quartz rejects KAR tokens from Karura', async () => { + await usingApi(async (api) => { + const destination = { + V1: { + parents: 1, + interior: { + X2: [ + {Parachain: QUARTZ_CHAIN}, + { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }, + ], + }, + }, + }; + + const id = { + Token: 'KAR', + }; + + const destWeight = 50000000; + + const tx = api.tx.xTokens.transfer(id as any, 100_000_000_000, destination, destWeight); + const events = await submitTransactionAsync(alice, tx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + }, karuraOptions()); + + await usingApi(async api => { + const maxWaitBlocks = 3; + const xcmpQueueFailEvent = await waitEvent(api, maxWaitBlocks, 'xcmpQueue', 'Fail'); + + expect( + xcmpQueueFailEvent != null, + '[Karura] xcmpQueue.FailEvent event is expected', + ).to.be.true; + + const event = xcmpQueueFailEvent!.event; + const outcome = event.data[1] as XcmV2TraitsError; + + expect( + outcome.isUntrustedReserveLocation, + '[Karura] The XCM error should be `UntrustedReserveLocation`', + ).to.be.true; + }); + }); +}); + +describe_xcm('[XCM] Integration test: Exchanging QTZ with Moonriver', () => { + + // Quartz constants + let quartzAlice: IKeyringPair; + let quartzAssetLocation; + + let randomAccountQuartz: IKeyringPair; + let randomAccountMoonriver: IKeyringPair; + + // Moonriver constants + let assetId: string; + + const moonriverKeyring = new Keyring({type: 'ethereum'}); + const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133'; + const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b'; + const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68'; + + const alithAccount = moonriverKeyring.addFromUri(alithPrivateKey, undefined, 'ethereum'); + const baltatharAccount = moonriverKeyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum'); + const dorothyAccount = moonriverKeyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum'); + + const councilVotingThreshold = 2; + const technicalCommitteeThreshold = 2; + const votingPeriod = 3; + const delayPeriod = 0; + + const quartzAssetMetadata = { + name: 'xcQuartz', + symbol: 'xcQTZ', + decimals: 18, + isFrozen: false, + minimalBalance: 1, + }; + + let balanceQuartzTokenInit: bigint; + let balanceQuartzTokenMiddle: bigint; + let balanceQuartzTokenFinal: bigint; + let balanceForeignQtzTokenInit: bigint; + let balanceForeignQtzTokenMiddle: bigint; + let balanceForeignQtzTokenFinal: bigint; + let balanceMovrTokenInit: bigint; + let balanceMovrTokenMiddle: bigint; + let balanceMovrTokenFinal: bigint; + + before(async () => { + await usingApi(async (api, privateKeyWrapper) => { + const keyringEth = new Keyring({type: 'ethereum'}); + const keyringSr25519 = new Keyring({type: 'sr25519'}); + + quartzAlice = privateKeyWrapper('//Alice'); + randomAccountQuartz = generateKeyringPair(keyringSr25519); + randomAccountMoonriver = generateKeyringPair(keyringEth); + + balanceForeignQtzTokenInit = 0n; + }); + + await usingApi( + async (api) => { + + // >>> Sponsoring Dorothy >>> + console.log('Sponsoring Dorothy.......'); + const tx0 = api.tx.balances.transfer(dorothyAccount.address, 11_000_000_000_000_000_000n); + const events0 = await submitTransactionAsync(alithAccount, tx0); + const result0 = getGenericResult(events0); + expect(result0.success).to.be.true; + console.log('Sponsoring Dorothy.......DONE'); + // <<< Sponsoring Dorothy <<< + + const sourceLocation: MultiLocation = api.createType( + 'MultiLocation', + { + parents: 1, + interior: {X1: {Parachain: QUARTZ_CHAIN}}, + }, + ); + + quartzAssetLocation = {XCM: sourceLocation}; + const existentialDeposit = 1; + const isSufficient = true; + const unitsPerSecond = '1'; + const numAssetsWeightHint = 0; + + const registerTx = api.tx.assetManager.registerForeignAsset( + quartzAssetLocation, + quartzAssetMetadata, + existentialDeposit, + isSufficient, + ); + console.log('Encoded proposal for registerAsset is %s', registerTx.method.toHex() || ''); + + const setUnitsTx = api.tx.assetManager.setAssetUnitsPerSecond( + quartzAssetLocation, + unitsPerSecond, + numAssetsWeightHint, + ); + console.log('Encoded proposal for setAssetUnitsPerSecond is %s', setUnitsTx.method.toHex() || ''); + + const batchCall = api.tx.utility.batchAll([registerTx, setUnitsTx]); + console.log('Encoded proposal for batchCall is %s', batchCall.method.toHex() || ''); + + // >>> Note motion preimage >>> + console.log('Note motion preimage.......'); + const encodedProposal = batchCall?.method.toHex() || ''; + const proposalHash = blake2AsHex(encodedProposal); + console.log('Encoded proposal for batch utility after schedule is %s', encodedProposal); + console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash); + console.log('Encoded length %d', encodedProposal.length); + + const tx1 = api.tx.democracy.notePreimage(encodedProposal); + const events1 = await submitTransactionAsync(baltatharAccount, tx1); + const result1 = getGenericResult(events1); + expect(result1.success).to.be.true; + console.log('Note motion preimage.......DONE'); + // <<< Note motion preimage <<< + + // >>> Propose external motion through council >>> + console.log('Propose external motion through council.......'); + const externalMotion = api.tx.democracy.externalProposeMajority(proposalHash); + const tx2 = api.tx.councilCollective.propose( + councilVotingThreshold, + externalMotion, + externalMotion.encodedLength, + ); + const events2 = await submitTransactionAsync(baltatharAccount, tx2); + const result2 = getGenericResult(events2); + expect(result2.success).to.be.true; + + const encodedMotion = externalMotion?.method.toHex() || ''; + const motionHash = blake2AsHex(encodedMotion); + console.log('Motion hash is %s', motionHash); + + const tx3 = api.tx.councilCollective.vote(motionHash, 0, true); + { + const events3 = await submitTransactionAsync(dorothyAccount, tx3); + const result3 = getGenericResult(events3); + expect(result3.success).to.be.true; + } + { + const events3 = await submitTransactionAsync(baltatharAccount, tx3); + const result3 = getGenericResult(events3); + expect(result3.success).to.be.true; + } + + const tx4 = api.tx.councilCollective.close(motionHash, 0, 1_000_000_000, externalMotion.encodedLength); + const events4 = await submitTransactionAsync(dorothyAccount, tx4); + const result4 = getGenericResult(events4); + expect(result4.success).to.be.true; + console.log('Propose external motion through council.......DONE'); + // <<< Propose external motion through council <<< + + // >>> Fast track proposal through technical committee >>> + console.log('Fast track proposal through technical committee.......'); + const fastTrack = api.tx.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod); + const tx5 = api.tx.techCommitteeCollective.propose( + technicalCommitteeThreshold, + fastTrack, + fastTrack.encodedLength, + ); + const events5 = await submitTransactionAsync(alithAccount, tx5); + const result5 = getGenericResult(events5); + expect(result5.success).to.be.true; + + const encodedFastTrack = fastTrack?.method.toHex() || ''; + const fastTrackHash = blake2AsHex(encodedFastTrack); + console.log('FastTrack hash is %s', fastTrackHash); + + const proposalIdx = Number(await api.query.techCommitteeCollective.proposalCount()) - 1; + const tx6 = api.tx.techCommitteeCollective.vote(fastTrackHash, proposalIdx, true); + { + const events6 = await submitTransactionAsync(baltatharAccount, tx6); + const result6 = getGenericResult(events6); + expect(result6.success).to.be.true; + } + { + const events6 = await submitTransactionAsync(alithAccount, tx6); + const result6 = getGenericResult(events6); + expect(result6.success).to.be.true; + } + + const tx7 = api.tx.techCommitteeCollective + .close(fastTrackHash, proposalIdx, 1_000_000_000, fastTrack.encodedLength); + const events7 = await submitTransactionAsync(baltatharAccount, tx7); + const result7 = getGenericResult(events7); + expect(result7.success).to.be.true; + console.log('Fast track proposal through technical committee.......DONE'); + // <<< Fast track proposal through technical committee <<< + + // >>> Referendum voting >>> + console.log('Referendum voting.......'); + const tx8 = api.tx.democracy.vote( + 0, + {Standard: {balance: 10_000_000_000_000_000_000n, vote: {aye: true, conviction: 1}}}, + ); + const events8 = await submitTransactionAsync(dorothyAccount, tx8); + const result8 = getGenericResult(events8); + expect(result8.success).to.be.true; + console.log('Referendum voting.......DONE'); + // <<< Referendum voting <<< + + // >>> Acquire Quartz AssetId Info on Moonriver >>> + console.log('Acquire Quartz AssetId Info on Moonriver.......'); + + // Wait for the democracy execute + await waitNewBlocks(api, 5); + + assetId = (await api.query.assetManager.assetTypeId({ + XCM: sourceLocation, + })).toString(); + + console.log('QTZ asset ID is %s', assetId); + console.log('Acquire Quartz AssetId Info on Moonriver.......DONE'); + // >>> Acquire Quartz AssetId Info on Moonriver >>> + + // >>> Sponsoring random Account >>> + console.log('Sponsoring random Account.......'); + const tx10 = api.tx.balances.transfer(randomAccountMoonriver.address, 11_000_000_000_000_000_000n); + const events10 = await submitTransactionAsync(baltatharAccount, tx10); + const result10 = getGenericResult(events10); + expect(result10.success).to.be.true; + console.log('Sponsoring random Account.......DONE'); + // <<< Sponsoring random Account <<< + + [balanceMovrTokenInit] = await getBalance(api, [randomAccountMoonriver.address]); + }, + moonriverOptions(), + ); + + await usingApi(async (api) => { + const tx0 = api.tx.balances.transfer(randomAccountQuartz.address, 10n * TRANSFER_AMOUNT); + const events0 = await submitTransactionAsync(quartzAlice, tx0); + const result0 = getGenericResult(events0); + expect(result0.success).to.be.true; + + [balanceQuartzTokenInit] = await getBalance(api, [randomAccountQuartz.address]); + }); + }); + + it('Should connect and send QTZ to Moonriver', async () => { + await usingApi(async (api) => { + const currencyId = { + NativeAssetId: 'Here', + }; + const dest = { + V1: { + parents: 1, + interior: { + X2: [ + {Parachain: MOONRIVER_CHAIN}, + {AccountKey20: {network: 'Any', key: randomAccountMoonriver.address}}, + ], + }, + }, + }; + const amount = TRANSFER_AMOUNT; + const destWeight = 850000000; + + const tx = api.tx.xTokens.transfer(currencyId, amount, dest, destWeight); + const events = await submitTransactionAsync(randomAccountQuartz, tx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + + [balanceQuartzTokenMiddle] = await getBalance(api, [randomAccountQuartz.address]); + expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true; + + const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT; + console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', bigIntToDecimals(transactionFees)); + expect(transactionFees > 0).to.be.true; + }); + + await usingApi( + async (api) => { + await waitNewBlocks(api, 3); + + [balanceMovrTokenMiddle] = await getBalance(api, [randomAccountMoonriver.address]); + + const movrFees = balanceMovrTokenInit - balanceMovrTokenMiddle; + console.log('[Quartz -> Moonriver] transaction fees on Moonriver: %s MOVR',bigIntToDecimals(movrFees)); + expect(movrFees == 0n).to.be.true; + + const qtzRandomAccountAsset = ( + await api.query.assets.account(assetId, randomAccountMoonriver.address) + ).toJSON()! as any; + + balanceForeignQtzTokenMiddle = BigInt(qtzRandomAccountAsset['balance']); + const qtzIncomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenInit; + console.log('[Quartz -> Moonriver] income %s QTZ', bigIntToDecimals(qtzIncomeTransfer)); + expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true; + }, + moonriverOptions(), + ); + }); + + it('Should connect to Moonriver and send QTZ back', async () => { + await usingApi( + async (api) => { + const asset = { + V1: { + id: { + Concrete: { + parents: 1, + interior: { + X1: {Parachain: QUARTZ_CHAIN}, + }, + }, + }, + fun: { + Fungible: TRANSFER_AMOUNT, + }, + }, + }; + const destination = { + V1: { + parents: 1, + interior: { + X2: [ + {Parachain: QUARTZ_CHAIN}, + {AccountId32: {network: 'Any', id: randomAccountQuartz.addressRaw}}, + ], + }, + }, + }; + const destWeight = 50000000; + + const tx = api.tx.xTokens.transferMultiasset(asset, destination, destWeight); + const events = await submitTransactionAsync(randomAccountMoonriver, tx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + + [balanceMovrTokenFinal] = await getBalance(api, [randomAccountMoonriver.address]); + + const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal; + console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', bigIntToDecimals(movrFees)); + expect(movrFees > 0).to.be.true; + + const qtzRandomAccountAsset = ( + await api.query.assets.account(assetId, randomAccountMoonriver.address) + ).toJSON()! as any; + + expect(qtzRandomAccountAsset).to.be.null; + + balanceForeignQtzTokenFinal = 0n; + + const qtzOutcomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenFinal; + console.log('[Quartz -> Moonriver] outcome %s QTZ', bigIntToDecimals(qtzOutcomeTransfer)); + expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true; + }, + moonriverOptions(), + ); + + await usingApi(async (api) => { + await waitNewBlocks(api, 3); + + [balanceQuartzTokenFinal] = await getBalance(api, [randomAccountQuartz.address]); + const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle; + expect(actuallyDelivered > 0).to.be.true; + + console.log('[Moonriver -> Quartz] actually delivered %s QTZ', bigIntToDecimals(actuallyDelivered)); + + const qtzFees = TRANSFER_AMOUNT - actuallyDelivered; + console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', bigIntToDecimals(qtzFees)); + expect(qtzFees == 0n).to.be.true; + }); + }); +}); --- /dev/null +++ b/tests/src/xcm/xcmUnique.test.ts @@ -0,0 +1,799 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; + +import {WsProvider, Keyring} from '@polkadot/api'; +import {ApiOptions} from '@polkadot/api/types'; +import {IKeyringPair} from '@polkadot/types/types'; +import usingApi, {submitTransactionAsync} from '../substrate/substrate-api'; +import {getGenericResult, generateKeyringPair, waitEvent, describe_xcm, bigIntToDecimals} from '../util/helpers'; +import {MultiLocation} from '@polkadot/types/interfaces'; +import {blake2AsHex} from '@polkadot/util-crypto'; +import waitNewBlocks from '../substrate/wait-new-blocks'; +import getBalance from '../substrate/get-balance'; +import {XcmV2TraitsError, XcmV2TraitsOutcome} from '../interfaces'; + +chai.use(chaiAsPromised); +const expect = chai.expect; + +const UNIQUE_CHAIN = 2037; +const ACALA_CHAIN = 2000; +const MOONBEAM_CHAIN = 2004; + +const RELAY_PORT = 9844; +const ACALA_PORT = 9946; +const MOONBEAM_PORT = 9947; + +const ACALA_DECIMALS = 12; + +const TRANSFER_AMOUNT = 2000000000000000000000000n; + +function parachainApiOptions(port: number): ApiOptions { + return { + provider: new WsProvider('ws://127.0.0.1:' + port.toString()), + }; +} + +function acalaOptions(): ApiOptions { + return parachainApiOptions(ACALA_PORT); +} + +function moonbeamOptions(): ApiOptions { + return parachainApiOptions(MOONBEAM_PORT); +} + +function relayOptions(): ApiOptions { + return parachainApiOptions(RELAY_PORT); +} + +describe_xcm('[XCM] Integration test: Exchanging tokens with Acala', () => { + let alice: IKeyringPair; + let randomAccount: IKeyringPair; + + let balanceUniqueTokenInit: bigint; + let balanceUniqueTokenMiddle: bigint; + let balanceUniqueTokenFinal: bigint; + let balanceAcalaTokenInit: bigint; + let balanceAcalaTokenMiddle: bigint; + let balanceAcalaTokenFinal: bigint; + let balanceUniqueForeignTokenInit: bigint; + let balanceUniqueForeignTokenMiddle: bigint; + let balanceUniqueForeignTokenFinal: bigint; + + before(async () => { + await usingApi(async (api, privateKeyWrapper) => { + const keyringSr25519 = new Keyring({type: 'sr25519'}); + + alice = privateKeyWrapper('//Alice'); + randomAccount = generateKeyringPair(keyringSr25519); + }); + + // Acala side + await usingApi( + async (api) => { + const destination = { + V0: { + X2: [ + 'Parent', + { + Parachain: UNIQUE_CHAIN, + }, + ], + }, + }; + + const metadata = { + name: 'UNQ', + symbol: 'UNQ', + decimals: 18, + minimalBalance: 1, + }; + + const tx = api.tx.assetRegistry.registerForeignAsset(destination, metadata); + const sudoTx = api.tx.sudo.sudo(tx as any); + const events = await submitTransactionAsync(alice, sudoTx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + + const tx1 = api.tx.balances.transfer(randomAccount.address, 10000000000000n); + const events1 = await submitTransactionAsync(alice, tx1); + const result1 = getGenericResult(events1); + expect(result1.success).to.be.true; + + [balanceAcalaTokenInit] = await getBalance(api, [randomAccount.address]); + { + const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any; + balanceUniqueForeignTokenInit = BigInt(free); + } + }, + acalaOptions(), + ); + + // Unique side + await usingApi(async (api) => { + const tx0 = api.tx.balances.transfer(randomAccount.address, 10n * TRANSFER_AMOUNT); + const events0 = await submitTransactionAsync(alice, tx0); + const result0 = getGenericResult(events0); + expect(result0.success).to.be.true; + + [balanceUniqueTokenInit] = await getBalance(api, [randomAccount.address]); + }); + }); + + it('Should connect and send UNQ to Acala', async () => { + + // Unique side + await usingApi(async (api) => { + + const destination = { + V0: { + X2: [ + 'Parent', + { + Parachain: ACALA_CHAIN, + }, + ], + }, + }; + + const beneficiary = { + V0: { + X1: { + AccountId32: { + network: 'Any', + id: randomAccount.addressRaw, + }, + }, + }, + }; + + const assets = { + V1: [ + { + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: TRANSFER_AMOUNT, + }, + }, + ], + }; + + const feeAssetItem = 0; + + const weightLimit = { + Limited: 5000000000, + }; + + const tx = api.tx.polkadotXcm.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit); + const events = await submitTransactionAsync(randomAccount, tx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + + [balanceUniqueTokenMiddle] = await getBalance(api, [randomAccount.address]); + + const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT; + console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees)); + expect(unqFees > 0n).to.be.true; + }); + + // Acala side + await usingApi( + async (api) => { + await waitNewBlocks(api, 3); + const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any; + balanceUniqueForeignTokenMiddle = BigInt(free); + + [balanceAcalaTokenMiddle] = await getBalance(api, [randomAccount.address]); + + const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle; + const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit; + + console.log( + '[Unique -> Acala] transaction fees on Acala: %s ACA', + bigIntToDecimals(acaFees, ACALA_DECIMALS), + ); + console.log('[Unique -> Acala] income %s UNQ', bigIntToDecimals(unqIncomeTransfer)); + expect(acaFees == 0n).to.be.true; + expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true; + }, + acalaOptions(), + ); + }); + + it('Should connect to Acala and send UNQ back', async () => { + + // Acala side + await usingApi( + async (api) => { + const destination = { + V1: { + parents: 1, + interior: { + X2: [ + {Parachain: UNIQUE_CHAIN}, + { + AccountId32: { + network: 'Any', + id: randomAccount.addressRaw, + }, + }, + ], + }, + }, + }; + + const id = { + ForeignAsset: 0, + }; + + const destWeight = 50000000; + + const tx = api.tx.xTokens.transfer(id as any, TRANSFER_AMOUNT, destination, destWeight); + const events = await submitTransactionAsync(randomAccount, tx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + + [balanceAcalaTokenFinal] = await getBalance(api, [randomAccount.address]); + { + const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, id)).toJSON() as any; + balanceUniqueForeignTokenFinal = BigInt(free); + } + + const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal; + const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal; + + console.log( + '[Acala -> Unique] transaction fees on Acala: %s ACA', + bigIntToDecimals(acaFees, ACALA_DECIMALS), + ); + console.log('[Acala -> Unique] outcome %s UNQ', bigIntToDecimals(unqOutcomeTransfer)); + + expect(acaFees > 0).to.be.true; + expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true; + }, + acalaOptions(), + ); + + // Unique side + await usingApi(async (api) => { + await waitNewBlocks(api, 3); + + [balanceUniqueTokenFinal] = await getBalance(api, [randomAccount.address]); + const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle; + expect(actuallyDelivered > 0).to.be.true; + + console.log('[Acala -> Unique] actually delivered %s UNQ', bigIntToDecimals(actuallyDelivered)); + + const unqFees = TRANSFER_AMOUNT - actuallyDelivered; + console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees)); + expect(unqFees == 0n).to.be.true; + }); + }); +}); + +// These tests are relevant only when the foreign asset pallet is disabled +describe_xcm('[XCM] Integration test: Unique rejects non-native tokens', () => { + let alice: IKeyringPair; + + before(async () => { + await usingApi(async (api, privateKeyWrapper) => { + alice = privateKeyWrapper('//Alice'); + }); + }); + + it('Unique rejects tokens from the Relay', async () => { + await usingApi(async (api) => { + const destination = { + V1: { + parents: 0, + interior: {X1: { + Parachain: UNIQUE_CHAIN, + }, + }, + }}; + + const beneficiary = { + V1: { + parents: 0, + interior: {X1: { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }}, + }, + }; + + const assets = { + V1: [ + { + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: 50_000_000_000_000_000n, + }, + }, + ], + }; + + const feeAssetItem = 0; + + const weightLimit = { + Limited: 5_000_000_000, + }; + + const tx = api.tx.xcmPallet.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit); + const events = await submitTransactionAsync(alice, tx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + }, relayOptions()); + + await usingApi(async api => { + const maxWaitBlocks = 3; + const dmpQueueExecutedDownward = await waitEvent( + api, + maxWaitBlocks, + 'dmpQueue', + 'ExecutedDownward', + ); + + expect( + dmpQueueExecutedDownward != null, + '[Relay] dmpQueue.ExecutedDownward event is expected', + ).to.be.true; + + const event = dmpQueueExecutedDownward!.event; + const outcome = event.data[1] as XcmV2TraitsOutcome; + + expect( + outcome.isIncomplete, + '[Relay] The outcome of the XCM should be `Incomplete`', + ).to.be.true; + + const incomplete = outcome.asIncomplete; + expect( + incomplete[1].toString() == 'AssetNotFound', + '[Relay] The XCM error should be `AssetNotFound`', + ).to.be.true; + }); + }); + + it('Unique rejects ACA tokens from Acala', async () => { + await usingApi(async (api) => { + const destination = { + V1: { + parents: 1, + interior: { + X2: [ + {Parachain: UNIQUE_CHAIN}, + { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }, + ], + }, + }, + }; + + const id = { + Token: 'ACA', + }; + + const destWeight = 50000000; + + const tx = api.tx.xTokens.transfer(id as any, 100_000_000_000, destination, destWeight); + const events = await submitTransactionAsync(alice, tx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + }, acalaOptions()); + + await usingApi(async api => { + const maxWaitBlocks = 3; + const xcmpQueueFailEvent = await waitEvent(api, maxWaitBlocks, 'xcmpQueue', 'Fail'); + + expect( + xcmpQueueFailEvent != null, + '[Acala] xcmpQueue.FailEvent event is expected', + ).to.be.true; + + const event = xcmpQueueFailEvent!.event; + const outcome = event.data[1] as XcmV2TraitsError; + + expect( + outcome.isUntrustedReserveLocation, + '[Acala] The XCM error should be `UntrustedReserveLocation`', + ).to.be.true; + }); + }); +}); + +describe_xcm('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => { + + // Unique constants + let uniqueAlice: IKeyringPair; + let uniqueAssetLocation; + + let randomAccountUnique: IKeyringPair; + let randomAccountMoonbeam: IKeyringPair; + + // Moonbeam constants + let assetId: string; + + const moonbeamKeyring = new Keyring({type: 'ethereum'}); + const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133'; + const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b'; + const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68'; + + const alithAccount = moonbeamKeyring.addFromUri(alithPrivateKey, undefined, 'ethereum'); + const baltatharAccount = moonbeamKeyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum'); + const dorothyAccount = moonbeamKeyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum'); + + const councilVotingThreshold = 2; + const technicalCommitteeThreshold = 2; + const votingPeriod = 3; + const delayPeriod = 0; + + const uniqueAssetMetadata = { + name: 'xcUnique', + symbol: 'xcUNQ', + decimals: 18, + isFrozen: false, + minimalBalance: 1, + }; + + let balanceUniqueTokenInit: bigint; + let balanceUniqueTokenMiddle: bigint; + let balanceUniqueTokenFinal: bigint; + let balanceForeignUnqTokenInit: bigint; + let balanceForeignUnqTokenMiddle: bigint; + let balanceForeignUnqTokenFinal: bigint; + let balanceGlmrTokenInit: bigint; + let balanceGlmrTokenMiddle: bigint; + let balanceGlmrTokenFinal: bigint; + + before(async () => { + await usingApi(async (api, privateKeyWrapper) => { + const keyringEth = new Keyring({type: 'ethereum'}); + const keyringSr25519 = new Keyring({type: 'sr25519'}); + + uniqueAlice = privateKeyWrapper('//Alice'); + randomAccountUnique = generateKeyringPair(keyringSr25519); + randomAccountMoonbeam = generateKeyringPair(keyringEth); + + balanceForeignUnqTokenInit = 0n; + }); + + await usingApi( + async (api) => { + + // >>> Sponsoring Dorothy >>> + console.log('Sponsoring Dorothy.......'); + const tx0 = api.tx.balances.transfer(dorothyAccount.address, 11_000_000_000_000_000_000n); + const events0 = await submitTransactionAsync(alithAccount, tx0); + const result0 = getGenericResult(events0); + expect(result0.success).to.be.true; + console.log('Sponsoring Dorothy.......DONE'); + // <<< Sponsoring Dorothy <<< + + const sourceLocation: MultiLocation = api.createType( + 'MultiLocation', + { + parents: 1, + interior: {X1: {Parachain: UNIQUE_CHAIN}}, + }, + ); + + uniqueAssetLocation = {XCM: sourceLocation}; + const existentialDeposit = 1; + const isSufficient = true; + const unitsPerSecond = '1'; + const numAssetsWeightHint = 0; + + const registerTx = api.tx.assetManager.registerForeignAsset( + uniqueAssetLocation, + uniqueAssetMetadata, + existentialDeposit, + isSufficient, + ); + console.log('Encoded proposal for registerAsset is %s', registerTx.method.toHex() || ''); + + const setUnitsTx = api.tx.assetManager.setAssetUnitsPerSecond( + uniqueAssetLocation, + unitsPerSecond, + numAssetsWeightHint, + ); + console.log('Encoded proposal for setAssetUnitsPerSecond is %s', setUnitsTx.method.toHex() || ''); + + const batchCall = api.tx.utility.batchAll([registerTx, setUnitsTx]); + console.log('Encoded proposal for batchCall is %s', batchCall.method.toHex() || ''); + + // >>> Note motion preimage >>> + console.log('Note motion preimage.......'); + const encodedProposal = batchCall?.method.toHex() || ''; + const proposalHash = blake2AsHex(encodedProposal); + console.log('Encoded proposal for batch utility after schedule is %s', encodedProposal); + console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash); + console.log('Encoded length %d', encodedProposal.length); + + const tx1 = api.tx.democracy.notePreimage(encodedProposal); + const events1 = await submitTransactionAsync(baltatharAccount, tx1); + const result1 = getGenericResult(events1); + expect(result1.success).to.be.true; + console.log('Note motion preimage.......DONE'); + // <<< Note motion preimage <<< + + // >>> Propose external motion through council >>> + console.log('Propose external motion through council.......'); + const externalMotion = api.tx.democracy.externalProposeMajority(proposalHash); + const tx2 = api.tx.councilCollective.propose( + councilVotingThreshold, + externalMotion, + externalMotion.encodedLength, + ); + const events2 = await submitTransactionAsync(baltatharAccount, tx2); + const result2 = getGenericResult(events2); + expect(result2.success).to.be.true; + + const encodedMotion = externalMotion?.method.toHex() || ''; + const motionHash = blake2AsHex(encodedMotion); + console.log('Motion hash is %s', motionHash); + + const tx3 = api.tx.councilCollective.vote(motionHash, 0, true); + { + const events3 = await submitTransactionAsync(dorothyAccount, tx3); + const result3 = getGenericResult(events3); + expect(result3.success).to.be.true; + } + { + const events3 = await submitTransactionAsync(baltatharAccount, tx3); + const result3 = getGenericResult(events3); + expect(result3.success).to.be.true; + } + + const tx4 = api.tx.councilCollective.close(motionHash, 0, 1_000_000_000, externalMotion.encodedLength); + const events4 = await submitTransactionAsync(dorothyAccount, tx4); + const result4 = getGenericResult(events4); + expect(result4.success).to.be.true; + console.log('Propose external motion through council.......DONE'); + // <<< Propose external motion through council <<< + + // >>> Fast track proposal through technical committee >>> + console.log('Fast track proposal through technical committee.......'); + const fastTrack = api.tx.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod); + const tx5 = api.tx.techCommitteeCollective.propose( + technicalCommitteeThreshold, + fastTrack, + fastTrack.encodedLength, + ); + const events5 = await submitTransactionAsync(alithAccount, tx5); + const result5 = getGenericResult(events5); + expect(result5.success).to.be.true; + + const encodedFastTrack = fastTrack?.method.toHex() || ''; + const fastTrackHash = blake2AsHex(encodedFastTrack); + console.log('FastTrack hash is %s', fastTrackHash); + + const proposalIdx = Number(await api.query.techCommitteeCollective.proposalCount()) - 1; + const tx6 = api.tx.techCommitteeCollective.vote(fastTrackHash, proposalIdx, true); + { + const events6 = await submitTransactionAsync(baltatharAccount, tx6); + const result6 = getGenericResult(events6); + expect(result6.success).to.be.true; + } + { + const events6 = await submitTransactionAsync(alithAccount, tx6); + const result6 = getGenericResult(events6); + expect(result6.success).to.be.true; + } + + const tx7 = api.tx.techCommitteeCollective + .close(fastTrackHash, proposalIdx, 1_000_000_000, fastTrack.encodedLength); + const events7 = await submitTransactionAsync(baltatharAccount, tx7); + const result7 = getGenericResult(events7); + expect(result7.success).to.be.true; + console.log('Fast track proposal through technical committee.......DONE'); + // <<< Fast track proposal through technical committee <<< + + // >>> Referendum voting >>> + console.log('Referendum voting.......'); + const tx8 = api.tx.democracy.vote( + 0, + {Standard: {balance: 10_000_000_000_000_000_000n, vote: {aye: true, conviction: 1}}}, + ); + const events8 = await submitTransactionAsync(dorothyAccount, tx8); + const result8 = getGenericResult(events8); + expect(result8.success).to.be.true; + console.log('Referendum voting.......DONE'); + // <<< Referendum voting <<< + + // >>> Acquire Unique AssetId Info on Moonbeam >>> + console.log('Acquire Unique AssetId Info on Moonbeam.......'); + + // Wait for the democracy execute + await waitNewBlocks(api, 5); + + assetId = (await api.query.assetManager.assetTypeId({ + XCM: sourceLocation, + })).toString(); + + console.log('UNQ asset ID is %s', assetId); + console.log('Acquire Unique AssetId Info on Moonbeam.......DONE'); + // >>> Acquire Unique AssetId Info on Moonbeam >>> + + // >>> Sponsoring random Account >>> + console.log('Sponsoring random Account.......'); + const tx10 = api.tx.balances.transfer(randomAccountMoonbeam.address, 11_000_000_000_000_000_000n); + const events10 = await submitTransactionAsync(baltatharAccount, tx10); + const result10 = getGenericResult(events10); + expect(result10.success).to.be.true; + console.log('Sponsoring random Account.......DONE'); + // <<< Sponsoring random Account <<< + + [balanceGlmrTokenInit] = await getBalance(api, [randomAccountMoonbeam.address]); + }, + moonbeamOptions(), + ); + + await usingApi(async (api) => { + const tx0 = api.tx.balances.transfer(randomAccountUnique.address, 10n * TRANSFER_AMOUNT); + const events0 = await submitTransactionAsync(uniqueAlice, tx0); + const result0 = getGenericResult(events0); + expect(result0.success).to.be.true; + + [balanceUniqueTokenInit] = await getBalance(api, [randomAccountUnique.address]); + }); + }); + + it('Should connect and send UNQ to Moonbeam', async () => { + await usingApi(async (api) => { + const currencyId = { + NativeAssetId: 'Here', + }; + const dest = { + V1: { + parents: 1, + interior: { + X2: [ + {Parachain: MOONBEAM_CHAIN}, + {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}}, + ], + }, + }, + }; + const amount = TRANSFER_AMOUNT; + const destWeight = 850000000; + + const tx = api.tx.xTokens.transfer(currencyId, amount, dest, destWeight); + const events = await submitTransactionAsync(randomAccountUnique, tx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + + [balanceUniqueTokenMiddle] = await getBalance(api, [randomAccountUnique.address]); + expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true; + + const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT; + console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', bigIntToDecimals(transactionFees)); + expect(transactionFees > 0).to.be.true; + }); + + await usingApi( + async (api) => { + await waitNewBlocks(api, 3); + + [balanceGlmrTokenMiddle] = await getBalance(api, [randomAccountMoonbeam.address]); + + const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle; + console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', bigIntToDecimals(glmrFees)); + expect(glmrFees == 0n).to.be.true; + + const unqRandomAccountAsset = ( + await api.query.assets.account(assetId, randomAccountMoonbeam.address) + ).toJSON()! as any; + + balanceForeignUnqTokenMiddle = BigInt(unqRandomAccountAsset['balance']); + const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit; + console.log('[Unique -> Moonbeam] income %s UNQ', bigIntToDecimals(unqIncomeTransfer)); + expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true; + }, + moonbeamOptions(), + ); + }); + + it('Should connect to Moonbeam and send UNQ back', async () => { + await usingApi( + async (api) => { + const asset = { + V1: { + id: { + Concrete: { + parents: 1, + interior: { + X1: {Parachain: UNIQUE_CHAIN}, + }, + }, + }, + fun: { + Fungible: TRANSFER_AMOUNT, + }, + }, + }; + const destination = { + V1: { + parents: 1, + interior: { + X2: [ + {Parachain: UNIQUE_CHAIN}, + {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}}, + ], + }, + }, + }; + const destWeight = 50000000; + + const tx = api.tx.xTokens.transferMultiasset(asset, destination, destWeight); + const events = await submitTransactionAsync(randomAccountMoonbeam, tx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + + [balanceGlmrTokenFinal] = await getBalance(api, [randomAccountMoonbeam.address]); + + const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal; + console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', bigIntToDecimals(glmrFees)); + expect(glmrFees > 0).to.be.true; + + const unqRandomAccountAsset = ( + await api.query.assets.account(assetId, randomAccountMoonbeam.address) + ).toJSON()! as any; + + expect(unqRandomAccountAsset).to.be.null; + + balanceForeignUnqTokenFinal = 0n; + + const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal; + console.log('[Unique -> Moonbeam] outcome %s UNQ', bigIntToDecimals(unqOutcomeTransfer)); + expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true; + }, + moonbeamOptions(), + ); + + await usingApi(async (api) => { + await waitNewBlocks(api, 3); + + [balanceUniqueTokenFinal] = await getBalance(api, [randomAccountUnique.address]); + const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle; + expect(actuallyDelivered > 0).to.be.true; + + console.log('[Moonbeam -> Unique] actually delivered %s UNQ', bigIntToDecimals(actuallyDelivered)); + + const unqFees = TRANSFER_AMOUNT - actuallyDelivered; + console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees)); + expect(unqFees == 0n).to.be.true; + }); + }); +}); --- a/tests/src/xcmTransfer.test.ts +++ b/tests/src/xcmTransfer.test.ts @@ -78,7 +78,7 @@ let balanceOnKaruraBefore: bigint; await usingApi(async (api) => { - const {free} = (await api.query.tokens.accounts(alice.addressRaw, {ForeignAsset: 0})).toJSON() as any; + const {free} = (await api.query.tokens.accounts(alice.addressRaw, {ForeignAssetId: 0})).toJSON() as any; balanceOnKaruraBefore = free; }, {provider: new WsProvider('ws://127.0.0.1:' + KARURA_PORT)}); @@ -136,7 +136,7 @@ await usingApi(async (api) => { // todo do something about instant sealing, where there might not be any new blocks await waitNewBlocks(api, 3); - const {free} = (await api.query.tokens.accounts(alice.addressRaw, {ForeignAsset: 0})).toJSON() as any; + const {free} = (await api.query.tokens.accounts(alice.addressRaw, {ForeignAssetId: 0})).toJSON() as any; expect(free > balanceOnKaruraBefore).to.be.true; }, {provider: new WsProvider('ws://127.0.0.1:' + KARURA_PORT)}); }); @@ -165,7 +165,7 @@ }; const id = { - ForeignAsset: 0, + ForeignAssetId: 0, }; const amount = TRANSFER_AMOUNT;