diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index b181074e..5e8b3ee7 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -13,10 +13,9 @@ permissions: jobs: contracts: name: Contracts - uses: ElrondNetwork/elrond-actions/.github/workflows/contracts.yml@v1 + uses: multiversx/mx-sc-actions/.github/workflows/contracts.yml@v2.3.1 with: - rust-toolchain: nightly-2022-03-01 - vmtools-version: v1.4.43 - extra-build-args: --ignore-eei-checks + rust-toolchain: nightly-2023-05-26 + vmtools-version: v1.4.60 secrets: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..6b6c8e53 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,79 @@ +name: Create release + +on: + workflow_dispatch: + inputs: + tag: + required: true + description: Release tag + title: + required: true + description: Release title + image_tag: + type: choice + required: true + description: Image multiversx/sdk-rust-contract-builder + options: + - v3.1.0 + - v4.0.2 + - v4.0.3 + +permissions: + contents: write + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + ref: ${{ env.GITHUB_REF_NAME }} + fetch-depth: 0 + repository: ${{ env.GITHUB_REPOSITORY }} + + - name: Download build script + run: | + wget https://raw.githubusercontent.com/multiversx/mx-sdk-rust-contract-builder/${{ github.event.inputs.image_tag }}/build_with_docker.py + - name: Build contract + run: | + python3 ./build_with_docker.py --no-docker-tty --image=multiversx/sdk-rust-contract-builder:${{ github.event.inputs.image_tag }} --project=. --output=/home/runner/work/output-from-docker + - name: Save artifacts + uses: actions/upload-artifact@v2 + with: + name: built-contracts + path: | + /home/runner/work/output-from-docker/**/*.wasm + /home/runner/work/output-from-docker/**/*.abi.json + if-no-files-found: error + + release: + needs: [build] + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v2 + with: + fetch-depth: "0" + + - name: Download all workflow artifacts + uses: actions/download-artifact@v2 + with: + path: assets + + - name: Create release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "Built using Docker image: **multiversx/sdk-rust-contract-builder:${{ github.event.inputs.image_tag }}**." >> notes.txt + echo "" >> notes.txt + echo "## Codehash (blake2b):" >> notes.txt + echo "" >> notes.txt + for i in $(find ./assets -type f -name *.wasm); do + filename=$(basename ${i}) + checksum=($(b2sum -l 256 ${i})) + echo " - **${filename}**: \`${checksum}\`" >> notes.txt + done + gh release create ${{ github.event.inputs.tag }} --target=$GITHUB_SHA --prerelease --title="${{ github.event.inputs.title }}" --generate-notes --notes-file=notes.txt + sleep 10 + gh release upload ${{ github.event.inputs.tag }} $(find ./assets -type f) \ No newline at end of file diff --git a/.gitignore b/.gitignore index 73fc5f2e..d1558eae 100644 --- a/.gitignore +++ b/.gitignore @@ -2,23 +2,9 @@ # will have compiled files and executables **/target/ **/output/ -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk # VSCode specific files .vscode/* -elrond.workspace.json - -# Elrond IDE -**/node_modules -**/output/** -**/testnet/** -**/erdpy.data-storage.json -**/*.interaction.json - -# Python scripts -**/__pycache__/** diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..dd18cf5c --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1353 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "bridged-tokens-wrapper" +version = "0.0.0" +dependencies = [ + "multiversx-sc", + "multiversx-sc-modules", + "multiversx-sc-scenario", + "transaction", +] + +[[package]] +name = "bridged-tokens-wrapper-meta" +version = "0.0.0" +dependencies = [ + "bridged-tokens-wrapper", + "multiversx-sc-meta", +] + +[[package]] +name = "bstr" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "clap_lex" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "colored" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" +dependencies = [ + "is-terminal", + "lazy_static", + "windows-sys", +] + +[[package]] +name = "common-path" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", +] + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + +[[package]] +name = "errno" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "esdt-safe" +version = "0.0.0" +dependencies = [ + "eth-address", + "fee-estimator-module", + "max-bridged-amount-module", + "multiversx-sc", + "multiversx-sc-modules", + "multiversx-sc-scenario", + "token-module", + "transaction", + "tx-batch-module", +] + +[[package]] +name = "esdt-safe-meta" +version = "0.0.0" +dependencies = [ + "esdt-safe", + "multiversx-sc-meta", +] + +[[package]] +name = "eth-address" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "fee-estimator-module" +version = "0.0.0" +dependencies = [ + "multiversx-sc", + "multiversx-sc-scenario", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "globset" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "ignore" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" +dependencies = [ + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + +[[package]] +name = "linux-raw-sys" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "max-bridged-amount-module" +version = "0.0.0" +dependencies = [ + "multiversx-sc", + "multiversx-sc-scenario", +] + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "multi-transfer-esdt" +version = "0.0.0" +dependencies = [ + "bridged-tokens-wrapper", + "max-bridged-amount-module", + "multiversx-sc", + "multiversx-sc-scenario", + "transaction", + "tx-batch-module", +] + +[[package]] +name = "multi-transfer-esdt-meta" +version = "0.0.0" +dependencies = [ + "multi-transfer-esdt", + "multiversx-sc-meta", +] + +[[package]] +name = "multisig" +version = "0.0.0" +dependencies = [ + "esdt-safe", + "eth-address", + "fee-estimator-module", + "max-bridged-amount-module", + "multi-transfer-esdt", + "multiversx-sc", + "multiversx-sc-modules", + "multiversx-sc-scenario", + "token-module", + "transaction", + "tx-batch-module", +] + +[[package]] +name = "multisig-meta" +version = "0.0.0" +dependencies = [ + "multisig", + "multiversx-sc-meta", +] + +[[package]] +name = "multiversx-chain-scenario-format" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d2592a441608937c5aebec6732c38e6097f58de1dc9a64d7dbe98e0ab97a3c0" +dependencies = [ + "bech32", + "hex", + "num-bigint", + "num-traits", + "serde", + "serde_json", + "sha3 0.9.1", +] + +[[package]] +name = "multiversx-chain-vm" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212ddb5a9199f5d94d873a30126fb24b9a80383af748911c5dea71595f39a5c7" +dependencies = [ + "bech32", + "ed25519-dalek", + "hex", + "itertools", + "multiversx-chain-scenario-format", + "multiversx-sc", + "multiversx-sc-meta", + "num-bigint", + "num-traits", + "rand 0.8.5", + "rand_seeder", + "serde", + "serde_json", + "sha2 0.10.8", + "sha3 0.10.8", +] + +[[package]] +name = "multiversx-sc" +version = "0.41.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfbe80ec68fedf299dd65469f8999cf3c0f884ffa497122428f08303bcb8b884" +dependencies = [ + "bitflags 1.3.2", + "hashbrown 0.13.2", + "hex-literal", + "multiversx-sc-codec", + "multiversx-sc-derive", + "num-traits", +] + +[[package]] +name = "multiversx-sc-codec" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d70ea458247d263b7e9fdfb207530b6a530546247139c162450e515c013a18" +dependencies = [ + "arrayvec", + "multiversx-sc-codec-derive", + "num-bigint", +] + +[[package]] +name = "multiversx-sc-codec-derive" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ad6920f80fda67fc60fd51aaa9f83ec7e069470f60a068c81205f9da5d05a30" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "multiversx-sc-derive" +version = "0.41.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c2b87d817f2176bf1830bef938884f24267516c334a377e71a96bc18ebb09c1" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "radix_trie", + "syn 1.0.109", +] + +[[package]] +name = "multiversx-sc-meta" +version = "0.41.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a027febbf84d887b429699cf8cbe2a9d1776d26dee7359d9b7afdc3407e57da0" +dependencies = [ + "clap", + "colored", + "common-path", + "convert_case", + "hex", + "lazy_static", + "multiversx-sc", + "pathdiff", + "ruplacer", + "rustc_version", + "serde", + "serde_json", + "toml", +] + +[[package]] +name = "multiversx-sc-modules" +version = "0.41.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704825223788bfdfd622980cae8feedae82609fded5ef5f53af31661d5e02312" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "multiversx-sc-scenario" +version = "0.41.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0466020f9dcceaeb5ffe293e07c1eb43d81395170267e1b77445063bf17279cc" +dependencies = [ + "colored", + "hex", + "itertools", + "multiversx-chain-vm", + "num-traits", + "pathdiff", + "serde", + "serde_json", + "sha2 0.10.8", +] + +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.10", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_seeder" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2890aaef0aa82719a50e808de264f9484b74b442e1a3a0e5ee38243ac40bdb" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "regex" +version = "1.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + +[[package]] +name = "ruplacer" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58a26a1b15ff113d31d139357f7422708312978ed69cd5dd47e36d1b80b7eaf3" +dependencies = [ + "Inflector", + "anyhow", + "clap", + "colored", + "ignore", + "regex", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25469e9ae0f3d0047ca8b93fc56843f38e6774f0914a107ff8b41be8be8e0b7" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "semver" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" + +[[package]] +name = "serde" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "serde_json" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "keccak", + "opaque-debug", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + +[[package]] +name = "smallvec" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "token-module" +version = "0.0.0" +dependencies = [ + "fee-estimator-module", + "multiversx-sc", + "multiversx-sc-scenario", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "indexmap", + "serde", +] + +[[package]] +name = "transaction" +version = "0.0.0" +dependencies = [ + "eth-address", + "multiversx-sc", +] + +[[package]] +name = "tx-batch-module" +version = "0.0.0" +dependencies = [ + "multiversx-sc", + "multiversx-sc-scenario", + "transaction", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "zeroize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] diff --git a/Cargo.toml b/Cargo.toml index 858229d3..f9432301 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,11 @@ [workspace] -resolver = "2" - members = [ - "neversea", - "neversea/meta" + "esdt-safe", + "esdt-safe/meta", + "multi-transfer-esdt", + "multi-transfer-esdt/meta", + "multisig", + "multisig/meta", + "bridged-tokens-wrapper", + "bridged-tokens-wrapper/meta" ] diff --git a/neversea/.gitignore b/bridged-tokens-wrapper/.gitignore similarity index 82% rename from neversea/.gitignore rename to bridged-tokens-wrapper/.gitignore index 920d759a..ced1b927 100644 --- a/neversea/.gitignore +++ b/bridged-tokens-wrapper/.gitignore @@ -3,5 +3,5 @@ /target/ */target/ -# The erdpy output +# The mxpy output output* diff --git a/bridged-tokens-wrapper/Cargo.toml b/bridged-tokens-wrapper/Cargo.toml new file mode 100644 index 00000000..dc1bbfd7 --- /dev/null +++ b/bridged-tokens-wrapper/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "bridged-tokens-wrapper" +version = "0.0.0" +authors = ["Alin Cruceat "] +edition = "2018" +publish = false + +[lib] +path = "src/lib.rs" +[dependencies.transaction] +path = "../common/transaction" + +[dependencies.multiversx-sc] +version = "0.41.3" + +[dependencies.multiversx-sc-modules] +version = "0.41.3" +[dev-dependencies.multiversx-sc-scenario] +version = "0.41.3" diff --git a/bridged-tokens-wrapper/mandos/add_wrapped_token.scen.json b/bridged-tokens-wrapper/mandos/add_wrapped_token.scen.json new file mode 100644 index 00000000..0a0d8c44 --- /dev/null +++ b/bridged-tokens-wrapper/mandos/add_wrapped_token.scen.json @@ -0,0 +1,131 @@ +{ + "name": "add wrapped token", + "gasSchedule": "v4", + "steps": [ + { + "step": "setState", + "accounts": { + "address:owner": { + "nonce": "0", + "balance": "0", + "storage": {} + }, + "address:user": { + "nonce": "0", + "esdt": { + "str:USDC-aaaaaa": { + "balance": "500000000000000" + }, + "str:USDC-bbbbbb": { + "balance": "500000000000000" + }, + "str:USDC-cccccc": { + "balance": "500000000000000" + }, + "str:WUSDC-abcdef": { + "balance": "500" + } + }, + "storage": {} + }, + "sc:bridged_tokens_wrapper": { + "nonce": "0", + "esdt": { + "str:WUSDC-abcdef": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:WUSDC-uvwxyz": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + } + }, + "code": "file:../output/bridged-tokens-wrapper.wasm", + "owner": "address:owner" + } + } + }, + { + "step": "scCall", + "txId": "add-wrapped-token", + "tx": { + "from": "address:owner", + "to": "sc:bridged_tokens_wrapper", + "function": "addWrappedToken", + "arguments": [ + "str:WUSDC-abcdef", + "u32:6" + ], + "gasLimit": "5,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:owner": { + "nonce": "1", + "balance": "0", + "storage": {} + }, + "address:user": { + "nonce": "0", + "esdt": { + "str:USDC-aaaaaa": { + "balance": "500000000000000" + }, + "str:USDC-bbbbbb": { + "balance": "500000000000000" + }, + "str:USDC-cccccc": { + "balance": "500000000000000" + }, + "str:WUSDC-abcdef": { + "balance": "500" + } + }, + "storage": {} + }, + "sc:bridged_tokens_wrapper": { + "nonce": "0", + "esdt": { + "str:WUSDC-abcdef": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:WUSDC-uvwxyz": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:universalBridgedTokenIds.len": "1", + "str:universalBridgedTokenIds.index|nested:str:WUSDC-abcdef": "1", + "str:universalBridgedTokenIds.item|u32:1": "str:WUSDC-abcdef", + "str:token_decimals_num|nested:str:WUSDC-abcdef": "6" + }, + "code": "file:../output/bridged-tokens-wrapper.wasm", + "owner": "address:owner" + } + } + } + ] +} diff --git a/bridged-tokens-wrapper/mandos/blacklist_token.scen.json b/bridged-tokens-wrapper/mandos/blacklist_token.scen.json new file mode 100644 index 00000000..7f36f67d --- /dev/null +++ b/bridged-tokens-wrapper/mandos/blacklist_token.scen.json @@ -0,0 +1,123 @@ +{ + "name": "blacklist token", + "gasSchedule": "v4", + "steps": [ + { + "step": "externalSteps", + "path": "wrap_token.scen.json" + }, + { + "step": "scCall", + "txId": "blacklist-token", + "tx": { + "from": "address:owner", + "to": "sc:bridged_tokens_wrapper", + "function": "blacklistToken", + "arguments": [ + "str:USDC-aaaaaa" + ], + "gasLimit": "5,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "unwrap-token", + "tx": { + "from": "address:user", + "to": "sc:bridged_tokens_wrapper", + "value": "0", + "esdt": { + "tokenIdentifier": "str:WUSDC-abcdef", + "value": "100" + }, + "function": "unwrapToken", + "arguments": [ + "str:USDC-aaaaaa" + ], + "gasLimit": "5,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:Esdt token unavailable", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:owner": { + "nonce": "4", + "balance": "0", + "storage": {} + }, + "address:user": { + "nonce": "5", + "esdt": { + "str:USDC-aaaaaa": { + "balance": "200000000000000" + }, + "str:USDC-bbbbbb": { + "balance": "500000000000000" + }, + "str:USDC-cccccc": { + "balance": "400000000000000" + }, + "str:WUSDC-abcdef": { + "balance": "900" + } + }, + "storage": {} + }, + "sc:bridged_tokens_wrapper": { + "nonce": "0", + "esdt": { + "str:WUSDC-abcdef": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:WUSDC-uvwxyz": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:USDC-aaaaaa": { + "balance": "300000000000000" + }, + "str:USDC-cccccc": { + "balance": "100000000000000" + } + }, + "storage": { + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.len": "1", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.index|nested:str:USDC-cccccc": "1", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.item|u32:1": "str:USDC-cccccc", + "str:chainSpecificToUniversalMapping|nested:str:USDC-cccccc": "str:WUSDC-abcdef", + "str:universalBridgedTokenIds.len": "1", + "str:universalBridgedTokenIds.index|nested:str:WUSDC-abcdef": "1", + "str:universalBridgedTokenIds.item|u32:1": "str:WUSDC-abcdef", + "str:tokenLiquidity|nested:str:USDC-aaaaaa": "300000000000000", + "str:tokenLiquidity|nested:str:USDC-cccccc": "100000000000000", + "str:token_decimals_num|nested:str:WUSDC-abcdef": "6", + "str:token_decimals_num|nested:str:USDC-cccccc": "18" + }, + "code": "file:../output/bridged-tokens-wrapper.wasm", + "owner": "address:owner" + } + } + } + ] +} diff --git a/bridged-tokens-wrapper/mandos/remove_wrapped_token.scen.json b/bridged-tokens-wrapper/mandos/remove_wrapped_token.scen.json new file mode 100644 index 00000000..79970914 --- /dev/null +++ b/bridged-tokens-wrapper/mandos/remove_wrapped_token.scen.json @@ -0,0 +1,114 @@ +{ + "name": "remove wrapped token", + "gasSchedule": "v4", + "steps": [ + { + "step": "externalSteps", + "path": "blacklist_token.scen.json" + }, + { + "step": "scCall", + "txId": "add-wrapped-token", + "tx": { + "from": "address:owner", + "to": "sc:bridged_tokens_wrapper", + "function": "addWrappedToken", + "arguments": [ + "str:WUSDC-uvwxyz", + "u32:18" + ], + "gasLimit": "5,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "remove-wrapped-token", + "tx": { + "from": "address:owner", + "to": "sc:bridged_tokens_wrapper", + "function": "removeWrappedToken", + "arguments": [ + "str:WUSDC-abcdef" + ], + "gasLimit": "5,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:owner": { + "nonce": "6", + "balance": "0", + "storage": {} + }, + "address:user": { + "nonce": "5", + "esdt": { + "str:USDC-aaaaaa": { + "balance": "200000000000000" + }, + "str:USDC-bbbbbb": { + "balance": "500000000000000" + }, + "str:USDC-cccccc": { + "balance": "400000000000000" + }, + "str:WUSDC-abcdef": { + "balance": "900" + } + }, + "storage": {} + }, + "sc:bridged_tokens_wrapper": { + "nonce": "0", + "esdt": { + "str:WUSDC-abcdef": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:WUSDC-uvwxyz": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:USDC-aaaaaa": { + "balance": "300000000000000" + }, + "str:USDC-cccccc": { + "balance": "100000000000000" + } + }, + "storage": { + "str:universalBridgedTokenIds.len": "1", + "str:universalBridgedTokenIds.index|nested:str:WUSDC-uvwxyz": "1", + "str:universalBridgedTokenIds.item|u32:1": "str:WUSDC-uvwxyz", + "str:tokenLiquidity|nested:str:USDC-aaaaaa": "300000000000000", + "str:tokenLiquidity|nested:str:USDC-cccccc": "100000000000000", + "str:token_decimals_num|nested:str:WUSDC-uvwxyz": "18" + }, + "code": "file:../output/bridged-tokens-wrapper.wasm", + "owner": "address:owner" + } + } + } + ] +} diff --git a/bridged-tokens-wrapper/mandos/setup.scen.json b/bridged-tokens-wrapper/mandos/setup.scen.json new file mode 100644 index 00000000..6a11bc37 --- /dev/null +++ b/bridged-tokens-wrapper/mandos/setup.scen.json @@ -0,0 +1,232 @@ +{ + "name": "add wrapped token", + "gasSchedule": "v4", + "steps": [ + { + "step": "setState", + "accounts": { + "address:owner": { + "nonce": "3", + "balance": "0", + "storage": {} + }, + "address:user": { + "nonce": "7", + "esdt": { + "str:USDC-aaaaaa": { + "balance": "300000000000000" + }, + "str:USDC-bbbbbb": { + "balance": "500000000000000" + }, + "str:USDC-cccccc": { + "balance": "400000000000000" + }, + "str:WUSDC-abcdef": { + "balance": "900" + }, + "str:WUSDC-ghijkl": { + "balance": "900" + }, + "str:WUSDC-mnopqr": { + "balance": "900" + } + }, + "storage": {} + }, + "sc:bridged_tokens_wrapper": { + "nonce": "0", + "esdt": { + "str:WUSDC-abcdef": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:WUSDC-ghijkl": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:WUSDC-mnopqr": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:WUSDC-uvwxyz": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:USDC-aaaaaa": { + "balance": "300000000000000" + }, + "str:USDC-cccccc": { + "balance": "100000000000000" + }, + "str:USDC-ffffff": { + "balance": "400000000000000" + }, + "str:USDC-eeeeee": { + "balance": "400000000000000" + } + }, + "storage": { + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.len": "2", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.index|nested:str:USDC-aaaaaa": "1", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.index|nested:str:USDC-cccccc": "2", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.item|u32:1": "str:USDC-aaaaaa", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.item|u32:2": "str:USDC-cccccc", + "str:chainSpecificToUniversalMapping|nested:str:USDC-aaaaaa": "str:WUSDC-abcdef", + "str:chainSpecificToUniversalMapping|nested:str:USDC-cccccc": "str:WUSDC-abcdef", + "str:chainSpecificTokenIds|nested:str:WUSDC-ghijkl|str:.len": "1", + "str:chainSpecificTokenIds|nested:str:WUSDC-ghijkl|str:.index|nested:str:USDC-ffffff": "1", + "str:chainSpecificTokenIds|nested:str:WUSDC-ghijkl|str:.item|u32:1": "str:USDC-ffffff", + "str:chainSpecificToUniversalMapping|nested:str:USDC-ffffff": "str:WUSDC-ghijkl", + "str:chainSpecificTokenIds|nested:str:WUSDC-mnopqr|str:.len": "1", + "str:chainSpecificTokenIds|nested:str:WUSDC-mnopqr|str:.item|u32:1": "str:USDC-eeeeee", + "str:chainSpecificTokenIds|nested:str:WUSDC-mnopqr|str:.index|nested:str:USDC-eeeeee": "1", + "str:chainSpecificToUniversalMapping|nested:str:USDC-eeeeee": "str:WUSDC-mnopqr", + "str:universalBridgedTokenIds.len": "3", + "str:universalBridgedTokenIds.index|nested:str:WUSDC-abcdef": "1", + "str:universalBridgedTokenIds.item|u32:1": "str:WUSDC-abcdef", + "str:universalBridgedTokenIds.index|nested:str:WUSDC-ghijkl": "2", + "str:universalBridgedTokenIds.item|u32:2": "str:WUSDC-ghijkl", + "str:universalBridgedTokenIds.index|nested:str:WUSDC-mnopqr": "3", + "str:universalBridgedTokenIds.item|u32:3": "str:WUSDC-mnopqr", + "str:tokenLiquidity|nested:str:USDC-aaaaaa": "300000000000000", + "str:tokenLiquidity|nested:str:USDC-cccccc": "100000000000000", + "str:tokenLiquidity|nested:str:USDC-eeeeee": "400000000000000", + "str:tokenLiquidity|nested:str:USDC-ffffff": "400000000000000", + "str:token_decimals_num|nested:str:WUSDC-abcdef": "6", + "str:token_decimals_num|nested:str:WUSDC-ghijkl": "6", + "str:token_decimals_num|nested:str:USDC-aaaaaa": "18", + "str:token_decimals_num|nested:str:USDC-cccccc": "18" + }, + "code": "file:../output/bridged-tokens-wrapper.wasm", + "owner": "address:owner" + } + } + }, + { + "step": "checkState", + "accounts": { + "address:owner": { + "nonce": "3", + "balance": "0", + "storage": {} + }, + "address:user": { + "nonce": "7", + "esdt": { + "str:USDC-aaaaaa": { + "balance": "300000000000000" + }, + "str:USDC-bbbbbb": { + "balance": "500000000000000" + }, + "str:USDC-cccccc": { + "balance": "400000000000000" + }, + "str:WUSDC-abcdef": { + "balance": "900" + }, + "str:WUSDC-ghijkl": { + "balance": "900" + }, + "str:WUSDC-mnopqr": { + "balance": "900" + } + }, + "storage": {} + }, + "sc:bridged_tokens_wrapper": { + "nonce": "0", + "esdt": { + "str:WUSDC-abcdef": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:WUSDC-ghijkl": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:WUSDC-mnopqr": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:WUSDC-uvwxyz": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:USDC-aaaaaa": { + "balance": "300000000000000" + }, + "str:USDC-cccccc": { + "balance": "100000000000000" + }, + "str:USDC-eeeeee": { + "balance": "400000000000000" + }, + "str:USDC-ffffff": { + "balance": "400000000000000" + } + }, + "storage": { + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.len": "2", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.index|nested:str:USDC-aaaaaa": "1", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.index|nested:str:USDC-cccccc": "2", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.item|u32:1": "str:USDC-aaaaaa", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.item|u32:2": "str:USDC-cccccc", + "str:chainSpecificToUniversalMapping|nested:str:USDC-aaaaaa": "str:WUSDC-abcdef", + "str:chainSpecificToUniversalMapping|nested:str:USDC-cccccc": "str:WUSDC-abcdef", + "str:chainSpecificTokenIds|nested:str:WUSDC-ghijkl|str:.len": "1", + "str:chainSpecificTokenIds|nested:str:WUSDC-ghijkl|str:.index|nested:str:USDC-ffffff": "1", + "str:chainSpecificTokenIds|nested:str:WUSDC-ghijkl|str:.item|u32:1": "str:USDC-ffffff", + "str:chainSpecificToUniversalMapping|nested:str:USDC-ffffff": "str:WUSDC-ghijkl", + "str:chainSpecificTokenIds|nested:str:WUSDC-mnopqr|str:.len": "1", + "str:chainSpecificTokenIds|nested:str:WUSDC-mnopqr|str:.item|u32:1": "str:USDC-eeeeee", + "str:chainSpecificTokenIds|nested:str:WUSDC-mnopqr|str:.index|nested:str:USDC-eeeeee": "1", + "str:chainSpecificToUniversalMapping|nested:str:USDC-eeeeee": "str:WUSDC-mnopqr", + "str:universalBridgedTokenIds.len": "3", + "str:universalBridgedTokenIds.index|nested:str:WUSDC-abcdef": "1", + "str:universalBridgedTokenIds.item|u32:1": "str:WUSDC-abcdef", + "str:universalBridgedTokenIds.index|nested:str:WUSDC-ghijkl": "2", + "str:universalBridgedTokenIds.item|u32:2": "str:WUSDC-ghijkl", + "str:universalBridgedTokenIds.index|nested:str:WUSDC-mnopqr": "3", + "str:universalBridgedTokenIds.item|u32:3": "str:WUSDC-mnopqr", + "str:tokenLiquidity|nested:str:USDC-aaaaaa": "300000000000000", + "str:tokenLiquidity|nested:str:USDC-cccccc": "100000000000000", + "str:tokenLiquidity|nested:str:USDC-eeeeee": "400000000000000", + "str:tokenLiquidity|nested:str:USDC-ffffff": "400000000000000", + "str:token_decimals_num|nested:str:WUSDC-abcdef": "6", + "str:token_decimals_num|nested:str:WUSDC-ghijkl": "6", + "str:token_decimals_num|nested:str:USDC-aaaaaa": "18", + "str:token_decimals_num|nested:str:USDC-cccccc": "18" + }, + "code": "file:../output/bridged-tokens-wrapper.wasm", + "owner": "address:owner" + } + } + } + ] +} diff --git a/bridged-tokens-wrapper/mandos/unwrap_token.scen.json b/bridged-tokens-wrapper/mandos/unwrap_token.scen.json new file mode 100644 index 00000000..819c3ecd --- /dev/null +++ b/bridged-tokens-wrapper/mandos/unwrap_token.scen.json @@ -0,0 +1,248 @@ +{ + "name": "unwrap token", + "gasSchedule": "v4", + "steps": [ + { + "step": "externalSteps", + "path": "setup.scen.json" + }, + { + "step": "scCall", + "txId": "unwrap-token-unavailable", + "tx": { + "from": "address:user", + "to": "sc:bridged_tokens_wrapper", + "value": "0", + "esdt": { + "tokenIdentifier": "str:WUSDC-abcdef", + "value": "300" + }, + "function": "unwrapToken", + "arguments": [ + "str:USDC-bbbbbb" + ], + "gasLimit": "5,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:Esdt token unavailable", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "unwrap-token-not-enough-funds", + "tx": { + "from": "address:user", + "to": "sc:bridged_tokens_wrapper", + "value": "0", + "esdt": { + "tokenIdentifier": "str:WUSDC-abcdef", + "value": "500" + }, + "function": "unwrapToken", + "arguments": [ + "str:USDC-aaaaaa" + ], + "gasLimit": "5,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:Contract does not have enough funds", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "unwrap-token-requires_updating", + "tx": { + "from": "address:user", + "to": "sc:bridged_tokens_wrapper", + "value": "0", + "esdt": { + "tokenIdentifier": "str:WUSDC-mnopqr", + "value": "500" + }, + "function": "unwrapToken", + "arguments": [ + "str:USDC-eeeeee" + ], + "gasLimit": "5,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:Universal token requires updating", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "chain-token-requires_updating", + "tx": { + "from": "address:user", + "to": "sc:bridged_tokens_wrapper", + "value": "0", + "esdt": { + "tokenIdentifier": "str:WUSDC-ghijkl", + "value": "500" + }, + "function": "unwrapToken", + "arguments": [ + "str:USDC-ffffff" + ], + "gasLimit": "5,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:Chain-specific token requires updating", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "unwrap-token", + "tx": { + "from": "address:user", + "to": "sc:bridged_tokens_wrapper", + "value": "0", + "esdt": { + "tokenIdentifier": "str:WUSDC-abcdef", + "value": "100" + }, + "function": "unwrapToken", + "arguments": [ + "str:USDC-aaaaaa" + ], + "gasLimit": "5,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:owner": { + "nonce": "3", + "balance": "0", + "storage": {} + }, + "address:user": { + "nonce": "12", + "esdt": { + "str:USDC-aaaaaa": { + "balance": "400000000000000" + }, + "str:USDC-bbbbbb": { + "balance": "500000000000000" + }, + "str:USDC-cccccc": { + "balance": "400000000000000" + }, + "str:WUSDC-abcdef": { + "balance": "800" + }, + "str:WUSDC-ghijkl": { + "balance": "900" + }, + "str:WUSDC-mnopqr": { + "balance": "900" + } + }, + "storage": {} + }, + "sc:bridged_tokens_wrapper": { + "nonce": "0", + "esdt": { + "str:WUSDC-abcdef": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:WUSDC-ghijkl": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:WUSDC-mnopqr": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:WUSDC-uvwxyz": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:USDC-aaaaaa": { + "balance": "200000000000000" + }, + "str:USDC-cccccc": { + "balance": "100000000000000" + }, + "str:USDC-ffffff": { + "balance": "400000000000000" + }, + "str:USDC-eeeeee": { + "balance": "400000000000000" + } + }, + "storage": { + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.len": "2", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.index|nested:str:USDC-aaaaaa": "1", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.index|nested:str:USDC-cccccc": "2", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.item|u32:1": "str:USDC-aaaaaa", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.item|u32:2": "str:USDC-cccccc", + "str:chainSpecificToUniversalMapping|nested:str:USDC-aaaaaa": "str:WUSDC-abcdef", + "str:chainSpecificToUniversalMapping|nested:str:USDC-cccccc": "str:WUSDC-abcdef", + "str:chainSpecificTokenIds|nested:str:WUSDC-ghijkl|str:.len": "1", + "str:chainSpecificTokenIds|nested:str:WUSDC-ghijkl|str:.index|nested:str:USDC-ffffff": "1", + "str:chainSpecificTokenIds|nested:str:WUSDC-ghijkl|str:.item|u32:1": "str:USDC-ffffff", + "str:chainSpecificToUniversalMapping|nested:str:USDC-ffffff": "str:WUSDC-ghijkl", + "str:chainSpecificTokenIds|nested:str:WUSDC-mnopqr|str:.len": "1", + "str:chainSpecificTokenIds|nested:str:WUSDC-mnopqr|str:.item|u32:1": "str:USDC-eeeeee", + "str:chainSpecificTokenIds|nested:str:WUSDC-mnopqr|str:.index|nested:str:USDC-eeeeee": "1", + "str:chainSpecificToUniversalMapping|nested:str:USDC-eeeeee": "str:WUSDC-mnopqr", + "str:universalBridgedTokenIds.len": "3", + "str:universalBridgedTokenIds.index|nested:str:WUSDC-abcdef": "1", + "str:universalBridgedTokenIds.item|u32:1": "str:WUSDC-abcdef", + "str:universalBridgedTokenIds.index|nested:str:WUSDC-ghijkl": "2", + "str:universalBridgedTokenIds.item|u32:2": "str:WUSDC-ghijkl", + "str:universalBridgedTokenIds.index|nested:str:WUSDC-mnopqr": "3", + "str:universalBridgedTokenIds.item|u32:3": "str:WUSDC-mnopqr", + "str:tokenLiquidity|nested:str:USDC-aaaaaa": "200000000000000", + "str:tokenLiquidity|nested:str:USDC-cccccc": "100000000000000", + "str:tokenLiquidity|nested:str:USDC-eeeeee": "400000000000000", + "str:tokenLiquidity|nested:str:USDC-ffffff": "400000000000000", + "str:token_decimals_num|nested:str:WUSDC-abcdef": "6", + "str:token_decimals_num|nested:str:WUSDC-ghijkl": "6", + "str:token_decimals_num|nested:str:USDC-aaaaaa": "18", + "str:token_decimals_num|nested:str:USDC-cccccc": "18" + }, + "code": "file:../output/bridged-tokens-wrapper.wasm", + "owner": "address:owner" + } + } + } + ] +} diff --git a/bridged-tokens-wrapper/mandos/whitelist_token.scen.json b/bridged-tokens-wrapper/mandos/whitelist_token.scen.json new file mode 100644 index 00000000..5a05ecaa --- /dev/null +++ b/bridged-tokens-wrapper/mandos/whitelist_token.scen.json @@ -0,0 +1,140 @@ +{ + "name": "whitelist token", + "gasSchedule": "v4", + "steps": [ + { + "step": "externalSteps", + "path": "add_wrapped_token.scen.json" + }, + { + "step": "scCall", + "txId": "whitelist-token-fail", + "tx": { + "from": "address:user", + "to": "sc:bridged_tokens_wrapper", + "function": "whitelistToken", + "arguments": [ + "str:USDC-bbbbbb", + "u32:18", + "str:WUSDC-abcdef" + ], + "gasLimit": "5,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:Endpoint can only be called by owner", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "whitelist-token-a", + "tx": { + "from": "address:owner", + "to": "sc:bridged_tokens_wrapper", + "function": "whitelistToken", + "arguments": [ + "str:USDC-aaaaaa", + "u32:18", + "str:WUSDC-abcdef" + ], + "gasLimit": "5,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "whitelist-token-c", + "tx": { + "from": "address:owner", + "to": "sc:bridged_tokens_wrapper", + "function": "whitelistToken", + "arguments": [ + "str:USDC-cccccc", + "u32:18", + "str:WUSDC-abcdef" + ], + "gasLimit": "5,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:owner": { + "nonce": "3", + "balance": "0", + "storage": {} + }, + "address:user": { + "nonce": "1", + "esdt": { + "str:USDC-aaaaaa": { + "balance": "500000000000000" + }, + "str:USDC-bbbbbb": { + "balance": "500000000000000" + }, + "str:USDC-cccccc": { + "balance": "500000000000000" + }, + "str:WUSDC-abcdef": { + "balance": "500" + } + }, + "storage": {} + }, + "sc:bridged_tokens_wrapper": { + "nonce": "0", + "esdt": { + "str:WUSDC-abcdef": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:WUSDC-uvwxyz": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.len": "2", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.index|nested:str:USDC-aaaaaa": "1", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.index|nested:str:USDC-cccccc": "2", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.item|u32:1": "str:USDC-aaaaaa", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.item|u32:2": "str:USDC-cccccc", + "str:chainSpecificToUniversalMapping|nested:str:USDC-aaaaaa": "str:WUSDC-abcdef", + "str:chainSpecificToUniversalMapping|nested:str:USDC-cccccc": "str:WUSDC-abcdef", + "str:universalBridgedTokenIds.len": "1", + "str:universalBridgedTokenIds.index|nested:str:WUSDC-abcdef": "1", + "str:universalBridgedTokenIds.item|u32:1": "str:WUSDC-abcdef", + "str:token_decimals_num|nested:str:WUSDC-abcdef": "6", + "str:token_decimals_num|nested:str:USDC-aaaaaa": "18", + "str:token_decimals_num|nested:str:USDC-cccccc": "18" + }, + "code": "file:../output/bridged-tokens-wrapper.wasm", + "owner": "address:owner" + } + } + } + ] +} diff --git a/bridged-tokens-wrapper/mandos/wrap_token.scen.json b/bridged-tokens-wrapper/mandos/wrap_token.scen.json new file mode 100644 index 00000000..65a5dfba --- /dev/null +++ b/bridged-tokens-wrapper/mandos/wrap_token.scen.json @@ -0,0 +1,157 @@ +{ + "name": "wrap token", + "gasSchedule": "v4", + "steps": [ + { + "step": "externalSteps", + "path": "whitelist_token.scen.json" + }, + { + "step": "scCall", + "txId": "wrap-token-fail", + "tx": { + "from": "address:user", + "to": "sc:bridged_tokens_wrapper", + "function": "wrapTokens", + "esdt": { + "tokenIdentifier": "str:USDC-bbbbbb", + "value": "300000000000000" + }, + "arguments": [], + "gasLimit": "5,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*", + "out": [ + "nested:str:USDC-bbbbbb|u64:0|biguint:300000000000000" + ] + } + }, + { + "step": "scCall", + "txId": "wrap-token-a", + "tx": { + "from": "address:user", + "to": "sc:bridged_tokens_wrapper", + "function": "wrapTokens", + "esdt": { + "tokenIdentifier": "str:USDC-aaaaaa", + "value": "300000000000000" + }, + "arguments": [], + "gasLimit": "5,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*", + "out": [ + "nested:str:WUSDC-abcdef|u64:0|biguint:300" + ] + } + }, + { + "step": "scCall", + "txId": "wrap-token-c", + "tx": { + "from": "address:user", + "to": "sc:bridged_tokens_wrapper", + "function": "wrapTokens", + "esdt": { + "tokenIdentifier": "str:USDC-cccccc", + "value": "100000000000000" + }, + "arguments": [], + "gasLimit": "5,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*", + "out": [ + "nested:str:WUSDC-abcdef|u64:0|biguint:100" + ] + } + }, + { + "step": "checkState", + "accounts": { + "address:owner": { + "nonce": "3", + "balance": "0", + "storage": {} + }, + "address:user": { + "nonce": "4", + "esdt": { + "str:USDC-aaaaaa": { + "balance": "200000000000000" + }, + "str:USDC-bbbbbb": { + "balance": "500000000000000" + }, + "str:USDC-cccccc": { + "balance": "400000000000000" + }, + "str:WUSDC-abcdef": { + "balance": "900" + } + }, + "storage": {} + }, + "sc:bridged_tokens_wrapper": { + "nonce": "0", + "esdt": { + "str:WUSDC-abcdef": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:WUSDC-uvwxyz": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:USDC-aaaaaa": { + "balance": "300000000000000" + }, + "str:USDC-cccccc": { + "balance": "100000000000000" + } + }, + "storage": { + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.len": "2", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.index|nested:str:USDC-aaaaaa": "1", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.index|nested:str:USDC-cccccc": "2", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.item|u32:1": "str:USDC-aaaaaa", + "str:chainSpecificTokenIds|nested:str:WUSDC-abcdef|str:.item|u32:2": "str:USDC-cccccc", + "str:chainSpecificToUniversalMapping|nested:str:USDC-aaaaaa": "str:WUSDC-abcdef", + "str:chainSpecificToUniversalMapping|nested:str:USDC-cccccc": "str:WUSDC-abcdef", + "str:universalBridgedTokenIds.len": "1", + "str:universalBridgedTokenIds.index|nested:str:WUSDC-abcdef": "1", + "str:universalBridgedTokenIds.item|u32:1": "str:WUSDC-abcdef", + "str:tokenLiquidity|nested:str:USDC-aaaaaa": "300000000000000", + "str:tokenLiquidity|nested:str:USDC-cccccc": "100000000000000", + "str:token_decimals_num|nested:str:WUSDC-abcdef": "6", + "str:token_decimals_num|nested:str:USDC-aaaaaa": "18", + "str:token_decimals_num|nested:str:USDC-cccccc": "18" + }, + "code": "file:../output/bridged-tokens-wrapper.wasm", + "owner": "address:owner" + } + } + } + ] +} diff --git a/bridged-tokens-wrapper/meta/Cargo.toml b/bridged-tokens-wrapper/meta/Cargo.toml new file mode 100644 index 00000000..55bf3058 --- /dev/null +++ b/bridged-tokens-wrapper/meta/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "bridged-tokens-wrapper-meta" +version = "0.0.0" +edition = "2018" +publish = false +[dependencies.bridged-tokens-wrapper] +path = ".." + +[dependencies.multiversx-sc-meta] +version = "0.41.3" diff --git a/bridged-tokens-wrapper/meta/src/main.rs b/bridged-tokens-wrapper/meta/src/main.rs new file mode 100644 index 00000000..ac93d248 --- /dev/null +++ b/bridged-tokens-wrapper/meta/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + multiversx_sc_meta::cli_main::(); +} diff --git a/neversea/elrond.json b/bridged-tokens-wrapper/multiversx.json similarity index 100% rename from neversea/elrond.json rename to bridged-tokens-wrapper/multiversx.json diff --git a/bridged-tokens-wrapper/src/dfp_big_uint.rs b/bridged-tokens-wrapper/src/dfp_big_uint.rs new file mode 100644 index 00000000..a70839f5 --- /dev/null +++ b/bridged-tokens-wrapper/src/dfp_big_uint.rs @@ -0,0 +1,44 @@ +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +#[derive(Clone, PartialEq, Eq)] +pub struct DFPBigUint { + bu: BigUint, + num_decimals: u32, +} + +impl DFPBigUint { + pub fn from_raw(bu: BigUint, num_decimals: u32) -> Self { + DFPBigUint { bu, num_decimals } + } + + pub fn convert(self, decimals: u32) -> Self { + if self.num_decimals == decimals { + return self; + } + + let new_bu = if self.num_decimals > decimals { + let diff_decimals = self.num_decimals - decimals; + self.bu / 10u64.pow(diff_decimals) + } else { + let diff_decimals = decimals - self.num_decimals; + self.bu * 10u64.pow(diff_decimals) + }; + + DFPBigUint { + bu: new_bu, + num_decimals: decimals, + } + } + + pub fn trunc(&self) -> Self { + DFPBigUint { + bu: self.bu.clone() / 10u64.pow(self.num_decimals), + num_decimals: 1, + } + } + + pub fn to_raw(&self) -> BigUint { + self.bu.clone() + } +} diff --git a/bridged-tokens-wrapper/src/lib.rs b/bridged-tokens-wrapper/src/lib.rs new file mode 100644 index 00000000..f18f28a9 --- /dev/null +++ b/bridged-tokens-wrapper/src/lib.rs @@ -0,0 +1,286 @@ +#![no_std] + +mod dfp_big_uint; +use core::ops::Deref; + +pub use dfp_big_uint::DFPBigUint; +use transaction::PaymentsVec; + +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +impl DFPBigUint {} + +#[multiversx_sc::contract] +pub trait BridgedTokensWrapper: multiversx_sc_modules::pause::PauseModule { + #[init] + fn init(&self) { + self.set_paused(true); + } + + #[only_owner] + #[endpoint(addWrappedToken)] + fn add_wrapped_token(&self, universal_bridged_token_ids: TokenIdentifier, num_decimals: u32) { + self.require_mint_and_burn_roles(&universal_bridged_token_ids); + self.token_decimals_num(&universal_bridged_token_ids) + .set(num_decimals); + self.universal_bridged_token_ids() + .insert(universal_bridged_token_ids); + } + + #[only_owner] + #[endpoint(updateWrappedToken)] + fn update_wrapped_token( + &self, + universal_bridged_token_ids: TokenIdentifier, + num_decimals: u32, + ) { + require!( + self.universal_bridged_token_ids() + .contains(&universal_bridged_token_ids), + "Universal token was not added yet" + ); + self.token_decimals_num(&universal_bridged_token_ids) + .set(num_decimals); + } + + #[only_owner] + #[endpoint(removeWrappedToken)] + fn remove_wrapped_token(&self, universal_bridged_token_ids: TokenIdentifier) { + let _ = self + .universal_bridged_token_ids() + .swap_remove(&universal_bridged_token_ids); + + let mut chain_specific_tokens = self.chain_specific_token_ids(&universal_bridged_token_ids); + for token in chain_specific_tokens.iter() { + self.chain_specific_to_universal_mapping(&token).clear(); + self.token_decimals_num(&token).clear(); + } + + chain_specific_tokens.clear(); + self.token_decimals_num(&universal_bridged_token_ids) + .clear(); + } + + #[only_owner] + #[endpoint(whitelistToken)] + fn whitelist_token( + &self, + chain_specific_token_id: TokenIdentifier, + chain_specific_token_decimals: u32, + universal_bridged_token_ids: TokenIdentifier, + ) { + self.require_mint_and_burn_roles(&universal_bridged_token_ids); + + let chain_to_universal_mapper = + self.chain_specific_to_universal_mapping(&chain_specific_token_id); + require!( + chain_to_universal_mapper.is_empty(), + "Chain-specific token is already mapped to another universal token" + ); + + self.token_decimals_num(&chain_specific_token_id) + .set(chain_specific_token_decimals); + + chain_to_universal_mapper.set(&universal_bridged_token_ids); + + let _ = self + .chain_specific_token_ids(&universal_bridged_token_ids) + .insert(chain_specific_token_id); + + self.universal_bridged_token_ids() + .insert(universal_bridged_token_ids); + } + + #[only_owner] + #[endpoint(updateWhitelistedToken)] + fn update_whitelisted_token( + &self, + chain_specific_token_id: TokenIdentifier, + chain_specific_token_decimals: u32, + ) { + let chain_to_universal_mapper = + self.chain_specific_to_universal_mapping(&chain_specific_token_id); + require!( + !chain_to_universal_mapper.is_empty(), + "Chain-specific token was not whitelisted yet" + ); + + self.token_decimals_num(&chain_specific_token_id) + .set(chain_specific_token_decimals); + } + + #[only_owner] + #[endpoint(blacklistToken)] + fn blacklist_token(&self, chain_specific_token_id: TokenIdentifier) { + let chain_to_universal_mapper = + self.chain_specific_to_universal_mapping(&chain_specific_token_id); + + let universal_bridged_token_ids = chain_to_universal_mapper.get(); + + let _ = self + .chain_specific_token_ids(&universal_bridged_token_ids) + .swap_remove(&chain_specific_token_id); + + chain_to_universal_mapper.clear(); + self.token_decimals_num(&chain_specific_token_id).clear(); + } + + #[payable("*")] + #[endpoint(depositLiquidity)] + fn deposit_liquidity(&self) { + let (payment_token, payment_amount) = self.call_value().single_fungible_esdt(); + self.token_liquidity(&payment_token) + .update(|liq| *liq += payment_amount); + } + + /// Will wrap what it can, and send back the rest unchanged + #[payable("*")] + #[endpoint(wrapTokens)] + fn wrap_tokens(&self) -> PaymentsVec { + require!(self.not_paused(), "Contract is paused"); + let original_payments = self.call_value().all_esdt_transfers().deref().clone(); + if original_payments.is_empty() { + return original_payments; + } + + let mut new_payments = ManagedVec::new(); + + for payment in &original_payments { + let universal_token_id_mapper = + self.chain_specific_to_universal_mapping(&payment.token_identifier); + + // if there is chain specific -> universal mapping, then the token is whitelisted + if universal_token_id_mapper.is_empty() { + new_payments.push(payment); + continue; + } + let universal_token_id = universal_token_id_mapper.get(); + self.require_tokens_have_set_decimals_num( + &universal_token_id, + &payment.token_identifier, + ); + self.token_liquidity(&payment.token_identifier) + .update(|value| *value += &payment.amount); + let converted_amount = self.get_converted_amount( + &payment.token_identifier, + &universal_token_id, + payment.amount, + ); + + self.send() + .esdt_local_mint(&universal_token_id, 0, &converted_amount); + new_payments.push(EsdtTokenPayment::new( + universal_token_id, + 0, + converted_amount, + )); + } + + let caller = self.blockchain().get_caller(); + self.send().direct_multi(&caller, &new_payments); + + new_payments + } + + #[payable("*")] + #[endpoint(unwrapToken)] + fn unwrap_token(&self, requested_token: TokenIdentifier) { + require!(self.not_paused(), "Contract is paused"); + let (payment_token, payment_amount) = self.call_value().single_fungible_esdt(); + require!(payment_amount > 0u32, "Must pay more than 0 tokens!"); + + let universal_bridged_token_ids = self + .chain_specific_to_universal_mapping(&requested_token) + .get(); + require!( + payment_token == universal_bridged_token_ids, + "Esdt token unavailable" + ); + self.require_tokens_have_set_decimals_num(&payment_token, &requested_token); + + let chain_specific_token_id = &requested_token; + let converted_amount = self.get_converted_amount( + &payment_token, + chain_specific_token_id, + payment_amount.clone(), + ); + + self.token_liquidity(chain_specific_token_id).update(|liq| { + require!( + converted_amount <= *liq, + "Contract does not have enough funds" + ); + + *liq -= &converted_amount; + }); + + self.send() + .esdt_local_burn(&universal_bridged_token_ids, 0, &payment_amount); + + let caller = self.blockchain().get_caller(); + self.send() + .direct_esdt(&caller, chain_specific_token_id, 0, &converted_amount); + } + + fn get_converted_amount( + &self, + from: &TokenIdentifier, + to: &TokenIdentifier, + amount: BigUint, + ) -> BigUint { + let from_decimals = self.token_decimals_num(from).get(); + let to_decimals = self.token_decimals_num(to).get(); + let converted_amount = DFPBigUint::from_raw(amount, from_decimals); + converted_amount.convert(to_decimals).to_raw() + } + + fn require_mint_and_burn_roles(&self, token_id: &TokenIdentifier) { + let roles = self.blockchain().get_esdt_local_roles(token_id); + + require!( + roles.has_role(&EsdtLocalRole::Mint) && roles.has_role(&EsdtLocalRole::Burn), + "Must set local role first" + ); + } + + fn require_tokens_have_set_decimals_num( + &self, + universal_token: &TokenIdentifier, + chain_token: &TokenIdentifier, + ) { + require!( + !self.token_decimals_num(universal_token).is_empty(), + "Universal token requires updating" + ); + require!( + !self.token_decimals_num(chain_token).is_empty(), + "Chain-specific token requires updating" + ); + } + + #[view(getUniversalBridgedTokenIds)] + #[storage_mapper("universalBridgedTokenIds")] + fn universal_bridged_token_ids(&self) -> UnorderedSetMapper; + + #[view(getTokenLiquidity)] + #[storage_mapper("tokenLiquidity")] + fn token_liquidity(&self, token: &TokenIdentifier) -> SingleValueMapper; + + #[view(getChainSpecificToUniversalMapping)] + #[storage_mapper("chainSpecificToUniversalMapping")] + fn chain_specific_to_universal_mapping( + &self, + token: &TokenIdentifier, + ) -> SingleValueMapper; + + #[view(getchainSpecificTokenIds)] + #[storage_mapper("chainSpecificTokenIds")] + fn chain_specific_token_ids( + &self, + universal_token_id: &TokenIdentifier, + ) -> UnorderedSetMapper; + + #[storage_mapper("token_decimals_num")] + fn token_decimals_num(&self, token: &TokenIdentifier) -> SingleValueMapper; +} diff --git a/neversea/testnet.toml b/bridged-tokens-wrapper/testnet.toml similarity index 100% rename from neversea/testnet.toml rename to bridged-tokens-wrapper/testnet.toml diff --git a/bridged-tokens-wrapper/tests/dfp_big_uint_test.rs b/bridged-tokens-wrapper/tests/dfp_big_uint_test.rs new file mode 100644 index 00000000..31a2c9b5 --- /dev/null +++ b/bridged-tokens-wrapper/tests/dfp_big_uint_test.rs @@ -0,0 +1,24 @@ +#![feature(associated_type_bounds)] +use bridged_tokens_wrapper::DFPBigUint; +use multiversx_sc_scenario::DebugApi; + +#[test] +fn test_biguint() { + let _ = DebugApi::dummy(); + let raw = 123456u64; + let dfp = DFPBigUint::::from_raw(raw.into(), 6); + let converted = dfp.clone().convert(9); + assert!(dfp.trunc() == converted.trunc()); + assert!(converted.clone().convert(9).to_raw() == 123456000u64); + assert!(converted.clone().convert(1).to_raw() == 1u64); + assert!(converted.clone().convert(3).to_raw() == 123u64); + assert!(converted.clone().convert(5).to_raw() == 12345u64); +} + +#[test] +fn test_mandos_scenario_values() { + let _ = DebugApi::dummy(); + let raw = 300000000000000u64; + let dfp = DFPBigUint::::from_raw(raw.into(), 18); + assert!(dfp.convert(6).to_raw() == 300u64); +} diff --git a/bridged-tokens-wrapper/tests/scenario_go_test.rs b/bridged-tokens-wrapper/tests/scenario_go_test.rs new file mode 100644 index 00000000..4bbc4b02 --- /dev/null +++ b/bridged-tokens-wrapper/tests/scenario_go_test.rs @@ -0,0 +1,29 @@ +#[test] +fn unwrap_token_go() { + multiversx_sc_scenario::run_go("mandos/unwrap_token.scen.json"); +} + +#[test] +fn wrap_token_go() { + multiversx_sc_scenario::run_go("mandos/wrap_token.scen.json"); +} + +#[test] +fn whitelist_token_go() { + multiversx_sc_scenario::run_go("mandos/whitelist_token.scen.json"); +} + +#[test] +fn blacklist_token_go() { + multiversx_sc_scenario::run_go("mandos/blacklist_token.scen.json"); +} + +#[test] +fn add_wrapped_token_go() { + multiversx_sc_scenario::run_go("mandos/add_wrapped_token.scen.json"); +} + +#[test] +fn remove_wrapped_token_go() { + multiversx_sc_scenario::run_go("mandos/remove_wrapped_token.scen.json"); +} diff --git a/bridged-tokens-wrapper/wasm/Cargo.lock b/bridged-tokens-wrapper/wasm/Cargo.lock new file mode 100644 index 00000000..f3a582a2 --- /dev/null +++ b/bridged-tokens-wrapper/wasm/Cargo.lock @@ -0,0 +1,299 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", + "version_check", +] + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bridged-tokens-wrapper" +version = "0.0.0" +dependencies = [ + "multiversx-sc", + "multiversx-sc-modules", + "transaction", +] + +[[package]] +name = "bridged-tokens-wrapper-wasm" +version = "0.0.0" +dependencies = [ + "bridged-tokens-wrapper", + "multiversx-sc-wasm-adapter", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + +[[package]] +name = "eth-address" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "multiversx-sc" +version = "0.39.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31561133b64837f1a7835ae4ba96383ab07d9ce401e703ad2a37aef45789a1e7" +dependencies = [ + "bitflags", + "hashbrown", + "hex-literal", + "multiversx-sc-codec", + "multiversx-sc-derive", + "num-traits", +] + +[[package]] +name = "multiversx-sc-codec" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7638cb46a0e99c636fd55443ac534ff0a5fad0bd772e1037fbac9a75e04c3c9" +dependencies = [ + "arrayvec", + "multiversx-sc-codec-derive", + "wee_alloc", +] + +[[package]] +name = "multiversx-sc-codec-derive" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e976002d51367f16140929c10ee695f95dd8d34c150a45db60d3fcd1328a267a" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "multiversx-sc-derive" +version = "0.39.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23187464277575f8055c92cffbd664592825591f5cebb8dae038d2af04c5e639" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "radix_trie", + "syn", +] + +[[package]] +name = "multiversx-sc-modules" +version = "0.39.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbcb0b06f37396484f01d89295091db401b8b515ab781e3580a6bb333e912591" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "multiversx-sc-wasm-adapter" +version = "0.39.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af5fee54f1498ec8181593cd54a96ef7436543ad78848e2e805b9b62c27c4095" +dependencies = [ + "multiversx-sc", + "wee_alloc", +] + +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "proc-macro2" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "transaction" +version = "0.0.0" +dependencies = [ + "eth-address", + "multiversx-sc", +] + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/bridged-tokens-wrapper/wasm/Cargo.toml b/bridged-tokens-wrapper/wasm/Cargo.toml new file mode 100644 index 00000000..05ea6ac7 --- /dev/null +++ b/bridged-tokens-wrapper/wasm/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "bridged-tokens-wrapper-wasm" +version = "0.0.0" +authors = ["Alin Cruceat "] +edition = "2018" +publish = false + +[lib] +crate-type = ["cdylib"] +[profile.release] +codegen-units = 1 +opt-level = "z" +lto = true +debug = false +panic = "abort" +[dependencies.bridged-tokens-wrapper] +path = ".." + +[dependencies.multiversx-sc-wasm-adapter] +version = "0.41.3" + +[workspace] +members = ["."] diff --git a/bridged-tokens-wrapper/wasm/src/lib.rs b/bridged-tokens-wrapper/wasm/src/lib.rs new file mode 100644 index 00000000..ca27291f --- /dev/null +++ b/bridged-tokens-wrapper/wasm/src/lib.rs @@ -0,0 +1,40 @@ +// Code generated by the multiversx-sc multi-contract system. DO NOT EDIT. + +//////////////////////////////////////////////////// +////////////////// AUTO-GENERATED ////////////////// +//////////////////////////////////////////////////// + +// Init: 1 +// Endpoints: 16 +// Async Callback (empty): 1 +// Total number of exported functions: 18 + +#![no_std] +#![feature(alloc_error_handler, lang_items)] + +multiversx_sc_wasm_adapter::allocator!(); +multiversx_sc_wasm_adapter::panic_handler!(); + +multiversx_sc_wasm_adapter::endpoints! { + bridged_tokens_wrapper + ( + addWrappedToken + updateWrappedToken + removeWrappedToken + whitelistToken + updateWhitelistedToken + blacklistToken + depositLiquidity + wrapTokens + unwrapToken + getUniversalBridgedTokenIds + getTokenLiquidity + getChainSpecificToUniversalMapping + getchainSpecificTokenIds + pause + unpause + isPaused + ) +} + +multiversx_sc_wasm_adapter::empty_callback! {} diff --git a/common/eth-address/Cargo.toml b/common/eth-address/Cargo.toml new file mode 100644 index 00000000..b0f24f38 --- /dev/null +++ b/common/eth-address/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "eth-address" +version = "0.0.0" +authors = ["dorin-iancu "] +edition = "2018" + +[lib] +path = "src/lib.rs" +[dependencies.multiversx-sc] +version = "0.41.3" diff --git a/common/eth-address/src/lib.rs b/common/eth-address/src/lib.rs new file mode 100644 index 00000000..0e4e6a53 --- /dev/null +++ b/common/eth-address/src/lib.rs @@ -0,0 +1,27 @@ +#![no_std] + +multiversx_sc::derive_imports!(); +use multiversx_sc::{ + api::ManagedTypeApi, + types::{ManagedBuffer, ManagedByteArray}, +}; + +pub const ETH_ADDRESS_LEN: usize = 20; + +/// Wrapper over a 20-byte array +#[derive(TypeAbi, TopEncode, TopDecode, NestedEncode, NestedDecode, Clone, ManagedVecItem)] +pub struct EthAddress { + pub raw_addr: ManagedByteArray, +} + +impl EthAddress { + pub fn zero() -> Self { + Self { + raw_addr: ManagedByteArray::new_from_bytes(&[0u8; ETH_ADDRESS_LEN]), + } + } + + pub fn as_managed_buffer(&self) -> &ManagedBuffer { + self.raw_addr.as_managed_buffer() + } +} diff --git a/common/fee-estimator-module/Cargo.toml b/common/fee-estimator-module/Cargo.toml new file mode 100644 index 00000000..5ae1347c --- /dev/null +++ b/common/fee-estimator-module/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "fee-estimator-module" +version = "0.0.0" +authors = ["dorin-iancu "] +edition = "2018" +[dependencies.multiversx-sc] +version = "0.41.3" +[dev-dependencies.multiversx-sc-scenario] +version = "0.41.3" diff --git a/common/fee-estimator-module/src/aggregator_proxy.rs b/common/fee-estimator-module/src/aggregator_proxy.rs new file mode 100644 index 00000000..123254ae --- /dev/null +++ b/common/fee-estimator-module/src/aggregator_proxy.rs @@ -0,0 +1,38 @@ +multiversx_sc::imports!(); + +pub const GWEI_STRING: &[u8] = b"GWEI"; + +pub type AggregatorResultAsMultiValue = + MultiValue5, ManagedBuffer, BigUint, u8>; + +#[multiversx_sc::proxy] +pub trait Aggregator { + #[view(latestPriceFeedOptional)] + fn latest_price_feed_optional( + &self, + from: ManagedBuffer, + to: ManagedBuffer, + ) -> OptionalValue>; +} + +pub struct AggregatorResult { + pub round_id: u32, + pub from_token_name: ManagedBuffer, + pub to_token_name: ManagedBuffer, + pub price: BigUint, + pub decimals: u8, +} + +impl From> for AggregatorResult { + fn from(multi_result: AggregatorResultAsMultiValue) -> Self { + let (round_id, from_token_name, to_token_name, price, decimals) = multi_result.into_tuple(); + + AggregatorResult { + round_id, + from_token_name, + to_token_name, + price, + decimals, + } + } +} diff --git a/common/fee-estimator-module/src/lib.rs b/common/fee-estimator-module/src/lib.rs new file mode 100644 index 00000000..3504543a --- /dev/null +++ b/common/fee-estimator-module/src/lib.rs @@ -0,0 +1,101 @@ +#![no_std] + +multiversx_sc::imports!(); + +mod aggregator_proxy; +pub use aggregator_proxy::*; + +#[multiversx_sc::module] +pub trait FeeEstimatorModule { + #[only_owner] + #[endpoint(setFeeEstimatorContractAddress)] + fn set_fee_estimator_contract_address(&self, new_address: ManagedAddress) { + self.fee_estimator_contract_address().set(&new_address); + } + + #[only_owner] + #[endpoint(setEthTxGasLimit)] + fn set_eth_tx_gas_limit(&self, new_limit: BigUint) { + self.eth_tx_gas_limit().set(&new_limit); + } + + /// Default price being used if the aggregator lacks a mapping for this token + /// or the aggregator address is not set + #[only_owner] + #[endpoint(setDefaultPricePerGasUnit)] + fn set_default_price_per_gas_unit( + &self, + token_id: TokenIdentifier, + default_price_per_gas_unit: BigUint, + ) { + self.default_price_per_gas_unit(&token_id) + .set(&default_price_per_gas_unit); + } + + /// Token ticker being used when querying the aggregator for GWEI prices + #[only_owner] + #[endpoint(setTokenTicker)] + fn set_token_ticker(&self, token_id: TokenIdentifier, ticker: ManagedBuffer) { + self.token_ticker(&token_id).set(&ticker); + } + + /// Returns the fee for the given token ID (the fee amount is in the given token) + #[view(calculateRequiredFee)] + fn calculate_required_fee(&self, token_id: &TokenIdentifier) -> BigUint { + let price_per_gas_unit = self.get_price_per_gas_unit(token_id); + let gas_limit = self.eth_tx_gas_limit().get(); + + price_per_gas_unit * gas_limit + } + + fn get_price_per_gas_unit(&self, token_id: &TokenIdentifier) -> BigUint { + let opt_price = self.get_aggregator_mapping(&TokenIdentifier::from(GWEI_STRING), token_id); + + opt_price.unwrap_or_else(|| self.default_price_per_gas_unit(token_id).get()) + } + + fn get_aggregator_mapping( + &self, + from: &TokenIdentifier, + to: &TokenIdentifier, + ) -> Option { + let fee_estimator_sc_address = self.fee_estimator_contract_address().get(); + if fee_estimator_sc_address.is_zero() { + return None; + } + + let from_ticker = self.token_ticker(from).get(); + let to_ticker = self.token_ticker(to).get(); + + let result: OptionalValue> = self + .aggregator_proxy(fee_estimator_sc_address) + .latest_price_feed_optional(from_ticker, to_ticker) + .execute_on_dest_context(); + + result + .into_option() + .map(|multi_result| AggregatorResult::from(multi_result).price) + } + + // proxies + + #[proxy] + fn aggregator_proxy(&self, sc_address: ManagedAddress) -> aggregator_proxy::Proxy; + + // storage + + #[view(getFeeEstimatorContractAddress)] + #[storage_mapper("feeEstimatorContractAddress")] + fn fee_estimator_contract_address(&self) -> SingleValueMapper; + + #[view(getDefaultPricePerGasUnit)] + #[storage_mapper("defaultPricePerGasUnit")] + fn default_price_per_gas_unit(&self, token_id: &TokenIdentifier) -> SingleValueMapper; + + #[storage_mapper("tokenTicker")] + fn token_ticker(&self, token_id: &TokenIdentifier) -> SingleValueMapper; + + #[view(getEthTxGasLimit)] + #[storage_mapper("ethTxGasLimit")] + fn eth_tx_gas_limit(&self) -> SingleValueMapper; +} diff --git a/common/max-bridged-amount-module/Cargo.toml b/common/max-bridged-amount-module/Cargo.toml new file mode 100644 index 00000000..8c9527b9 --- /dev/null +++ b/common/max-bridged-amount-module/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "max-bridged-amount-module" +version = "0.0.0" +authors = ["dorin-iancu "] +edition = "2018" +[dependencies.multiversx-sc] +version = "0.41.3" +[dev-dependencies.multiversx-sc-scenario] +version = "0.41.3" diff --git a/common/max-bridged-amount-module/src/lib.rs b/common/max-bridged-amount-module/src/lib.rs new file mode 100644 index 00000000..89ca7afb --- /dev/null +++ b/common/max-bridged-amount-module/src/lib.rs @@ -0,0 +1,32 @@ +#![no_std] + +multiversx_sc::imports!(); + +#[multiversx_sc::module] +pub trait MaxBridgedAmountModule { + #[only_owner] + #[endpoint(setMaxBridgedAmount)] + fn set_max_bridged_amount(&self, token_id: TokenIdentifier, max_amount: BigUint) { + self.max_bridged_amount(&token_id).set(&max_amount); + } + + fn is_above_max_amount(&self, token_id: &TokenIdentifier, amount: &BigUint) -> bool { + let max_amount = self.max_bridged_amount(token_id).get(); + if max_amount > 0 { + amount > &max_amount + } else { + false + } + } + + fn require_below_max_amount(&self, token_id: &TokenIdentifier, amount: &BigUint) { + require!( + !self.is_above_max_amount(token_id, amount), + "Deposit over max amount" + ); + } + + #[view(getMaxBridgedAmount)] + #[storage_mapper("maxBridgedAmount")] + fn max_bridged_amount(&self, token_id: &TokenIdentifier) -> SingleValueMapper; +} diff --git a/common/token-module/Cargo.toml b/common/token-module/Cargo.toml new file mode 100644 index 00000000..04386c99 --- /dev/null +++ b/common/token-module/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "token-module" +version = "0.0.0" +authors = ["dorin-iancu "] +edition = "2018" +[dependencies.fee-estimator-module] +path = "../fee-estimator-module" + +[dependencies.multiversx-sc] +version = "0.41.3" +[dev-dependencies.multiversx-sc-scenario] +version = "0.41.3" diff --git a/common/token-module/src/lib.rs b/common/token-module/src/lib.rs new file mode 100644 index 00000000..b1b5e3f3 --- /dev/null +++ b/common/token-module/src/lib.rs @@ -0,0 +1,125 @@ +#![no_std] + +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +pub const PERCENTAGE_TOTAL: u32 = 10_000; // precision of 2 decimals +pub static INVALID_PERCENTAGE_SUM_OVER_ERR_MSG: &[u8] = b"Percentages do not add up to 100%"; + +#[derive(NestedEncode, NestedDecode, TypeAbi, ManagedVecItem, Clone)] +pub struct AddressPercentagePair { + pub address: ManagedAddress, + pub percentage: u32, +} + +#[multiversx_sc::module] +pub trait TokenModule: fee_estimator_module::FeeEstimatorModule { + // endpoints - owner-only + + /// Distributes the accumulated fees to the given addresses. + /// Expected arguments are pairs of (address, percentage), + /// where percentages must add up to the PERCENTAGE_TOTAL constant + #[only_owner] + #[endpoint(distributeFees)] + fn distribute_fees( + &self, + address_percentage_pairs: ManagedVec>, + ) { + let percentage_total = BigUint::from(PERCENTAGE_TOTAL); + + let mut percentage_sum = 0u64; + for pair in &address_percentage_pairs { + percentage_sum += pair.percentage as u64; + } + require!( + percentage_sum == PERCENTAGE_TOTAL as u64, + INVALID_PERCENTAGE_SUM_OVER_ERR_MSG + ); + + for token_id in self.token_whitelist().iter() { + let accumulated_fees = self.accumulated_transaction_fees(&token_id).get(); + if accumulated_fees == 0u32 { + continue; + } + + let mut remaining_fees = accumulated_fees.clone(); + + for pair in &address_percentage_pairs { + let amount_to_send = + &(&accumulated_fees * &BigUint::from(pair.percentage)) / &percentage_total; + + if amount_to_send > 0 { + remaining_fees -= &amount_to_send; + + self.send() + .direct_esdt(&pair.address, &token_id, 0, &amount_to_send); + } + } + + self.accumulated_transaction_fees(&token_id) + .set(&remaining_fees); + } + } + + #[only_owner] + #[endpoint(addTokenToWhitelist)] + fn add_token_to_whitelist( + &self, + token_id: TokenIdentifier, + ticker: ManagedBuffer, + opt_default_price_per_gas_unit: OptionalValue, + ) { + self.token_ticker(&token_id).set(&ticker); + + if let OptionalValue::Some(default_price_per_gas_unit) = opt_default_price_per_gas_unit { + self.default_price_per_gas_unit(&token_id) + .set(&default_price_per_gas_unit); + } + + let _ = self.token_whitelist().insert(token_id); + } + + #[only_owner] + #[endpoint(removeTokenFromWhitelist)] + fn remove_token_from_whitelist(&self, token_id: TokenIdentifier) { + self.token_ticker(&token_id).clear(); + self.default_price_per_gas_unit(&token_id).clear(); + + let _ = self.token_whitelist().swap_remove(&token_id); + } + + // private + + fn require_token_in_whitelist(&self, token_id: &TokenIdentifier) { + require!( + self.token_whitelist().contains(token_id), + "Token not in whitelist" + ); + } + + fn require_local_role_set(&self, token_id: &TokenIdentifier, role: &EsdtLocalRole) { + require!( + self.is_local_role_set(token_id, role), + "Must set local role first" + ); + } + + fn is_local_role_set(&self, token_id: &TokenIdentifier, role: &EsdtLocalRole) -> bool { + let roles = self.blockchain().get_esdt_local_roles(token_id); + + roles.has_role(role) + } + + // storage + + #[view(getAllKnownTokens)] + #[storage_mapper("tokenWhitelist")] + fn token_whitelist(&self) -> UnorderedSetMapper; + + #[view(getAccumulatedTransactionFees)] + #[storage_mapper("accumulatedTransactionFees")] + fn accumulated_transaction_fees( + &self, + token_id: &TokenIdentifier, + ) -> SingleValueMapper; +} diff --git a/common/transaction/Cargo.toml b/common/transaction/Cargo.toml new file mode 100644 index 00000000..a3c78a10 --- /dev/null +++ b/common/transaction/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "transaction" +version = "0.0.0" +authors = ["dorin-iancu "] +edition = "2018" + +[lib] +path = "src/lib.rs" +[dependencies.eth-address] +path = "../eth-address" + +[dependencies.multiversx-sc] +version = "0.41.3" diff --git a/common/transaction/src/lib.rs b/common/transaction/src/lib.rs new file mode 100644 index 00000000..c2050a82 --- /dev/null +++ b/common/transaction/src/lib.rs @@ -0,0 +1,81 @@ +#![no_std] + +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +use eth_address::EthAddress; + +pub mod transaction_status; + +// revert protection +pub const MIN_BLOCKS_FOR_FINALITY: u64 = 10; +pub const TX_MULTIRESULT_NR_FIELDS: usize = 6; + +pub type TxNonce = u64; +pub type BlockNonce = u64; +pub type SenderAddressRaw = ManagedBuffer; +pub type ReceiverAddressRaw = ManagedBuffer; +pub type TxAsMultiValue = MultiValue6< + BlockNonce, + TxNonce, + SenderAddressRaw, + ReceiverAddressRaw, + TokenIdentifier, + BigUint, +>; +pub type PaymentsVec = ManagedVec>; +pub type TxBatchSplitInFields = MultiValue2>>; + +#[derive(TopEncode, TopDecode, NestedEncode, NestedDecode, TypeAbi, ManagedVecItem, Clone)] +pub struct EthTransaction { + pub from: EthAddress, + pub to: ManagedAddress, + pub token_id: TokenIdentifier, + pub amount: BigUint, + pub tx_nonce: TxNonce, +} + +pub type EthTxAsMultiValue = + MultiValue5, ManagedAddress, TokenIdentifier, BigUint, TxNonce>; + +#[derive(TopEncode, TopDecode, NestedEncode, NestedDecode, TypeAbi, ManagedVecItem, Clone)] +pub struct Transaction { + pub block_nonce: BlockNonce, + pub nonce: TxNonce, + pub from: ManagedBuffer, + pub to: ManagedBuffer, + pub token_identifier: TokenIdentifier, + pub amount: BigUint, + pub is_refund_tx: bool, +} + +impl From> for Transaction { + fn from(tx_as_multiresult: TxAsMultiValue) -> Self { + let (block_nonce, nonce, from, to, token_identifier, amount) = + tx_as_multiresult.into_tuple(); + + Transaction { + block_nonce, + nonce, + from, + to, + token_identifier, + amount, + is_refund_tx: false, + } + } +} + +impl Transaction { + pub fn into_multiresult(self) -> TxAsMultiValue { + ( + self.block_nonce, + self.nonce, + self.from, + self.to, + self.token_identifier, + self.amount, + ) + .into() + } +} diff --git a/common/transaction/src/transaction_status.rs b/common/transaction/src/transaction_status.rs new file mode 100644 index 00000000..467477dc --- /dev/null +++ b/common/transaction/src/transaction_status.rs @@ -0,0 +1,21 @@ +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +#[derive( + TopEncode, + TopDecode, + NestedEncode, + NestedDecode, + TypeAbi, + PartialEq, + Clone, + Copy, + ManagedVecItem, +)] +pub enum TransactionStatus { + None, + Pending, + InProgress, + Executed, + Rejected, +} diff --git a/common/tx-batch-module/Cargo.toml b/common/tx-batch-module/Cargo.toml new file mode 100644 index 00000000..45b50869 --- /dev/null +++ b/common/tx-batch-module/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "tx-batch-module" +version = "0.0.0" +authors = ["dorin-iancu "] +edition = "2018" +[dependencies.multiversx-sc] +version = "0.41.3" + +[dependencies.transaction] +path = "../transaction" +[dev-dependencies.multiversx-sc-scenario] +version = "0.41.3" diff --git a/common/tx-batch-module/src/batch_status.rs b/common/tx-batch-module/src/batch_status.rs new file mode 100644 index 00000000..cdf6f902 --- /dev/null +++ b/common/tx-batch-module/src/batch_status.rs @@ -0,0 +1,16 @@ +multiversx_sc::derive_imports!(); + +use multiversx_sc::{api::ManagedTypeApi, types::ManagedVec}; +use transaction::{BlockNonce, TxNonce}; + +#[derive(TopEncode, TopDecode, TypeAbi)] +pub enum BatchStatus { + AlreadyProcessed, + Empty, + PartiallyFull { + end_block_nonce: BlockNonce, + tx_ids: ManagedVec, + }, + Full, + WaitingForSignatures, +} diff --git a/common/tx-batch-module/src/lib.rs b/common/tx-batch-module/src/lib.rs new file mode 100644 index 00000000..545a4cca --- /dev/null +++ b/common/tx-batch-module/src/lib.rs @@ -0,0 +1,275 @@ +#![no_std] + +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +pub use batch_status::BatchStatus; +use transaction::{Transaction, TxBatchSplitInFields, MIN_BLOCKS_FOR_FINALITY}; +use tx_batch_mapper::TxBatchMapper; + +pub mod batch_status; +pub mod tx_batch_mapper; + +#[multiversx_sc::module] +pub trait TxBatchModule { + // endpoints - owner-only + + #[only_owner] + #[endpoint(setMaxTxBatchSize)] + fn set_max_tx_batch_size(&self, new_max_tx_batch_size: usize) { + require!( + new_max_tx_batch_size > 0, + "Max tx batch size must be more than 0" + ); + + self.max_tx_batch_size().set(new_max_tx_batch_size); + } + + #[only_owner] + #[endpoint(setMaxTxBatchBlockDuration)] + fn set_max_tx_batch_block_duration(&self, new_max_tx_batch_block_duration: u64) { + require!( + new_max_tx_batch_block_duration > 0, + "Max tx batch block duration must be more than 0" + ); + + self.max_tx_batch_block_duration() + .set(new_max_tx_batch_block_duration); + } + + // views + + #[view(getCurrentTxBatch)] + fn get_current_tx_batch(&self) -> OptionalValue> { + let first_batch_id = self.first_batch_id().get(); + let first_batch = self.pending_batches(first_batch_id); + + if self.is_batch_full(&first_batch, first_batch_id, first_batch_id) + && self.is_batch_final(&first_batch) + { + let mut result_vec = MultiValueEncoded::new(); + for tx in first_batch.iter() { + result_vec.push(tx.into_multiresult()); + } + + return OptionalValue::Some((first_batch_id, result_vec).into()); + } + + OptionalValue::None + } + + #[view(getFirstBatchAnyStatus)] + fn get_first_batch_any_status(&self) -> OptionalValue> { + let first_batch_id = self.first_batch_id().get(); + self.get_batch(first_batch_id) + } + + #[view(getBatch)] + fn get_batch(&self, batch_id: u64) -> OptionalValue> { + let tx_batch = self.pending_batches(batch_id); + if tx_batch.is_empty() { + return OptionalValue::None; + } + + let mut result_vec = MultiValueEncoded::new(); + for tx in tx_batch.iter() { + result_vec.push(tx.into_multiresult()); + } + + OptionalValue::Some((batch_id, result_vec).into()) + } + + #[view(getBatchStatus)] + fn get_batch_status(&self, batch_id: u64) -> BatchStatus { + let first_batch_id = self.first_batch_id().get(); + if batch_id < first_batch_id { + return BatchStatus::AlreadyProcessed; + } + + let tx_batch = self.pending_batches(batch_id); + if tx_batch.is_empty() { + return BatchStatus::Empty; + } + + if self.is_batch_full(&tx_batch, batch_id, first_batch_id) { + if batch_id == first_batch_id { + return BatchStatus::WaitingForSignatures; + } else { + return BatchStatus::Full; + } + } + + let mut tx_ids = ManagedVec::new(); + for tx in tx_batch.iter() { + tx_ids.push(tx.nonce); + } + + let max_tx_batch_block_duration = self.max_tx_batch_block_duration().get(); + let first_tx_in_batch_block_nonce = tx_batch.get_first_tx().block_nonce; + + BatchStatus::PartiallyFull { + end_block_nonce: first_tx_in_batch_block_nonce + max_tx_batch_block_duration, + tx_ids, + } + } + + // private + + fn add_to_batch(&self, transaction: Transaction) -> u64 { + let first_batch_id = self.first_batch_id().get(); + let last_batch_id = self.last_batch_id().get(); + let mut last_batch = self.pending_batches(last_batch_id); + + if self.is_batch_full(&last_batch, last_batch_id, first_batch_id) { + let (new_batch_id, _) = self.create_new_batch(transaction); + + new_batch_id + } else { + last_batch.push(transaction); + + last_batch_id + } + } + + // optimized to prevent reading/storing the batch over and over + fn add_multiple_tx_to_batch( + &self, + transactions: &ManagedVec>, + ) -> ManagedVec { + if transactions.is_empty() { + return ManagedVec::new(); + } + + let first_batch_id = self.first_batch_id().get(); + let mut last_batch_id = self.last_batch_id().get(); + let mut last_batch = self.pending_batches(last_batch_id); + let mut batch_ids = ManagedVec::new(); + + for tx in transactions { + if self.is_batch_full(&last_batch, last_batch_id, first_batch_id) { + (last_batch_id, last_batch) = self.create_new_batch(tx); + } else { + last_batch.push(tx); + } + + batch_ids.push(last_batch_id); + } + + batch_ids + } + + fn create_new_batch( + &self, + transaction: Transaction, + ) -> (u64, TxBatchMapper) { + let last_batch_id = self.last_batch_id().get(); + let new_batch_id = last_batch_id + 1; + + let mut new_batch = self.pending_batches(new_batch_id); + new_batch.push(transaction); + + self.last_batch_id().set(new_batch_id); + + (new_batch_id, new_batch) + } + + fn is_batch_full( + &self, + tx_batch: &TxBatchMapper, + batch_id: u64, + first_batch_id: u64, + ) -> bool { + if tx_batch.is_empty() { + return false; + } + + let max_batch_size = self.max_tx_batch_size().get(); + if tx_batch.len() == max_batch_size { + return true; + } + + // if this is not the first batch, we ignore the timestamp checks + // we only check for max len + if batch_id > first_batch_id { + return false; + } + + let current_block_nonce = self.blockchain().get_block_nonce(); + let first_tx_in_batch_block_nonce = tx_batch.get_first_tx().block_nonce; + + // reorg protection + if current_block_nonce < first_tx_in_batch_block_nonce { + return false; + } + + let block_diff = current_block_nonce - first_tx_in_batch_block_nonce; + let max_tx_batch_block_duration = self.max_tx_batch_block_duration().get(); + + block_diff >= max_tx_batch_block_duration + } + + fn is_batch_final(&self, tx_batch: &TxBatchMapper) -> bool { + if tx_batch.is_empty() { + return false; + } + + let last_tx_in_batch = tx_batch.get_last_tx(); + let current_block = self.blockchain().get_block_nonce(); + + // reorg protection + if current_block < last_tx_in_batch.block_nonce { + return false; + } + + let block_diff = current_block - last_tx_in_batch.block_nonce; + + block_diff > MIN_BLOCKS_FOR_FINALITY + } + + fn clear_first_batch(&self, mapper: &mut TxBatchMapper) { + let first_batch_id = self.first_batch_id().get(); + let new_first_batch_id = first_batch_id + 1; + + // for the case when the last existing batch was processed + // otherwise, we'd create a batch with the same ID again + self.last_batch_id().update(|last_batch_id| { + if *last_batch_id == first_batch_id { + *last_batch_id = new_first_batch_id; + } + }); + self.first_batch_id().set(new_first_batch_id); + + mapper.clear(); + } + + fn get_and_save_next_tx_id(&self) -> u64 { + self.last_tx_nonce().update(|last_tx_nonce| { + *last_tx_nonce += 1; + *last_tx_nonce + }) + } + + // storage + + #[view(getFirstBatchId)] + #[storage_mapper("firstBatchId")] + fn first_batch_id(&self) -> SingleValueMapper; + + #[view(getLastBatchId)] + #[storage_mapper("lastBatchId")] + fn last_batch_id(&self) -> SingleValueMapper; + + #[storage_mapper("pendingBatches")] + fn pending_batches(&self, batch_id: u64) -> TxBatchMapper; + + #[storage_mapper("lastTxNonce")] + fn last_tx_nonce(&self) -> SingleValueMapper; + + // configurable + + #[storage_mapper("maxTxBatchSize")] + fn max_tx_batch_size(&self) -> SingleValueMapper; + + #[storage_mapper("maxTxBatchBlockDuration")] + fn max_tx_batch_block_duration(&self) -> SingleValueMapper; +} diff --git a/common/tx-batch-module/src/tx_batch_mapper.rs b/common/tx-batch-module/src/tx_batch_mapper.rs new file mode 100644 index 00000000..589d7c08 --- /dev/null +++ b/common/tx-batch-module/src/tx_batch_mapper.rs @@ -0,0 +1,141 @@ +use multiversx_sc::{ + api::{ErrorApiImpl, StorageMapperApi}, + storage::{ + mappers::{StorageClearable, StorageMapper, VecMapper}, + StorageKey, + }, +}; +use transaction::Transaction; + +static EMPTY_VEC_ERR_MSG: &[u8] = b"Empty vec"; + +pub struct TxBatchMapper +where + SA: StorageMapperApi, +{ + vec_mapper: VecMapper>, + vec_len: usize, + first_tx: Option>, + last_tx: Option>, +} + +impl StorageMapper for TxBatchMapper +where + SA: StorageMapperApi, +{ + fn new(base_key: StorageKey) -> Self { + let vec_mapper = VecMapper::new(base_key); + let vec_len = vec_mapper.len(); + + let (first_tx, last_tx) = if vec_len > 0 { + (Some(vec_mapper.get(1)), Some(vec_mapper.get(vec_len))) + } else { + (None, None) + }; + + TxBatchMapper { + vec_mapper, + vec_len, + first_tx, + last_tx, + } + } +} + +impl StorageClearable for TxBatchMapper +where + SA: StorageMapperApi, +{ + fn clear(&mut self) { + self.vec_mapper.clear(); + } +} + +impl TxBatchMapper +where + SA: StorageMapperApi, +{ + #[inline] + pub fn len(&self) -> usize { + self.vec_len + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn get_first_tx(&self) -> &Transaction { + if self.is_empty() { + SA::error_api_impl().signal_error(EMPTY_VEC_ERR_MSG); + } + + unsafe { self.first_tx.as_ref().unwrap_unchecked() } + } + + pub fn get_last_tx(&self) -> &Transaction { + if self.is_empty() { + SA::error_api_impl().signal_error(EMPTY_VEC_ERR_MSG); + } + + unsafe { self.last_tx.as_ref().unwrap_unchecked() } + } + + pub fn push(&mut self, tx: Transaction) { + if self.is_empty() { + self.first_tx = Some(tx.clone()); + } + + self.vec_mapper.push(&tx); + self.vec_len += 1; + self.last_tx = Some(tx); + } + + /// Provides a forward iterator. + pub fn iter(&self) -> Iter<'_, SA> { + Iter::new(self) + } +} + +pub struct Iter<'a, SA> +where + SA: StorageMapperApi, +{ + index: usize, + mapper: &'a TxBatchMapper, +} + +impl<'a, SA> Iter<'a, SA> +where + SA: StorageMapperApi, +{ + fn new(mapper: &'a TxBatchMapper) -> Iter<'a, SA> { + Iter { index: 1, mapper } + } +} + +impl<'a, SA> Iterator for Iter<'a, SA> +where + SA: StorageMapperApi, +{ + type Item = Transaction; + + #[inline] + fn next(&mut self) -> Option { + let current_index = self.index; + if current_index > self.mapper.len() { + return None; + } + + self.index += 1; + + if current_index == 1 { + return Some(self.mapper.get_first_tx().clone()); + } + if current_index == self.mapper.len() { + return Some(self.mapper.get_last_tx().clone()); + } + + Some(self.mapper.vec_mapper.get_unchecked(current_index)) + } +} diff --git a/erdjs-snippets/.gitignore b/erdjs-snippets/.gitignore deleted file mode 100644 index d3369a9c..00000000 --- a/erdjs-snippets/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -**/node_modules -package-lock.json -**/*.session.sqlite -out diff --git a/erdjs-snippets/adder/adder.spec.ts b/erdjs-snippets/adder/adder.spec.ts deleted file mode 100644 index 9ae50ed9..00000000 --- a/erdjs-snippets/adder/adder.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Balance } from "@elrondnetwork/erdjs"; -import { AirdropService, ITestSession, IUser, TestSession } from "@elrondnetwork/erdjs-snippets"; -import { assert } from "chai"; -import { AdderInteractor } from "./adderInteractor"; - -describe("adder snippet", async function () { - this.bail(true); - - let suite = this; - let session: ITestSession; - let whale: IUser; - let owner: IUser; - - this.beforeAll(async function () { - session = await TestSession.loadOnSuite("default", suite); - whale = session.users.whale; - owner = session.users.alice; - await session.syncNetworkConfig(); - }); - - it("airdrop", async function () { - session.expectLongInteraction(this); - - await session.syncUsers([whale]); - await AirdropService.createOnSession(session).sendToEachUser(whale, Balance.egld(1)); - }); - - it("setup", async function () { - session.expectLongInteraction(this); - - await session.syncUsers([owner]); - - let interactor = await AdderInteractor.create(session); - let contractAddress = await interactor.deploy(owner, 42); - await session.saveAddress("contractAddress", contractAddress); - }); - - it("add", async function () { - session.expectLongInteraction(this); - - await session.syncUsers([owner]); - - let contractAddress = await session.loadAddress("contractAddress"); - let interactor = await AdderInteractor.create(session, contractAddress); - - await interactor.add(owner, 3); - }); - - it("getSum", async function () { - let contractAddress = await session.loadAddress("contractAddress"); - let interactor = await AdderInteractor.create(session, contractAddress); - let result = await interactor.getSum(owner); - assert.isTrue(result > 0); - }); -}); diff --git a/erdjs-snippets/adder/adderInteractor.ts b/erdjs-snippets/adder/adderInteractor.ts deleted file mode 100644 index f12302b6..00000000 --- a/erdjs-snippets/adder/adderInteractor.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Address, BigUIntValue, Code, GasLimit, Interaction } from "@elrondnetwork/erdjs"; -import path from "path"; -import { createSmartContract, DefaultInteractor, ITestSession, IUser } from "@elrondnetwork/erdjs-snippets"; - -const PathToWasm = path.resolve(__dirname, "..", "adder", "output", "adder.wasm"); -const PathToAbi = path.resolve(__dirname, "..", "adder", "output", "adder.abi.json"); - -export class AdderInteractor extends DefaultInteractor { - static async create(session: ITestSession, address?: Address): Promise { - let contract = await createSmartContract(PathToAbi, address); - let interactor = new AdderInteractor(session, contract); - return interactor; - } - - async deploy(deployer: IUser, initialValue: number): Promise
{ - return await this.doDeploy(deployer, PathToWasm, { - gasLimit: new GasLimit(20000000), - initArguments: [new BigUIntValue(initialValue)] - }); - } - - async add(owner: IUser, value: number): Promise { - let interaction = this.contract.methods - .add([new BigUIntValue(value)]) - .withGasLimit(new GasLimit(10000000)); - - await this.runInteraction(owner, interaction); - } - - async getSum(caller: IUser): Promise { - let interaction = this.contract.methods.getSum(); - let { firstValue: result } = await this.runQuery(caller, interaction); - return (result).valueOf().toNumber(); - } -} diff --git a/erdjs-snippets/devnet.session.json b/erdjs-snippets/devnet.session.json deleted file mode 100644 index 9ee10b03..00000000 --- a/erdjs-snippets/devnet.session.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "proxyUrl": "https://devnet-gateway.elrond.com", - "whalePem": "~/elrondsdk/testwallets/alice.pem" -} diff --git a/erdjs-snippets/localnet.session.json b/erdjs-snippets/localnet.session.json deleted file mode 100644 index d49a2cd4..00000000 --- a/erdjs-snippets/localnet.session.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "proxyUrl": "http://localhost:7950", - "whalePem": "~/elrondsdk/testwallets/alice.pem" -} diff --git a/erdjs-snippets/package.json b/erdjs-snippets/package.json deleted file mode 100644 index 43273ec5..00000000 --- a/erdjs-snippets/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "your-snippets", - "files": [ - "out/**/*" - ], - "scripts": { - "compile": "tsc -p tsconfig.json" - }, - "dependencies": { - "@elrondnetwork/erdjs": "^9.2.0", - "@elrondnetwork/erdjs-snippets": "^1.0.0", - "bignumber.js": "9.0.2" - }, - "devDependencies": { - "@types/assert": "1.4.6", - "@types/chai": "4.2.11", - "@types/mocha": "9.1.0", - "@types/node": "13.13.2", - "typescript": "4.1.2", - "chai": "4.2.0", - "mocha": "9.2.1", - "ts-node": "9.1.1" - }, - "mocha": { - "extension": ["ts"], - "require": "ts-node/register", - "spec": "**/*.spec.ts" - } -} diff --git a/erdjs-snippets/tsconfig.json b/erdjs-snippets/tsconfig.json deleted file mode 100644 index f0bbd11d..00000000 --- a/erdjs-snippets/tsconfig.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "compilerOptions": { - "module": "CommonJS", - "target": "es2015", - "outDir": "out", - "lib": [ - "ES2015" - ], - "sourceMap": true, - "allowJs": true, - "strict": true, - "strictPropertyInitialization": true, - "strictNullChecks": true, - "skipLibCheck": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "noUnusedParameters": true, - "esModuleInterop": true, - "declaration": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true - }, - "include": [ - "**/*.ts" - ], - "exclude": [ - "node_modules", - "out" - ] -} diff --git a/esdt-safe/.gitignore b/esdt-safe/.gitignore new file mode 100644 index 00000000..f00d026a --- /dev/null +++ b/esdt-safe/.gitignore @@ -0,0 +1,11 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +*/target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# The mxpy output +output diff --git a/esdt-safe/Cargo.toml b/esdt-safe/Cargo.toml new file mode 100644 index 00000000..d3c4062d --- /dev/null +++ b/esdt-safe/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "esdt-safe" +version = "0.0.0" +authors = ["you"] +edition = "2018" +publish = false + +[lib] +path = "src/lib.rs" +[dependencies.transaction] +path = "../common/transaction" + +[dependencies.eth-address] +path = "../common/eth-address" + +[dependencies.fee-estimator-module] +path = "../common/fee-estimator-module" + +[dependencies.token-module] +path = "../common/token-module" + +[dependencies.tx-batch-module] +path = "../common/tx-batch-module" + +[dependencies.max-bridged-amount-module] +path = "../common/max-bridged-amount-module" + +[dependencies.multiversx-sc] +version = "0.41.3" + +[dependencies.multiversx-sc-modules] +version = "0.41.3" +[dev-dependencies.multiversx-sc-scenario] +version = "0.41.3" diff --git a/esdt-safe/interaction/snippets.sh b/esdt-safe/interaction/snippets.sh new file mode 100644 index 00000000..8a46ca3a --- /dev/null +++ b/esdt-safe/interaction/snippets.sh @@ -0,0 +1,130 @@ +ALICE="/home/elrond/elrond-sdk/mxpy/testnet/wallets/users/alice.pem" +BOB="/home/elrond/elrond-sdk/mxpy/testnet/wallets/users/bob.pem" +ADDRESS=$(mxpy data load --key=address-testnet-esdt-safe) +DEPLOY_TRANSACTION=$(mxpy data load --key=deployTransaction-testnet) +PROXY=https://testnet-gateway.elrond.com +CHAIN_ID=T + +BOB_ADDRESS=0x8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8 # 32 bytes +CAROL_ADDRESS=0xb2a11555ce521e4944e09ab17549d85b487dcd26c84b5017a39e31a3670889ba # 32 bytes +ALICE_ETH_ADDRESS=0x7d61a56899dd55e5D16C1Bab38f46f42b4d33887 # 20 bytes + +TX_STATUS_EXECUTED=0x03 +TX_STATUS_REJECTED=0x04 + +ESDT_SYSTEM_SC_ADDRESS=erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u + +######################################################################## +################## Update after issuing the tokens ##################### +######################################################################## +WRAPPED_EGLD_TOKEN_ID=0x +WRAPPED_ETH_TOKEN_ID=0x + +deploy() { + ####################################################################### + ################## Update with the contract's address ################# + ####################################################################### + local ETHEREUM_FEE_PREPAY_SC_ADDRESS=0x + + mxpy --verbose contract deploy --project=${PROJECT} --recall-nonce --pem=${ALICE} \ + --gas-limit=100000000 \ + --arguments ${ETHEREUM_FEE_PREPAY_SC_ADDRESS} ${WRAPPED_EGLD_TOKEN_ID} ${WRAPPED_ETH_TOKEN_ID} \ + --send --outfile="deploy-testnet.interaction.json" --proxy=${PROXY} --chain=${CHAIN_ID} || return + + TRANSACTION=$(mxpy data parse --file="deploy-testnet.interaction.json" --expression="data['emitted_tx']['hash']") + ADDRESS=$(mxpy data parse --file="deploy-testnet.interaction.json" --expression="data['emitted_tx']['address']") + + mxpy data store --key=address-testnet-esdt-safe --value=${ADDRESS} + mxpy data store --key=deployTransaction-testnet --value=${TRANSACTION} + + echo "" + echo "Smart contract address: ${ADDRESS}" +} + +upgrade() { + mxpy --verbose contract upgrade ${ADDRESS} --project=${PROJECT} --recall-nonce --pem=${ALICE} \ + --gas-limit=100000000 --send --outfile="upgrade.json" --proxy=${PROXY} --chain=${CHAIN_ID} || return +} + +issueWrappedEth() { + local TOKEN_DISPLAY_NAME=0x57726170706564457468 # "WrappedEth" + local TOKEN_TICKER=0x57455448 # "WETH" + local INITIAL_SUPPLY=0x01 # 1 + local NR_DECIMALS=0x12 # 18 + local CAN_ADD_SPECIAL_ROLES=0x63616e4164645370656369616c526f6c6573 # "canAddSpecialRoles" + local TRUE=0x74727565 # "true" + + mxpy --verbose contract call ${ESDT_SYSTEM_SC_ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=60000000 --value=5000000000000000000 --function="issue" \ + --arguments ${TOKEN_DISPLAY_NAME} ${TOKEN_TICKER} ${INITIAL_SUPPLY} ${NR_DECIMALS} ${CAN_ADD_SPECIAL_ROLES} ${TRUE} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +setLocalRolesWrappedEgld() { + local LOCAL_BURN_ROLE=0x45534454526f6c654c6f63616c4275726e # "ESDTRoleLocalBurn" + local ADDRESS_HEX = $(mxpy wallet bech32 --decode ${ADDRESS}) + + mxpy --verbose contract call ${ESDT_SYSTEM_SC_ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=60000000 --function="setSpecialRole" \ + --arguments ${WRAPPED_EGLD_TOKEN_ID} ${ADDRESS_HEX} ${LOCAL_BURN_ROLE} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +setLocalRolesWrappedEth() { + local LOCAL_BURN_ROLE=0x45534454526f6c654c6f63616c4275726e # "ESDTRoleLocalBurn" + local ADDRESS_HEX = $(mxpy wallet bech32 --decode ${ADDRESS}) + + mxpy --verbose contract call ${ESDT_SYSTEM_SC_ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=60000000 --function="setSpecialRole" \ + --arguments ${WRAPPED_ETH_TOKEN_ID} ${ADDRESS_HEX} ${LOCAL_BURN_ROLE} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +getNextPendingTransaction() { + mxpy --verbose contract call ${ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=25000000 --function="getNextPendingTransaction" \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +setTransactionExecuted() { + local RELAYER_REWARD_ADDRESS = ${CAROL_ADDRESS} + local ORIGINAL_TX_SENDER = ${BOB_ADDRESS} + local TX_NONCE = 0x01 + local TX_STATUS = ${TX_STATUS_EXECUTED} + + mxpy --verbose contract call ${ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=90000000 --function="setTransactionStatus" \ + --arguments ${RELAYER_REWARD_ADDRESS} ${ORIGINAL_TX_SENDER} ${TX_NONCE} ${TX_STATUS} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +setTransactionRejected() { + local RELAYER_REWARD_ADDRESS = ${CAROL_ADDRESS} + local ORIGINAL_TX_SENDER = ${BOB_ADDRESS} + local TX_NONCE = 0x02 + local TX_STATUS = ${TX_STATUS_REJECTED} + + mxpy --verbose contract call ${ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=90000000 --function="setTransactionStatus" \ + --arguments ${RELAYER_REWARD_ADDRESS} ${ORIGINAL_TX_SENDER} ${TX_NONCE} ${TX_STATUS} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID}D} +} + +createTransaction() { + local CREATE_TRANSACTION_ENDPOINT = 0x6372656174655472616e73616374696f6e # "createTransaction" + local DEST_ADDRESS = ${ALICE_ETH_ADDRESS} + local TOKEN_USED_FOR_TX_FEES = 0x45474c44 # "EGLD" + + mxpy --verbose contract call ${ADDRESS} --recall-nonce --pem=${BOB} \ + --gas-limit=50000000 --function="ESDTTransfer" \ + --arguments ${WRAPPED_EGLD_TOKEN_IDENTIFIER} 0x64 ${CREATE_TRANSACTION_ENDPOINT} ${DEST_ADDRESS} ${TOKEN_USED_FOR_TX_FEES} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +# views + +getTransactionStatus() { + mxpy --verbose contract query ${ADDRESS} --function="getTransactionStatus" \ + --arguments ${BOB_ADDRESS} 0x01 \ + --proxy=${PROXY} +} diff --git a/esdt-safe/mandos/add_refund_batch.scen.json b/esdt-safe/mandos/add_refund_batch.scen.json new file mode 100644 index 00000000..c88b0013 --- /dev/null +++ b/esdt-safe/mandos/add_refund_batch.scen.json @@ -0,0 +1,116 @@ +{ + "name": "add refund batch", + "steps": [ + { + "step": "externalSteps", + "path": "create_another_tx_ok.scen.json" + }, + { + "step": "setState", + "currentBlockInfo": { + "blockNonce": "5" + } + }, + { + "step": "scCall", + "txId": "add-refund-batch", + "tx": { + "from": "address:owner", + "to": "sc:esdt_safe", + "function": "addRefundBatch", + "arguments": [ + { + "01-block_nonce": "u64:2", + "02-nonce": "u64:1", + "03-from": "u32:20|0x0102030405060708091011121314151617181920", + "04-to": "u32:32|address:user1", + "05-token_identifier": "nested:str:BRIDGE-123456", + "06-amount": "biguint:2,000,000", + "07-is_refund_tx": "u8:1", + "11-block_nonce": "u64:3", + "12-nonce": "u64:2", + "13-from": "u32:20|0x0102030405060708091011121314151617181920", + "14-to": "u32:32|address:user2", + "15-token_identifier": "nested:str:BRIDGE-123456", + "16-amount": "biguint:3,000,000", + "17-is_refund_tx": "u8:1" + } + ], + "gasLimit": "100,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "out": [ + + ], + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": { + "balance": "3,001,300", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:pendingBatches|u64:1|str:.item|u32:1": { + "1-block_nonce": "u64:0", + "2-nonce": "u64:1", + "3-from": "u32:32|address:user1", + "4-to": "u32:20|0x0102030405060708091011121314151617181920", + "5-token_identifier": "nested:str:BRIDGE-123456", + "6-amount": "biguint:400", + "7-is_refund_tx": "u8:0" + }, + "str:pendingBatches|u64:1|str:.item|u32:2": { + "1-block_nonce": "u64:0", + "2-nonce": "u64:2", + "3-from": "u32:32|address:user2", + "4-to": "u32:20|0x0102030405060708091011121314151617181920", + "5-token_identifier": "nested:str:BRIDGE-123456", + "6-amount": "biguint:900", + "7-is_refund_tx": "u8:0" + }, + "str:pendingBatches|u64:1|str:.item|u32:3": { + "1-block_nonce": "u64:5", + "2-nonce": "u64:3", + "3-from": "u32:32|address:user1", + "4-to": "u32:20|0x0102030405060708091011121314151617181920", + "5-token_identifier": "nested:str:BRIDGE-123456", + "6-amount": "biguint:500,000", + "7-is_refund_tx": "u8:1" + }, + "str:pendingBatches|u64:1|str:.item|u32:4": { + "1-block_nonce": "u64:5", + "2-nonce": "u64:4", + "3-from": "u32:32|address:user2", + "4-to": "u32:20|0x0102030405060708091011121314151617181920", + "5-token_identifier": "nested:str:BRIDGE-123456", + "6-amount": "biguint:1,500,000", + "7-is_refund_tx": "u8:1" + }, + "str:firstBatchId": "1", + "str:lastBatchId": "1", + + "str:accumulatedTransactionFees|nested:str:BRIDGE-123456": "3,000,000", + + "+": "" + }, + "code": "file:../output/esdt-safe.wasm" + }, + "+": {} + } + } + ] +} diff --git a/esdt-safe/mandos/create_another_tx_ok.scen.json b/esdt-safe/mandos/create_another_tx_ok.scen.json new file mode 100644 index 00000000..224c9e7a --- /dev/null +++ b/esdt-safe/mandos/create_another_tx_ok.scen.json @@ -0,0 +1,108 @@ +{ + "name": "user2 creates a tx as well", + "steps": [ + { + "step": "externalSteps", + "path": "create_transaction_ok.scen.json" + }, + { + "step": "scCall", + "txId": "user2-create-transaction-ok", + "tx": { + "from": "address:user2", + "to": "sc:esdt_safe", + "esdt": { + "tokenIdentifier": "str:BRIDGE-123456", + "value": "1,500,900" + }, + "function": "createTransaction", + "arguments": [ + "0x0102030405060708091011121314151617181920" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "out": [ + + ], + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scQuery", + "txId": "get-batch-status-after-second-add", + "tx": { + "to": "sc:esdt_safe", + "function": "getBatchStatus", + "arguments": [ + "1" + ] + }, + "expect": { + "out": [ + { + "1-enum-variant": "u8:2", + "2-end_block_nonce": "u64:100", + "3-vec_len": "u32:2", + "4-tx_ids": "u64:1|u64:2" + } + ] + } + }, + { + "step": "checkState", + "accounts": { + "address:user2": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "499,100" + }, + "storage": {} + }, + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": { + "balance": "3,001,300", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:pendingBatches|u64:1|str:.item|u32:1": { + "1-block_nonce": "u64:0", + "2-nonce": "u64:1", + "3-from": "u32:32|address:user1", + "4-to": "u32:20|0x0102030405060708091011121314151617181920", + "5-token_identifier": "nested:str:BRIDGE-123456", + "6-amount": "biguint:400", + "7-is_refund_tx": "u8:0" + }, + "str:pendingBatches|u64:1|str:.item|u32:2": { + "1-block_nonce": "u64:0", + "2-nonce": "u64:2", + "3-from": "u32:32|address:user2", + "4-to": "u32:20|0x0102030405060708091011121314151617181920", + "5-token_identifier": "nested:str:BRIDGE-123456", + "6-amount": "biguint:900", + "7-is_refund_tx": "u8:0" + }, + "str:firstBatchId": "1", + "str:lastBatchId": "1", + "str:accumulatedTransactionFees|nested:str:BRIDGE-123456": "3,000,000", + "+": "" + }, + "code": "file:../output/esdt-safe.wasm" + }, + "+": {} + } + } + ] +} diff --git a/esdt-safe/mandos/create_another_tx_too_late_for_batch.scen.json b/esdt-safe/mandos/create_another_tx_too_late_for_batch.scen.json new file mode 100644 index 00000000..c9584de9 --- /dev/null +++ b/esdt-safe/mandos/create_another_tx_too_late_for_batch.scen.json @@ -0,0 +1,292 @@ +{ + "name": "user1 creates another tx, but it's too late to enter the current batch", + "steps": [ + { + "step": "externalSteps", + "path": "create_another_tx_ok.scen.json" + }, + { + "step": "setState", + "currentBlockInfo": { + "blockNonce": "101" + }, + "accounts": { + "address:user2": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "2,000,000" + }, + "storage": {} + } + } + }, + { + "step": "scQuery", + "txId": "get-batch-status-final", + "tx": { + "to": "sc:esdt_safe", + "function": "getBatchStatus", + "arguments": [ + "1" + ] + }, + "expect": { + "out": [ + { + "1-enum-variant": "u8:4" + } + ] + } + }, + { + "step": "scCall", + "txId": "user2-create-transaction-too-late-for-batch", + "tx": { + "from": "address:user2", + "to": "sc:esdt_safe", + "esdt": { + "tokenIdentifier": "str:BRIDGE-123456", + "value": "1,500,100" + }, + "function": "createTransaction", + "arguments": [ + "0x0102030405060708091011121314151617181920" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "out": [ + + ], + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:user2": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "499,900" + }, + "storage": {} + }, + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": { + "balance": "4,501,400", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:pendingBatches|u64:1|str:.item|u32:1": { + "1-block_nonce": "u64:0", + "2-nonce": "u64:1", + "3-from": "u32:32|address:user1", + "4-to": "u32:20|0x0102030405060708091011121314151617181920", + "5-token_identifier": "nested:str:BRIDGE-123456", + "6-amount": "biguint:400", + "7-is_refund_tx": "u8:0" + }, + "str:pendingBatches|u64:1|str:.item|u32:2": { + "1-block_nonce": "u64:0", + "2-nonce": "u64:2", + "3-from": "u32:32|address:user2", + "4-to": "u32:20|0x0102030405060708091011121314151617181920", + "5-token_identifier": "nested:str:BRIDGE-123456", + "6-amount": "biguint:900", + "7-is_refund_tx": "u8:0" + }, + "str:pendingBatches|u64:2|str:.item|u32:1": { + "1-block_nonce": "u64:101", + "2-nonce": "u64:3", + "3-from": "u32:32|address:user2", + "4-to": "u32:20|0x0102030405060708091011121314151617181920", + "5-token_identifier": "nested:str:BRIDGE-123456", + "6-amount": "biguint:100", + "7-is_refund_tx": "u8:0" + }, + "str:firstBatchId": "1", + "str:lastBatchId": "2", + "str:accumulatedTransactionFees|nested:str:BRIDGE-123456": "4,500,000", + "+": "" + }, + "code": "file:../output/esdt-safe.wasm" + }, + "+": {} + } + }, + { + "step": "scQuery", + "txId": "query-first-batch-id", + "tx": { + "to": "sc:esdt_safe", + "function": "getFirstBatchId", + "arguments": [] + }, + "expect": { + "out": [ + "1" + ] + } + }, + { + "step": "scQuery", + "txId": "query-last-batch-id", + "tx": { + "to": "sc:esdt_safe", + "function": "getLastBatchId", + "arguments": [] + }, + "expect": { + "out": [ + "2" + ] + } + }, + { + "step": "setState", + "currentBlockInfo": { + "blockNonce": "500" + }, + "accounts": { + "address:user3": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "2,000,000" + }, + "storage": {} + } + } + }, + { + "step": "scCall", + "txId": "add another tx, too late, but still added to current last batch", + "tx": { + "from": "address:user3", + "to": "sc:esdt_safe", + "esdt": { + "tokenIdentifier": "str:BRIDGE-123456", + "value": "1,500,100" + }, + "function": "createTransaction", + "arguments": [ + "0x0102030405060708091011121314151617181920" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "out": [ + + ], + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:user2": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "499,900" + }, + "storage": {} + }, + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": { + "balance": "6,001,500", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:pendingBatches|u64:1|str:.item|u32:1": { + "1-block_nonce": "u64:0", + "2-nonce": "u64:1", + "3-from": "u32:32|address:user1", + "4-to": "u32:20|0x0102030405060708091011121314151617181920", + "5-token_identifier": "nested:str:BRIDGE-123456", + "6-amount": "biguint:400", + "7-is_refund_tx": "u8:0" + }, + "str:pendingBatches|u64:1|str:.item|u32:2": { + "1-block_nonce": "u64:0", + "2-nonce": "u64:2", + "3-from": "u32:32|address:user2", + "4-to": "u32:20|0x0102030405060708091011121314151617181920", + "5-token_identifier": "nested:str:BRIDGE-123456", + "6-amount": "biguint:900", + "7-is_refund_tx": "u8:0" + }, + "str:pendingBatches|u64:2|str:.item|u32:1": { + "1-block_nonce": "u64:101", + "2-nonce": "u64:3", + "3-from": "u32:32|address:user2", + "4-to": "u32:20|0x0102030405060708091011121314151617181920", + "5-token_identifier": "nested:str:BRIDGE-123456", + "6-amount": "biguint:100", + "7-is_refund_tx": "u8:0" + }, + "str:pendingBatches|u64:2|str:.item|u32:2": { + "1-block_nonce": "u64:500", + "2-nonce": "u64:4", + "3-from": "u32:32|address:user3", + "4-to": "u32:20|0x0102030405060708091011121314151617181920", + "5-token_identifier": "nested:str:BRIDGE-123456", + "6-amount": "biguint:100", + "7-is_refund_tx": "u8:0" + }, + "str:firstBatchId": "1", + "str:lastBatchId": "2", + "str:accumulatedTransactionFees|nested:str:BRIDGE-123456": "6,000,000", + "+": "" + }, + "code": "file:../output/esdt-safe.wasm" + }, + "+": {} + } + }, + { + "step": "scQuery", + "txId": "query-is-batch-2-final", + "tx": { + "to": "sc:esdt_safe", + "function": "getBatchStatus", + "arguments": [ + "2" + ] + }, + "expect": { + "out": [ + { + "1-enum_identifier": "u8:2", + "2-end_block_nonce": "u64:201", + "3-vec_len": "u32:2", + "4-first_tx_nonce": "u64:3", + "5-second_tx_nonce": "u64:4" + } + ] + } + } + ] +} diff --git a/esdt-safe/mandos/create_transaction_ok.scen.json b/esdt-safe/mandos/create_transaction_ok.scen.json new file mode 100644 index 00000000..d3e159cb --- /dev/null +++ b/esdt-safe/mandos/create_transaction_ok.scen.json @@ -0,0 +1,115 @@ +{ + "name": "create transaction ok", + "steps": [ + { + "step": "externalSteps", + "path": "setup_accounts.scen.json" + }, + { + "step": "scQuery", + "txId": "get-batch-status-before", + "tx": { + "to": "sc:esdt_safe", + "function": "getBatchStatus", + "arguments": [ + "1" + ] + }, + "expect": { + "out": [ + { + "1-enum-variant": "u8:1" + } + ] + } + }, + { + "step": "scCall", + "txId": "create-transaction-ok", + "tx": { + "from": "address:user1", + "to": "sc:esdt_safe", + "esdt": { + "tokenIdentifier": "str:BRIDGE-123456", + "value": "1,500,400" + }, + "function": "createTransaction", + "arguments": [ + "0x0102030405060708091011121314151617181920" + ], + "gasLimit": "60,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "out": [], + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scQuery", + "txId": "get-batch-status-after", + "tx": { + "to": "sc:esdt_safe", + "function": "getBatchStatus", + "arguments": [ + "1" + ] + }, + "expect": { + "out": [ + { + "1-enum-variant": "u8:2", + "2-end_block_nonce": "u64:100", + "3-vec_len": "u32:1", + "4-tx_ids": "u64:1" + } + ] + } + }, + { + "step": "checkState", + "accounts": { + "address:user1": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "499,600" + }, + "storage": {} + }, + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": { + "balance": "1,500,400", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:pendingBatches|u64:1|str:.item|u32:1": { + "1-block_nonce": "u64:0", + "2-nonce": "u64:1", + "3-from": "u32:32|address:user1", + "4-to": "u32:20|0x0102030405060708091011121314151617181920", + "5-token_identifier": "nested:str:BRIDGE-123456", + "6-amount": "biguint:400", + "7-is_refund_tx": "u8:0" + }, + "str:firstBatchId": "1", + "str:lastBatchId": "1", + "str:accumulatedTransactionFees|nested:str:BRIDGE-123456": "1,500,000", + "+": "" + }, + "code": "file:../output/esdt-safe.wasm" + }, + "+": {} + } + } + ] +} diff --git a/esdt-safe/mandos/create_transaction_over_max_amount.scen.json b/esdt-safe/mandos/create_transaction_over_max_amount.scen.json new file mode 100644 index 00000000..c76faa7f --- /dev/null +++ b/esdt-safe/mandos/create_transaction_over_max_amount.scen.json @@ -0,0 +1,82 @@ +{ + "name": "create transaction ok", + "steps": [ + { + "step": "externalSteps", + "path": "setup_accounts.scen.json" + }, + { + "step": "scCall", + "txId": "create-transaction-ok", + "tx": { + "from": "address:owner", + "to": "sc:esdt_safe", + "function": "setMaxBridgedAmount", + "arguments": [ + "str:BRIDGE-123456", + "1,600,000" + ], + "gasLimit": "60,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "out": [], + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "create-transaction-over-max", + "tx": { + "from": "address:user1", + "to": "sc:esdt_safe", + "esdt": { + "tokenIdentifier": "str:BRIDGE-123456", + "value": "1,800,000" + }, + "function": "createTransaction", + "arguments": [ + "0x0102030405060708091011121314151617181920" + ], + "gasLimit": "60,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:Deposit over max amount", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "create-transaction-ok", + "tx": { + "from": "address:user1", + "to": "sc:esdt_safe", + "esdt": { + "tokenIdentifier": "str:BRIDGE-123456", + "value": "1,600,000" + }, + "function": "createTransaction", + "arguments": [ + "0x0102030405060708091011121314151617181920" + ], + "gasLimit": "60,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "out": [ + + ], + "message": "", + "gas": "*", + "refund": "*" + } + } + ] +} diff --git a/esdt-safe/mandos/distribute_fees.scen.json b/esdt-safe/mandos/distribute_fees.scen.json new file mode 100644 index 00000000..30469112 --- /dev/null +++ b/esdt-safe/mandos/distribute_fees.scen.json @@ -0,0 +1,63 @@ +{ + "name": "owner claim fees", + "steps": [ + { + "step": "externalSteps", + "path": "execute_batch_one_success_one_rejected.scen.json" + }, + { + "step": "scCall", + "txId": "owner-distribute-fees", + "tx": { + "from": "address:owner", + "to": "sc:esdt_safe", + "value": "0", + "function": "distributeFees", + "arguments": [ + "address:owner|u32:10000" + ], + "gasLimit": "30,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:owner": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "3,000,000" + }, + "storage": {} + }, + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": { + "balance": "0", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:accumulatedTransactionFees|nested:str:BRIDGE-123456": "0", + + "+": "" + }, + "code": "file:../output/esdt-safe.wasm" + }, + "+": {} + } + } + ] +} diff --git a/esdt-safe/mandos/execute_batch_both_rejected.scen.json b/esdt-safe/mandos/execute_batch_both_rejected.scen.json new file mode 100644 index 00000000..55d42cc2 --- /dev/null +++ b/esdt-safe/mandos/execute_batch_both_rejected.scen.json @@ -0,0 +1,119 @@ +{ + "name": "execute batch transactions, both rejected", + "steps": [ + { + "step": "externalSteps", + "path": "get_next_tx_batch.scen.json" + }, + { + "step": "scCall", + "txId": "set-tx-status-both-rejected", + "tx": { + "from": "address:owner", + "to": "sc:esdt_safe", + "value": "0", + "function": "setTransactionBatchStatus", + "arguments": [ + "1", + "4", "4" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "user1-claim-refund", + "tx": { + "from": "address:user1", + "to": "sc:esdt_safe", + "value": "0", + "function": "claimRefund", + "arguments": [ + "str:BRIDGE-123456" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": "*", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "user2-claim-refund", + "tx": { + "from": "address:user2", + "to": "sc:esdt_safe", + "value": "0", + "function": "claimRefund", + "arguments": [ + "str:BRIDGE-123456" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": "*", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:user1": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "500,000" + }, + "storage": {} + }, + "address:user2": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "500,000" + }, + "storage": {} + }, + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": { + "balance": "3,000,000", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:pendingBatches|u64:1": "", + "str:firstBatchId": "2", + "str:lastBatchId": "2", + + "str:accumulatedTransactionFees|nested:str:BRIDGE-123456": "3,000,000", + + "+": "" + }, + "code": "file:../output/esdt-safe.wasm" + }, + "+": {} + } + } + ] +} diff --git a/esdt-safe/mandos/execute_batch_both_success.scen.json b/esdt-safe/mandos/execute_batch_both_success.scen.json new file mode 100644 index 00000000..d8d3bdcb --- /dev/null +++ b/esdt-safe/mandos/execute_batch_both_success.scen.json @@ -0,0 +1,117 @@ +{ + "name": "execute batch transactions, both successful", + "steps": [ + { + "step": "externalSteps", + "path": "get_next_tx_batch.scen.json" + }, + { + "step": "scCall", + "txId": "set-tx-status-both-executed", + "tx": { + "from": "address:owner", + "to": "sc:esdt_safe", + "value": "0", + "function": "setTransactionBatchStatus", + "arguments": [ + "1", + "3", "3" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "user1-claim-refund", + "tx": { + "from": "address:user1", + "to": "sc:esdt_safe", + "value": "0", + "function": "claimRefund", + "arguments": [ + "str:BRIDGE-123456" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:Nothing to refund", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "user2-claim-refund", + "tx": { + "from": "address:user2", + "to": "sc:esdt_safe", + "value": "0", + "function": "claimRefund", + "arguments": [ + "str:BRIDGE-123456" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:Nothing to refund", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:user1": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "499,600" + }, + "storage": {} + }, + "address:user2": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "499,100" + }, + "storage": {} + }, + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": { + "balance": "3,000,000", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:pendingBatches|u64:1": "", + "str:firstBatchId": "2", + "str:lastBatchId": "2", + + "str:accumulatedTransactionFees|nested:str:BRIDGE-123456": "3,000,000", + + "+": "" + }, + "code": "file:../output/esdt-safe.wasm" + }, + "+": {} + } + } + ] +} diff --git a/esdt-safe/mandos/execute_batch_one_success_one_rejected.scen.json b/esdt-safe/mandos/execute_batch_one_success_one_rejected.scen.json new file mode 100644 index 00000000..757f5141 --- /dev/null +++ b/esdt-safe/mandos/execute_batch_one_success_one_rejected.scen.json @@ -0,0 +1,118 @@ +{ + "name": "execute batch transactions, one successful, one rejected", + "steps": [ + { + "step": "externalSteps", + "path": "get_next_tx_batch.scen.json" + }, + { + "step": "scCall", + "txId": "set-tx-status-one-executed-one-rejected", + "tx": { + "from": "address:owner", + "to": "sc:esdt_safe", + "value": "0", + "function": "setTransactionBatchStatus", + "arguments": [ + "1", + "3", "4" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "user1-claim-refund", + "tx": { + "from": "address:user1", + "to": "sc:esdt_safe", + "value": "0", + "function": "claimRefund", + "arguments": [ + "str:BRIDGE-123456" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:Nothing to refund", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "user2-claim-refund", + "tx": { + "from": "address:user2", + "to": "sc:esdt_safe", + "value": "0", + "function": "claimRefund", + "arguments": [ + "str:BRIDGE-123456" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": "*", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:user1": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "499,600" + }, + "storage": {} + }, + "address:user2": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "500,000" + }, + "storage": {} + }, + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": { + "balance": "3,000,000", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:pendingBatches|u64:1": "", + "str:firstBatchId": "2", + "str:lastBatchId": "2", + + "str:accumulatedTransactionFees|nested:str:BRIDGE-123456": "3,000,000", + + "+": "" + }, + "code": "file:../output/esdt-safe.wasm" + }, + "+": {} + } + } + ] +} diff --git a/esdt-safe/mandos/execute_transaction_rejected.scen.json b/esdt-safe/mandos/execute_transaction_rejected.scen.json new file mode 100644 index 00000000..52a289ac --- /dev/null +++ b/esdt-safe/mandos/execute_transaction_rejected.scen.json @@ -0,0 +1,89 @@ +{ + "name": "execute transaction rejected", + "steps": [ + { + "step": "externalSteps", + "path": "get_next_pending_tx.scen.json" + }, + { + "step": "scCall", + "txId": "set-tx-status-rejected", + "tx": { + "from": "address:owner", + "to": "sc:esdt_safe", + "value": "0", + "function": "setTransactionBatchStatus", + "arguments": [ + "1", + "4" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "user1-claim-refund", + "tx": { + "from": "address:user1", + "to": "sc:esdt_safe", + "value": "0", + "function": "claimRefund", + "arguments": [ + "str:BRIDGE-123456" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": "*", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:user1": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "500,000" + }, + "storage": {} + }, + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": { + "balance": "1,500,000", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:pendingBatches|u64:1": "", + "str:firstBatchId": "2", + "str:lastBatchId": "2", + + "str:accumulatedTransactionFees|nested:str:BRIDGE-123456": "1,500,000", + + "+": "" + }, + "code": "file:../output/esdt-safe.wasm" + }, + "+": {} + } + } + ] +} diff --git a/esdt-safe/mandos/execute_transaction_success.scen.json b/esdt-safe/mandos/execute_transaction_success.scen.json new file mode 100644 index 00000000..71fbb4c3 --- /dev/null +++ b/esdt-safe/mandos/execute_transaction_success.scen.json @@ -0,0 +1,88 @@ +{ + "name": "execute transaction success", + "steps": [ + { + "step": "externalSteps", + "path": "get_next_pending_tx.scen.json" + }, + { + "step": "scCall", + "txId": "set-tx-status-executed", + "tx": { + "from": "address:owner", + "to": "sc:esdt_safe", + "value": "0", + "function": "setTransactionBatchStatus", + "arguments": [ + "1", + "3" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "user1-claim-refund", + "tx": { + "from": "address:user1", + "to": "sc:esdt_safe", + "value": "0", + "function": "claimRefund", + "arguments": [ + "str:BRIDGE-123456" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:Nothing to refund", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:user1": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "499,600" + }, + "storage": {} + }, + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": { + "balance": "1,500,000", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:pendingBatches|u64:1": "", + "str:firstBatchId": "2", + "str:lastBatchId": "2", + + "str:accumulatedTransactionFees|nested:str:BRIDGE-123456": "1,500,000", + + "+": "" + }, + "code": "file:../output/esdt-safe.wasm" + }, + "+": {} + } + } + ] +} diff --git a/esdt-safe/mandos/get_next_pending_tx.scen.json b/esdt-safe/mandos/get_next_pending_tx.scen.json new file mode 100644 index 00000000..00b61c4f --- /dev/null +++ b/esdt-safe/mandos/get_next_pending_tx.scen.json @@ -0,0 +1,36 @@ +{ + "name": "get next pending tx", + "steps": [ + { + "step": "externalSteps", + "path": "create_transaction_ok.scen.json" + }, + { + "step": "setState", + "currentBlockInfo": { + "blockNonce": "500" + } + }, + { + "step": "scQuery", + "txId": "get-current-tx-batch", + "tx": { + "to": "sc:esdt_safe", + "function": "getCurrentTxBatch", + "arguments": [] + }, + "expect": { + "out": [ + "1", + + "0", + "1", + "address:user1", + "0x0102030405060708091011121314151617181920", + "str:BRIDGE-123456", + "400" + ] + } + } + ] +} diff --git a/esdt-safe/mandos/get_next_tx_batch.scen.json b/esdt-safe/mandos/get_next_tx_batch.scen.json new file mode 100644 index 00000000..8a22bcda --- /dev/null +++ b/esdt-safe/mandos/get_next_tx_batch.scen.json @@ -0,0 +1,43 @@ +{ + "name": "get next tx batch", + "steps": [ + { + "step": "externalSteps", + "path": "create_another_tx_ok.scen.json" + }, + { + "step": "setState", + "currentBlockInfo": { + "blockNonce": "500" + } + }, + { + "step": "scQuery", + "txId": "get-current-tx-batch", + "tx": { + "to": "sc:esdt_safe", + "function": "getCurrentTxBatch", + "arguments": [] + }, + "expect": { + "out": [ + "1", + + "0", + "1", + "address:user1", + "0x0102030405060708091011121314151617181920", + "str:BRIDGE-123456", + "400", + + "0", + "2", + "address:user2", + "0x0102030405060708091011121314151617181920", + "str:BRIDGE-123456", + "900" + ] + } + } + ] +} diff --git a/esdt-safe/mandos/get_next_tx_batch_too_early.scen.json b/esdt-safe/mandos/get_next_tx_batch_too_early.scen.json new file mode 100644 index 00000000..bff60c09 --- /dev/null +++ b/esdt-safe/mandos/get_next_tx_batch_too_early.scen.json @@ -0,0 +1,71 @@ +{ + "name": "get next tx batch too early", + "steps": [ + { + "step": "externalSteps", + "path": "create_another_tx_ok.scen.json" + }, + { + "step": "setState", + "currentBlockInfo": { + "blockNonce": "99" + } + }, + { + "step": "scQuery", + "txId": "get-current-tx-batch", + "tx": { + "to": "sc:esdt_safe", + "function": "getCurrentTxBatch", + "arguments": [] + }, + "expect": { + "out": [] + } + }, + { + "step": "scQuery", + "txId": "get-first-tx-batch", + "tx": { + "to": "sc:esdt_safe", + "function": "getBatch", + "arguments": [ + "1" + ] + }, + "expect": { + "out": [ + "1", + + "0", + "1", + "address:user1", + "0x0102030405060708091011121314151617181920", + "str:BRIDGE-123456", + "400", + + "0", + "2", + "address:user2", + "0x0102030405060708091011121314151617181920", + "str:BRIDGE-123456", + "900" + ] + } + }, + { + "step": "scQuery", + "txId": "get-batch-invalid-id", + "tx": { + "to": "sc:esdt_safe", + "function": "getBatch", + "arguments": [ + "2" + ] + }, + "expect": { + "out": [] + } + } + ] +} diff --git a/esdt-safe/mandos/setup_accounts.scen.json b/esdt-safe/mandos/setup_accounts.scen.json new file mode 100644 index 00000000..c6e77b27 --- /dev/null +++ b/esdt-safe/mandos/setup_accounts.scen.json @@ -0,0 +1,174 @@ +{ + "name": "setup accounts", + "steps": [ + { + "step": "externalSteps", + "path": "../../price-aggregator/mandos/get_latest_price_feed.scen.json" + }, + { + "step": "setState", + "accounts": { + "address:owner": { + "nonce": "0", + "balance": "0", + "storage": {} + }, + "address:user1": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "2,000,000" + }, + "storage": {} + }, + "address:user2": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "2,000,000" + }, + "storage": {} + } + }, + "newAddresses": [ + { + "creatorAddress": "address:owner", + "creatorNonce": "0", + "newAddress": "sc:esdt_safe" + } + ] + }, + { + "step": "scDeploy", + "txId": "deploy", + "tx": { + "from": "address:owner", + "contractCode": "file:../output/esdt-safe.wasm", + "value": "0", + "arguments": [ + "sc:price_aggregator", + "150,000" + ], + "gasLimit": "20,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "add-token-1", + "tx": { + "from": "address:owner", + "to": "sc:esdt_safe", + "value": "0", + "function": "addTokenToWhitelist", + "arguments": [ + "str:BRIDGE-123456", + "str:BRIDGE" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "storage": { + "str:feeEstimatorContractAddress": "sc:price_aggregator", + "str:maxTxBatchSize": "10", + "str:maxTxBatchBlockDuration": "100", + "str:firstBatchId": "1", + "str:lastBatchId": "1", + "str:ethTxGasLimit": "150,000", + "str:tokenTicker|nested:str:BRIDGE-123456": "str:BRIDGE", + "str:tokenTicker|nested:str:GWEI": "str:GWEI", + "str:tokenWhitelist.index|nested:str:BRIDGE-123456": "1", + "str:pause_module:paused": "true", + "+": "" + }, + "code": "file:../output/esdt-safe.wasm" + }, + "+": {} + } + }, + { + "step": "scCall", + "txId": "unpause", + "tx": { + "from": "address:owner", + "to": "sc:esdt_safe", + "function": "unpause", + "arguments": [], + "gasLimit": "100,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "out": [], + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "setState", + "comment": "setting local burn role", + "accounts": { + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": { + "balance": "0", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:feeEstimatorContractAddress": "sc:price_aggregator", + "str:maxTxBatchSize": "10", + "str:maxTxBatchBlockDuration": "100", + "str:firstBatchId": "1", + "str:lastBatchId": "1", + "str:ethTxGasLimit": "150,000", + "str:tokenTicker|nested:str:BRIDGE-123456": "str:BRIDGE", + "str:tokenTicker|nested:str:GWEI": "str:GWEI", + "str:tokenWhitelist.index|nested:str:BRIDGE-123456": "1", + "str:tokenWhitelist.item|u32:1": "str:BRIDGE-123456", + "str:tokenWhitelist.len": "1" + }, + "code": "file:../output/esdt-safe.wasm", + "owner": "address:owner" + } + } + }, + { + "step": "scQuery", + "txId": "get-all-known-tokens", + "tx": { + "to": "sc:esdt_safe", + "function": "getAllKnownTokens" + }, + "expect": { + "out": [ + "str:BRIDGE-123456" + ] + } + } + ] +} \ No newline at end of file diff --git a/esdt-safe/mandos/zero_fees.scen.json b/esdt-safe/mandos/zero_fees.scen.json new file mode 100644 index 00000000..f5d6d36d --- /dev/null +++ b/esdt-safe/mandos/zero_fees.scen.json @@ -0,0 +1,121 @@ +{ + "name": "test zero fees, by setting fee estimator address to zero, and reading default from storage", + "steps": [ + { + "step": "externalSteps", + "path": "setup_accounts.scen.json" + }, + { + "step": "scCall", + "txId": "set-fee-estimator-address-to-address-zero", + "tx": { + "from": "address:owner", + "to": "sc:esdt_safe", + "value": "0", + "function": "setFeeEstimatorContractAddress", + "arguments": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "gasLimit": "30,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": { + "balance": "0", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:feeEstimatorContractAddress": "0x0000000000000000000000000000000000000000000000000000000000000000", + "+": "" + }, + "code": "file:../output/esdt-safe.wasm" + }, + "+": {} + } + }, + { + "step": "scCall", + "txId": "create-transaction-ok-no-fees", + "tx": { + "from": "address:user1", + "to": "sc:esdt_safe", + "esdt": { + "tokenIdentifier": "str:BRIDGE-123456", + "value": "1,500,400" + }, + "function": "createTransaction", + "arguments": [ + "0x0102030405060708091011121314151617181920" + ], + "gasLimit": "60,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "out": [], + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:user1": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "499,600" + }, + "storage": {} + }, + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": { + "balance": "1,500,400", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:pendingBatches|u64:1|str:.item|u32:1": { + "1-block_nonce": "u64:0", + "2-nonce": "u64:1", + "3-from": "u32:32|address:user1", + "4-to": "u32:20|0x0102030405060708091011121314151617181920", + "5-token_identifier": "nested:str:BRIDGE-123456", + "6-amount": "biguint:1,500,400", + "7-is_refund_tx": "u8:0" + }, + "str:firstBatchId": "1", + "str:lastBatchId": "1", + "str:accumulatedTransactionFees|nested:str:BRIDGE-123456": "0", + "+": "" + }, + "code": "file:../output/esdt-safe.wasm" + }, + "+": {} + } + } + ] +} diff --git a/esdt-safe/meta/Cargo.toml b/esdt-safe/meta/Cargo.toml new file mode 100644 index 00000000..a16eeefd --- /dev/null +++ b/esdt-safe/meta/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "esdt-safe-meta" +version = "0.0.0" +authors = ["you"] +edition = "2018" +publish = false + +[dev-dependencies] +[dependencies.esdt-safe] +path = ".." + +[dependencies.multiversx-sc-meta] +version = "0.41.3" diff --git a/esdt-safe/meta/src/main.rs b/esdt-safe/meta/src/main.rs new file mode 100644 index 00000000..232dc09e --- /dev/null +++ b/esdt-safe/meta/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + multiversx_sc_meta::cli_main::(); +} diff --git a/esdt-safe/multiversx.json b/esdt-safe/multiversx.json new file mode 100644 index 00000000..73655396 --- /dev/null +++ b/esdt-safe/multiversx.json @@ -0,0 +1,3 @@ +{ + "language": "rust" +} \ No newline at end of file diff --git a/esdt-safe/src/lib.rs b/esdt-safe/src/lib.rs new file mode 100644 index 00000000..30269c30 --- /dev/null +++ b/esdt-safe/src/lib.rs @@ -0,0 +1,284 @@ +#![no_std] +#![allow(non_snake_case)] + +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +use core::convert::TryFrom; + +use eth_address::*; +use fee_estimator_module::GWEI_STRING; +use transaction::{transaction_status::TransactionStatus, Transaction}; + +const DEFAULT_MAX_TX_BATCH_SIZE: usize = 10; +const DEFAULT_MAX_TX_BATCH_BLOCK_DURATION: u64 = 100; // ~10 minutes + +#[multiversx_sc::contract] +pub trait EsdtSafe: + fee_estimator_module::FeeEstimatorModule + + token_module::TokenModule + + tx_batch_module::TxBatchModule + + max_bridged_amount_module::MaxBridgedAmountModule + + multiversx_sc_modules::pause::PauseModule +{ + /// fee_estimator_contract_address - The address of a Price Aggregator contract, + /// which will get the price of token A in token B + /// + /// eth_tx_gas_limit - The gas limit that will be used for transactions on the ETH side. + /// Will be used to compute the fees for the transfer + #[init] + fn init(&self, fee_estimator_contract_address: ManagedAddress, eth_tx_gas_limit: BigUint) { + self.fee_estimator_contract_address() + .set(&fee_estimator_contract_address); + self.eth_tx_gas_limit().set(ð_tx_gas_limit); + + self.max_tx_batch_size() + .set_if_empty(DEFAULT_MAX_TX_BATCH_SIZE); + self.max_tx_batch_block_duration() + .set_if_empty(DEFAULT_MAX_TX_BATCH_BLOCK_DURATION); + + // batch ID 0 is considered invalid + self.first_batch_id().set_if_empty(1); + self.last_batch_id().set_if_empty(1); + + // set ticker for "GWEI" + let gwei_token_id = TokenIdentifier::from(GWEI_STRING); + self.token_ticker(&gwei_token_id) + .set(gwei_token_id.as_managed_buffer()); + + self.set_paused(true); + } + + /// Sets the statuses for the transactions, after they were executed on the Ethereum side. + /// + /// Only TransactionStatus::Executed (3) and TransactionStatus::Rejected (4) values are allowed. + /// Number of provided statuses must be equal to number of transactions in the batch. + #[only_owner] + #[endpoint(setTransactionBatchStatus)] + fn set_transaction_batch_status( + &self, + batch_id: u64, + tx_statuses: MultiValueEncoded, + ) { + let first_batch_id = self.first_batch_id().get(); + require!( + batch_id == first_batch_id, + "Batches must be processed in order" + ); + + let mut tx_batch = self.pending_batches(batch_id); + require!( + tx_batch.len() == tx_statuses.len(), + "Invalid number of statuses provided" + ); + + for (tx, tx_status) in tx_batch.iter().zip(tx_statuses.to_vec().iter()) { + // Since tokens don't exist in the EsdtSafe in the case of a refund transaction + // we have no tokens to burn, nor to refund + if tx.is_refund_tx { + continue; + } + + match tx_status { + TransactionStatus::Executed => { + // local burn role might be removed while tx is executed + // tokens will remain locked forever in that case + // otherwise, the whole batch would fail + if self.is_local_role_set(&tx.token_identifier, &EsdtLocalRole::Burn) { + self.burn_esdt_token(&tx.token_identifier, &tx.amount); + } + } + TransactionStatus::Rejected => { + let addr = ManagedAddress::try_from(tx.from).unwrap(); + self.mark_refund(&addr, &tx.token_identifier, &tx.amount); + } + _ => { + sc_panic!("Transaction status may only be set to Executed or Rejected"); + } + } + + self.set_status_event(batch_id, tx.nonce, tx_status); + } + + self.clear_first_batch(&mut tx_batch); + } + + /// Converts failed Ethereum -> Elrond transactions to Elrond -> Ethereum transaction. + /// This is done every now and then to refund the tokens. + /// + /// As with normal Elrond -> Ethereum transactions, a part of the tokens will be + /// subtracted to pay for the fees + #[only_owner] + #[endpoint(addRefundBatch)] + fn add_refund_batch(&self, refund_transactions: ManagedVec>) { + let block_nonce = self.blockchain().get_block_nonce(); + let mut cached_token_ids = ManagedVec::::new(); + let mut cached_prices = ManagedVec::::new(); + let mut new_transactions = ManagedVec::new(); + let mut original_tx_nonces = ManagedVec::::new(); + + for refund_tx in &refund_transactions { + let required_fee = match cached_token_ids + .iter() + .position(|id| *id == refund_tx.token_identifier) + { + Some(index) => (*cached_prices.get(index)).clone(), + None => { + let queried_fee = self.calculate_required_fee(&refund_tx.token_identifier); + cached_token_ids.push(refund_tx.token_identifier.clone()); + cached_prices.push(queried_fee.clone()); + + queried_fee + } + }; + + if refund_tx.amount <= required_fee { + continue; + } + + let actual_bridged_amount = refund_tx.amount - required_fee; + let tx_nonce = self.get_and_save_next_tx_id(); + + // "from" and "to" are inverted, since this was initially an Ethereum -> Elrond tx + let new_tx = Transaction { + block_nonce, + nonce: tx_nonce, + from: refund_tx.to, + to: refund_tx.from, + token_identifier: refund_tx.token_identifier, + amount: actual_bridged_amount, + is_refund_tx: true, + }; + new_transactions.push(new_tx); + original_tx_nonces.push(refund_tx.nonce); + } + + let batch_ids = self.add_multiple_tx_to_batch(&new_transactions); + for (i, tx) in new_transactions.iter().enumerate() { + let batch_id = batch_ids.get(i); + let original_tx_nonce = original_tx_nonces.get(i); + + self.add_refund_transaction_event(batch_id, tx.nonce, original_tx_nonce); + } + } + + // endpoints + + /// Create an Elrond -> Ethereum transaction. Only fungible tokens are accepted. + /// + /// Every transfer will have a part of the tokens subtracted as fees. + /// The fee amount depends on the global eth_tx_gas_limit + /// and the current GWEI price, respective to the bridged token + /// + /// fee_amount = price_per_gas_unit * eth_tx_gas_limit + #[payable("*")] + #[endpoint(createTransaction)] + fn create_transaction(&self, to: EthAddress) { + require!(self.not_paused(), "Cannot create transaction while paused"); + + let (payment_token, payment_amount) = self.call_value().single_fungible_esdt(); + self.require_token_in_whitelist(&payment_token); + + let required_fee = self.calculate_required_fee(&payment_token); + require!( + required_fee < payment_amount, + "Transaction fees cost more than the entire bridged amount" + ); + + self.require_below_max_amount(&payment_token, &payment_amount); + + self.accumulated_transaction_fees(&payment_token) + .update(|fees| *fees += &required_fee); + + let actual_bridged_amount = payment_amount - required_fee; + let caller = self.blockchain().get_caller(); + let tx_nonce = self.get_and_save_next_tx_id(); + let tx = Transaction { + block_nonce: self.blockchain().get_block_nonce(), + nonce: tx_nonce, + from: caller.as_managed_buffer().clone(), + to: to.as_managed_buffer().clone(), + token_identifier: payment_token, + amount: actual_bridged_amount, + is_refund_tx: false, + }; + + let batch_id = self.add_to_batch(tx); + self.create_transaction_event(batch_id, tx_nonce); + } + + /// Claim funds for failed Elrond -> Ethereum transactions. + /// These are not sent automatically to prevent the contract getting stuck. + /// For example, if the receiver is a SC, a frozen account, etc. + #[endpoint(claimRefund)] + fn claim_refund(&self, token_id: TokenIdentifier) -> EsdtTokenPayment { + let caller = self.blockchain().get_caller(); + let refund_amount = self.refund_amount(&caller, &token_id).get(); + require!(refund_amount > 0, "Nothing to refund"); + + self.refund_amount(&caller, &token_id).clear(); + self.send() + .direct_esdt(&caller, &token_id, 0, &refund_amount); + + EsdtTokenPayment::new(token_id, 0, refund_amount) + } + + /// Query function that lists all refund amounts for a user. + /// Useful for knowing which token IDs to pass to the claimRefund endpoint. + #[view(getRefundAmounts)] + fn get_refund_amounts( + &self, + address: ManagedAddress, + ) -> MultiValueEncoded> { + let mut refund_amounts = MultiValueEncoded::new(); + for token_id in self.token_whitelist().iter() { + let amount = self.refund_amount(&address, &token_id).get(); + if amount > 0u32 { + refund_amounts.push((token_id, amount).into()); + } + } + + refund_amounts + } + + // private + + fn burn_esdt_token(&self, token_id: &TokenIdentifier, amount: &BigUint) { + self.send().esdt_local_burn(token_id, 0, amount); + } + + fn mark_refund(&self, to: &ManagedAddress, token_id: &TokenIdentifier, amount: &BigUint) { + self.refund_amount(to, token_id) + .update(|refund| *refund += amount); + } + + // events + + #[event("createTransactionEvent")] + fn create_transaction_event(&self, #[indexed] batch_id: u64, #[indexed] tx_id: u64); + + #[event("addRefundTransactionEvent")] + fn add_refund_transaction_event( + &self, + #[indexed] batch_id: u64, + #[indexed] tx_id: u64, + #[indexed] original_tx_id: u64, + ); + + #[event("setStatusEvent")] + fn set_status_event( + &self, + #[indexed] batch_id: u64, + #[indexed] tx_id: u64, + #[indexed] tx_status: TransactionStatus, + ); + + // storage + + #[storage_mapper("refundAmount")] + fn refund_amount( + &self, + address: &ManagedAddress, + token_id: &TokenIdentifier, + ) -> SingleValueMapper; +} diff --git a/esdt-safe/testnet.toml b/esdt-safe/testnet.toml new file mode 100644 index 00000000..e69de29b diff --git a/esdt-safe/tests/scenario_go_test.rs b/esdt-safe/tests/scenario_go_test.rs new file mode 100644 index 00000000..205306a5 --- /dev/null +++ b/esdt-safe/tests/scenario_go_test.rs @@ -0,0 +1,69 @@ +#[test] +fn claim_fees_go() { + multiversx_sc_scenario::run_go("mandos/distribute_fees.scen.json"); +} + +#[test] +fn create_another_tx_ok_go() { + multiversx_sc_scenario::run_go("mandos/create_another_tx_ok.scen.json"); +} + +#[test] +fn create_another_tx_too_late_for_batch_go() { + multiversx_sc_scenario::run_go("mandos/create_another_tx_too_late_for_batch.scen.json"); +} + +#[test] +fn create_transaction_ok_go() { + multiversx_sc_scenario::run_go("mandos/create_transaction_ok.scen.json"); +} + +#[test] +fn execute_batch_both_rejected_go() { + multiversx_sc_scenario::run_go("mandos/execute_batch_both_rejected.scen.json"); +} + +#[test] +fn execute_batch_both_success_go() { + multiversx_sc_scenario::run_go("mandos/execute_batch_both_success.scen.json"); +} + +#[test] +fn execute_batch_one_success_one_rejected_go() { + multiversx_sc_scenario::run_go("mandos/execute_batch_one_success_one_rejected.scen.json"); +} + +#[test] +fn execute_transaction_rejected_go() { + multiversx_sc_scenario::run_go("mandos/execute_transaction_rejected.scen.json"); +} + +#[test] +fn execute_transaction_success_go() { + multiversx_sc_scenario::run_go("mandos/execute_transaction_success.scen.json"); +} + +#[test] +fn get_next_pending_tx_go() { + multiversx_sc_scenario::run_go("mandos/get_next_pending_tx.scen.json"); +} + +#[test] +fn get_next_tx_batch_go() { + multiversx_sc_scenario::run_go("mandos/get_next_tx_batch.scen.json"); +} + +#[test] +fn get_next_tx_batch_too_early_go() { + multiversx_sc_scenario::run_go("mandos/get_next_tx_batch_too_early.scen.json"); +} + +#[test] +fn setup_accounts_go() { + multiversx_sc_scenario::run_go("mandos/setup_accounts.scen.json"); +} + +#[test] +fn zero_fees_go() { + multiversx_sc_scenario::run_go("mandos/zero_fees.scen.json"); +} diff --git a/esdt-safe/wasm/Cargo.toml b/esdt-safe/wasm/Cargo.toml new file mode 100644 index 00000000..d49dabc8 --- /dev/null +++ b/esdt-safe/wasm/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "esdt-safe-wasm" +version = "0.0.0" +authors = ["you"] +edition = "2018" +publish = false + +[lib] +crate-type = ["cdylib"] + +[workspace] +members = ["."] + +[dev-dependencies] +[profile.release] +codegen-units = 1 +opt-level = "z" +lto = true +debug = false +panic = "abort" +[dependencies.esdt-safe] +path = ".." + +[dependencies.multiversx-sc-wasm-adapter] +version = "0.41.3" diff --git a/esdt-safe/wasm/src/lib.rs b/esdt-safe/wasm/src/lib.rs new file mode 100644 index 00000000..bb0d544d --- /dev/null +++ b/esdt-safe/wasm/src/lib.rs @@ -0,0 +1,55 @@ +// Code generated by the multiversx-sc multi-contract system. DO NOT EDIT. + +//////////////////////////////////////////////////// +////////////////// AUTO-GENERATED ////////////////// +//////////////////////////////////////////////////// + +// Init: 1 +// Endpoints: 31 +// Async Callback (empty): 1 +// Total number of exported functions: 33 + +#![no_std] +#![feature(alloc_error_handler, lang_items)] + +multiversx_sc_wasm_adapter::allocator!(); +multiversx_sc_wasm_adapter::panic_handler!(); + +multiversx_sc_wasm_adapter::endpoints! { + esdt_safe + ( + setTransactionBatchStatus + addRefundBatch + createTransaction + claimRefund + getRefundAmounts + setFeeEstimatorContractAddress + setEthTxGasLimit + setDefaultPricePerGasUnit + setTokenTicker + calculateRequiredFee + getFeeEstimatorContractAddress + getDefaultPricePerGasUnit + getEthTxGasLimit + distributeFees + addTokenToWhitelist + removeTokenFromWhitelist + getAllKnownTokens + getAccumulatedTransactionFees + setMaxTxBatchSize + setMaxTxBatchBlockDuration + getCurrentTxBatch + getFirstBatchAnyStatus + getBatch + getBatchStatus + getFirstBatchId + getLastBatchId + setMaxBridgedAmount + getMaxBridgedAmount + pause + unpause + isPaused + ) +} + +multiversx_sc_wasm_adapter::empty_callback! {} diff --git a/multi-transfer-esdt/.gitignore b/multi-transfer-esdt/.gitignore new file mode 100644 index 00000000..9494cb14 --- /dev/null +++ b/multi-transfer-esdt/.gitignore @@ -0,0 +1,7 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +*/target/ + +# The mxpy output +output diff --git a/multi-transfer-esdt/Cargo.toml b/multi-transfer-esdt/Cargo.toml new file mode 100644 index 00000000..18c9455e --- /dev/null +++ b/multi-transfer-esdt/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "multi-transfer-esdt" +version = "0.0.0" +authors = ["you"] +edition = "2018" +publish = false + +[lib] +path = "src/lib.rs" +[dependencies.transaction] +path = "../common/transaction" + +[dependencies.tx-batch-module] +path = "../common/tx-batch-module" + +[dependencies.max-bridged-amount-module] +path = "../common/max-bridged-amount-module" + +[dependencies.bridged-tokens-wrapper] +path = "../bridged-tokens-wrapper" + +[dependencies.multiversx-sc] +version = "0.41.3" +[dev-dependencies.multiversx-sc-scenario] +version = "0.41.3" diff --git a/neversea/README.md b/multi-transfer-esdt/README.md similarity index 100% rename from neversea/README.md rename to multi-transfer-esdt/README.md diff --git a/multi-transfer-esdt/interaction/snippets.sh b/multi-transfer-esdt/interaction/snippets.sh new file mode 100644 index 00000000..32996d89 --- /dev/null +++ b/multi-transfer-esdt/interaction/snippets.sh @@ -0,0 +1,66 @@ +ALICE="/home/elrond/elrond-sdk/mxpy/testnet/wallets/users/alice.pem" +BOB="/home/elrond/elrond-sdk/mxpy/testnet/wallets/users/bob.pem" +ADDRESS=$(mxpy data load --key=address-testnet-multi-transfer-esdt) +DEPLOY_TRANSACTION=$(mxpy data load --key=deployTransaction-testnet) +PROXY=https://testnet-gateway.elrond.com +CHAIN_ID=T + +ALICE_ADDRESS=0x0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1 +BOB_ADDRESS=0x8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8 + +######################################################################## +################## Update after issuing the tokens ##################### +######################################################################## +WRAPPED_EGLD_TOKEN_ID=0x +WRAPPED_ETH_TOKEN_ID=0x + +deploy() { + mxpy --verbose contract deploy --project=${PROJECT} \ + --arguments ${WRAPPED_EGLD_TOKEN_ID} ${WRAPPED_ETH_TOKEN_ID} \ + --recall-nonce --pem=${ALICE} --gas-limit=100000000 --send \ + --outfile="deploy-testnet.interaction.json" --proxy=${PROXY} --chain=${CHAIN_ID} || return + + TRANSACTION=$(mxpy data parse --file="deploy-testnet.interaction.json" --expression="data['emitted_tx']['hash']") + ADDRESS=$(mxpy data parse --file="deploy-testnet.interaction.json" --expression="data['emitted_tx']['address']") + + mxpy data store --key=address-testnet --value=${ADDRESS} + mxpy data store --key=deployTransaction-testnet-multi-transfer-esdt --value=${TRANSACTION} + + echo "" + echo "Smart contract address: ${ADDRESS}" +} + +upgrade() { + mxpy --verbose contract upgrade ${ADDRESS} --project=${PROJECT} --recall-nonce --pem=${ALICE} \ + --gas-limit=100000000 --send --outfile="upgrade.json" --proxy=${PROXY} --chain=${CHAIN_ID} || return +} + +setLocalRolesWrappedEgld() { + local LOCAL_MINT_ROLE=0x45534454526f6c654c6f63616c4d696e74 # "ESDTRoleLocalMint" + local ADDRESS_HEX = $(mxpy wallet bech32 --decode ${ADDRESS}) + + mxpy --verbose contract call ${ESDT_SYSTEM_SC_ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=60000000 --function="setSpecialRole" \ + --arguments ${WRAPPED_EGLD_TOKEN_ID} ${ADDRESS_HEX} ${LOCAL_MINT_ROLE} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +setLocalRolesWrappedEth() { + local LOCAL_MINT_ROLE=0x45534454526f6c654c6f63616c4d696e74 # "ESDTRoleLocalMint" + local ADDRESS_HEX = $(mxpy wallet bech32 --decode ${ADDRESS}) + + mxpy --verbose contract call ${ESDT_SYSTEM_SC_ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=60000000 --function="setSpecialRole" \ + --arguments ${WRAPPED_ETH_TOKEN_ID} ${ADDRESS} ${LOCAL_MINT_ROLE} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +transferEsdtToken() { + local DEST_ADDRESS = BOB_ADDRESS + local TOKEN_ID = WRAPPED_ETH_TOKEN_ID + local AMOUNT = 0x05 + + mxpy --verbose contract call ${ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=10000000 --function="transferEsdtToken" \ + --arguments ${DEST_ADDRESS} ${TOKEN_ID} ${AMOUNT} --send --proxy=${PROXY} --chain=${CHAIN_ID} +} diff --git a/multi-transfer-esdt/mandos/batch_transfer_both_executed.scen.json b/multi-transfer-esdt/mandos/batch_transfer_both_executed.scen.json new file mode 100644 index 00000000..e64ad857 --- /dev/null +++ b/multi-transfer-esdt/mandos/batch_transfer_both_executed.scen.json @@ -0,0 +1,55 @@ +{ + "name": "batch transfer both executed", + "steps": [ + { + "step": "externalSteps", + "path": "setup_accounts.scen.json" + }, + { + "step": "scCall", + "txId": "batch-transfer-both-executed", + "tx": { + "from": "address:owner", + "to": "sc:multi_transfer_esdt", + "value": "0", + "function": "batchTransferEsdtToken", + "arguments": [ + "1", + "0x0102030405060708091011121314151617181920|address:user1|nested:str:BRIDGE-123456|biguint:100,200|u64:1", + "0x0102030405060708091011121314151617181920|address:user2|nested:str:WRAPPED-123456|biguint:500|u64:2" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:user1": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "100,200" + }, + "storage": {} + }, + "address:user2": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:WRAPPED-123456": "500" + }, + "storage": {} + }, + "+": {} + } + } + ] +} diff --git a/multi-transfer-esdt/mandos/batch_transfer_both_failed.scen.json b/multi-transfer-esdt/mandos/batch_transfer_both_failed.scen.json new file mode 100644 index 00000000..a9b34ac0 --- /dev/null +++ b/multi-transfer-esdt/mandos/batch_transfer_both_failed.scen.json @@ -0,0 +1,109 @@ +{ + "name": "batch transfer both failed, trying to send to smart contract", + "steps": [ + { + "step": "externalSteps", + "path": "setup_accounts.scen.json" + }, + { + "step": "scCall", + "txId": "batch-transfer-both-failed", + "tx": { + "from": "address:owner", + "to": "sc:multi_transfer_esdt", + "value": "0", + "function": "batchTransferEsdtToken", + "arguments": [ + "1", + "0x0102030405060708091011121314151617181920|sc:multi_transfer_esdt|nested:str:BRIDGE-123456|biguint:100,200|u64:1", + "0x0102030405060708091011121314151617181920|sc:multi_transfer_esdt|nested:str:WRAPPED-123456|biguint:100,500|u64:2" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "scQuery", + "txId": "get-current-refund-tx-batch", + "tx": { + "to": "sc:multi_transfer_esdt", + "function": "getFirstBatchAnyStatus", + "arguments": [] + }, + "expect": { + "out": [ + "1", + + "0", + "1", + "0x0102030405060708091011121314151617181920", + "sc:multi_transfer_esdt", + "str:BRIDGE-123456", + "100,200", + + "0", + "2", + "0x0102030405060708091011121314151617181920", + "sc:multi_transfer_esdt", + "str:WRAPPED-123456", + "100,500" + ] + } + }, + { + "step": "scCall", + "txId": "clear-refund-batch", + "tx": { + "from": "address:owner", + "to": "sc:multi_transfer_esdt", + "value": "0", + "function": "getAndClearFirstRefundBatch", + "arguments": [], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [ + "1", + + "0", + "1", + "0x0102030405060708091011121314151617181920", + "sc:multi_transfer_esdt", + "str:BRIDGE-123456", + "100,200", + + "0", + "2", + "0x0102030405060708091011121314151617181920", + "sc:multi_transfer_esdt", + "str:WRAPPED-123456", + "100,500" + ], + "gas": "*", + "refund": "*" + } + }, + { + "step": "scQuery", + "txId": "get-current-refund-tx-batch-after-clear", + "tx": { + "to": "sc:multi_transfer_esdt", + "function": "getFirstBatchAnyStatus", + "arguments": [] + }, + "expect": { + "out": [] + } + } + ] +} diff --git a/multi-transfer-esdt/mandos/batch_transfer_one_executed_one_failed.scen.json b/multi-transfer-esdt/mandos/batch_transfer_one_executed_one_failed.scen.json new file mode 100644 index 00000000..7ee4cb31 --- /dev/null +++ b/multi-transfer-esdt/mandos/batch_transfer_one_executed_one_failed.scen.json @@ -0,0 +1,68 @@ +{ + "name": "batch transfer one executed, one failed", + "steps": [ + { + "step": "externalSteps", + "path": "setup_accounts.scen.json" + }, + { + "step": "scCall", + "txId": "batch-transfer-one-executed-one-failed", + "tx": { + "from": "address:owner", + "to": "sc:multi_transfer_esdt", + "value": "0", + "function": "batchTransferEsdtToken", + "arguments": [ + "1", + "0x0102030405060708091011121314151617181920|address:user1|nested:str:BRIDGE-123456|biguint:100,200|u64:1", + "0x0102030405060708091011121314151617181920|sc:multi_transfer_esdt|nested:str:WRAPPED-123456|biguint:500|u64:2" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:user1": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "100,200" + }, + "storage": {} + }, + "+": {} + } + }, + { + "step": "scQuery", + "txId": "get-current-refund-tx-batch", + "tx": { + "to": "sc:multi_transfer_esdt", + "function": "getFirstBatchAnyStatus", + "arguments": [] + }, + "expect": { + "out": [ + "1", + + "0", + "2", + "0x0102030405060708091011121314151617181920", + "sc:multi_transfer_esdt", + "str:WRAPPED-123456", + "500" + ] + } + } + ] +} diff --git a/multi-transfer-esdt/mandos/batch_transfer_to_frozen_account.scen.json b/multi-transfer-esdt/mandos/batch_transfer_to_frozen_account.scen.json new file mode 100644 index 00000000..53ffbc12 --- /dev/null +++ b/multi-transfer-esdt/mandos/batch_transfer_to_frozen_account.scen.json @@ -0,0 +1,87 @@ +{ + "name": "batch transfer one executed, one failed", + "steps": [ + { + "step": "externalSteps", + "path": "setup_accounts.scen.json" + }, + { + "step": "setState", + "accounts": { + "address:frozen_user": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": { + "instances": [ + { + "nonce": "0", + "balance": "200" + } + ], + "frozen": "true" + } + } + } + } + }, + { + "step": "scCall", + "txId": "batch-transfer-to-frozen-account", + "tx": { + "from": "address:owner", + "to": "sc:multi_transfer_esdt", + "value": "0", + "function": "batchTransferEsdtToken", + "arguments": [ + "1", + "0x0102030405060708091011121314151617181920|address:user1|nested:str:BRIDGE-123456|biguint:100,200|u64:1", + "0x0102030405060708091011121314151617181920|address:frozen_user|nested:str:BRIDGE-123456|biguint:500|u64:2" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:user1": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "100,200" + }, + "storage": {} + }, + "+": {} + } + }, + { + "step": "scQuery", + "txId": "get-current-refund-tx-batch", + "tx": { + "to": "sc:multi_transfer_esdt", + "function": "getFirstBatchAnyStatus", + "arguments": [] + }, + "expect": { + "out": [ + "1", + "0", + "2", + "0x0102030405060708091011121314151617181920", + "address:frozen_user", + "str:BRIDGE-123456", + "500" + ] + } + } + ] +} \ No newline at end of file diff --git a/multi-transfer-esdt/mandos/batch_transfer_with_wrapping.scen.json b/multi-transfer-esdt/mandos/batch_transfer_with_wrapping.scen.json new file mode 100644 index 00000000..07bb451c --- /dev/null +++ b/multi-transfer-esdt/mandos/batch_transfer_with_wrapping.scen.json @@ -0,0 +1,164 @@ +{ + "name": "batch transfer both executed", + "steps": [ + { + "step": "externalSteps", + "path": "../../bridged-tokens-wrapper/mandos/whitelist_token.scen.json" + }, + { + "step": "setState", + "accounts": { + "address:owner": { + "nonce": "0", + "balance": "0", + "storage": {} + }, + "address:user1": { + "nonce": "0", + "balance": "0", + "storage": {} + }, + "address:user2": { + "nonce": "0", + "balance": "0", + "storage": {} + } + }, + "newAddresses": [ + { + "creatorAddress": "address:owner", + "creatorNonce": "0", + "newAddress": "sc:multi_transfer_esdt" + } + ] + }, + { + "step": "scDeploy", + "txId": "deploy", + "tx": { + "from": "address:owner", + "contractCode": "file:../output/multi-transfer-esdt.wasm", + "value": "0", + "arguments": [], + "gasLimit": "20,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "setState", + "comment": "setting local mint role", + "accounts": { + "sc:multi_transfer_esdt": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": { + "balance": "0", + "roles": [ + "ESDTRoleLocalMint" + ] + }, + "str:USDC-aaaaaa": { + "balance": "0", + "roles": [ + "ESDTRoleLocalMint" + ] + }, + "str:USDC-cccccc": { + "balance": "0", + "roles": [ + "ESDTRoleLocalMint" + ] + } + }, + "storage": { + "str:maxTxBatchSize": "10", + "str:maxTxBatchBlockDuration": "3,600", + "str:firstBatchId": "1", + "str:lastBatchId": "1", + "str:wrappingContractAddress": "sc:bridged_tokens_wrapper" + }, + "code": "file:../output/multi-transfer-esdt.wasm", + "owner": "address:owner" + } + } + }, + { + "step": "scCall", + "txId": "batch-transfer-both-executed", + "tx": { + "from": "address:owner", + "to": "sc:multi_transfer_esdt", + "value": "0", + "function": "batchTransferEsdtToken", + "arguments": [ + "1", + "0x0102030405060708091011121314151617181920|address:user1|nested:str:BRIDGE-123456|biguint:100,200|u64:1", + "0x0102030405060708091011121314151617181920|address:user2|nested:str:USDC-aaaaaa|biguint:500|u64:2", + "0x0102030405060708091011121314151617181920|address:user1|nested:str:USDC-cccccc|biguint:1000|u64:3" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": "*", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:user1": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "100,200", + "str:WUSDC-abcdef": "1,000" + }, + "storage": {} + }, + "address:user2": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:WUSDC-abcdef": "500" + }, + "storage": {} + }, + "sc:bridged_tokens_wrapper": { + "nonce": "0", + "esdt": { + "str:WUSDC-abcdef": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:WUSDC-uvwxyz": { + "balance": "1", + "roles": [ + "ESDTRoleLocalMint", + "ESDTRoleLocalBurn" + ] + }, + "str:USDC-aaaaaa": "500", + "str:USDC-cccccc": "1,000" + }, + "storage": "*", + "code": "*" + }, + "+": {} + } + } + ] +} diff --git a/multi-transfer-esdt/mandos/setup_accounts.scen.json b/multi-transfer-esdt/mandos/setup_accounts.scen.json new file mode 100644 index 00000000..37240de8 --- /dev/null +++ b/multi-transfer-esdt/mandos/setup_accounts.scen.json @@ -0,0 +1,82 @@ +{ + "name": "setup accounts", + "steps": [ + { + "step": "setState", + "accounts": { + "address:owner": { + "nonce": "0", + "balance": "0", + "storage": {} + }, + "address:user1": { + "nonce": "0", + "balance": "0", + "storage": {} + }, + "address:user2": { + "nonce": "0", + "balance": "0", + "storage": {} + } + }, + "newAddresses": [ + { + "creatorAddress": "address:owner", + "creatorNonce": "0", + "newAddress": "sc:multi_transfer_esdt" + } + ] + }, + { + "step": "scDeploy", + "txId": "deploy", + "tx": { + "from": "address:owner", + "contractCode": "file:../output/multi-transfer-esdt.wasm", + "value": "0", + "arguments": [], + "gasLimit": "20,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "setState", + "comment": "setting local mint role", + "accounts": { + "sc:multi_transfer_esdt": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": { + "balance": "0", + "roles": [ + "ESDTRoleLocalMint" + ] + }, + "str:WRAPPED-123456": { + "balance": "0", + "roles": [ + "ESDTRoleLocalMint" + ] + } + }, + "storage": { + "str:maxTxBatchSize": "10", + "str:maxTxBatchBlockDuration": "3,600", + "str:firstBatchId": "1", + "str:lastBatchId": "1" + }, + "code": "file:../output/multi-transfer-esdt.wasm", + "owner": "address:owner" + } + } + } + ] +} diff --git a/multi-transfer-esdt/mandos/transfer_ok.scen.json b/multi-transfer-esdt/mandos/transfer_ok.scen.json new file mode 100644 index 00000000..85911b69 --- /dev/null +++ b/multi-transfer-esdt/mandos/transfer_ok.scen.json @@ -0,0 +1,46 @@ +{ + "name": "transfer ok", + "steps": [ + { + "step": "externalSteps", + "path": "setup_accounts.scen.json" + }, + { + "step": "scCall", + "txId": "transfer-ok", + "tx": { + "from": "address:owner", + "to": "sc:multi_transfer_esdt", + "value": "0", + "function": "batchTransferEsdtToken", + "arguments": [ + "1", + "0x0102030405060708091011121314151617181920|address:user1|nested:str:BRIDGE-123456|biguint:100,200|u64:1" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:user1": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "100,200" + }, + "storage": {} + }, + "+": "" + } + } + ] +} diff --git a/multi-transfer-esdt/mandos/two_transfers_same_token.scen.json b/multi-transfer-esdt/mandos/two_transfers_same_token.scen.json new file mode 100644 index 00000000..4799f41f --- /dev/null +++ b/multi-transfer-esdt/mandos/two_transfers_same_token.scen.json @@ -0,0 +1,47 @@ +{ + "name": "two transfers, same token, both executed", + "steps": [ + { + "step": "externalSteps", + "path": "setup_accounts.scen.json" + }, + { + "step": "scCall", + "txId": "batch-transfer-both-executed", + "tx": { + "from": "address:owner", + "to": "sc:multi_transfer_esdt", + "value": "0", + "function": "batchTransferEsdtToken", + "arguments": [ + "1", + "0x0102030405060708091011121314151617181920|address:user1|nested:str:BRIDGE-123456|biguint:100,200|u64:1", + "0x0102030405060708091011121314151617181920|address:user1|nested:str:BRIDGE-123456|biguint:100,200|u64:2" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:user1": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:BRIDGE-123456": "200,400" + }, + "storage": {} + }, + "+": {} + } + } + ] +} diff --git a/multi-transfer-esdt/meta/Cargo.toml b/multi-transfer-esdt/meta/Cargo.toml new file mode 100644 index 00000000..9a489dd5 --- /dev/null +++ b/multi-transfer-esdt/meta/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "multi-transfer-esdt-meta" +version = "0.0.0" +authors = ["you"] +edition = "2018" +publish = false + +[dev-dependencies] +[dependencies.multi-transfer-esdt] +path = ".." + +[dependencies.multiversx-sc-meta] +version = "0.41.3" diff --git a/multi-transfer-esdt/meta/src/main.rs b/multi-transfer-esdt/meta/src/main.rs new file mode 100644 index 00000000..224a78da --- /dev/null +++ b/multi-transfer-esdt/meta/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + multiversx_sc_meta::cli_main::(); +} diff --git a/multi-transfer-esdt/multiversx.json b/multi-transfer-esdt/multiversx.json new file mode 100644 index 00000000..73655396 --- /dev/null +++ b/multi-transfer-esdt/multiversx.json @@ -0,0 +1,3 @@ +{ + "language": "rust" +} \ No newline at end of file diff --git a/multi-transfer-esdt/src/lib.rs b/multi-transfer-esdt/src/lib.rs new file mode 100644 index 00000000..015d9e42 --- /dev/null +++ b/multi-transfer-esdt/src/lib.rs @@ -0,0 +1,208 @@ +#![no_std] + +multiversx_sc::imports!(); + +use transaction::{EthTransaction, PaymentsVec, Transaction, TxBatchSplitInFields}; + +const DEFAULT_MAX_TX_BATCH_SIZE: usize = 10; +const DEFAULT_MAX_TX_BATCH_BLOCK_DURATION: u64 = u64::MAX; + +#[multiversx_sc::contract] +pub trait MultiTransferEsdt: + tx_batch_module::TxBatchModule + max_bridged_amount_module::MaxBridgedAmountModule +{ + #[init] + fn init(&self, opt_wrapping_contract_address: OptionalValue) { + self.max_tx_batch_size() + .set_if_empty(DEFAULT_MAX_TX_BATCH_SIZE); + self.max_tx_batch_block_duration() + .set_if_empty(DEFAULT_MAX_TX_BATCH_BLOCK_DURATION); + + self.set_wrapping_contract_address(opt_wrapping_contract_address); + + // batch ID 0 is considered invalid + self.first_batch_id().set_if_empty(1); + self.last_batch_id().set_if_empty(1); + } + + #[only_owner] + #[endpoint(batchTransferEsdtToken)] + fn batch_transfer_esdt_token( + &self, + batch_id: u64, + transfers: MultiValueEncoded>, + ) { + let mut valid_payments_list = ManagedVec::new(); + let mut valid_dest_addresses_list = ManagedVec::new(); + let mut refund_tx_list = ManagedVec::new(); + + let own_sc_address = self.blockchain().get_sc_address(); + let sc_shard = self.blockchain().get_shard_of_address(&own_sc_address); + + for eth_tx in transfers { + let mut must_refund = false; + if eth_tx.to.is_zero() || self.blockchain().is_smart_contract(ð_tx.to) { + self.transfer_failed_invalid_destination(batch_id, eth_tx.tx_nonce); + must_refund = true; + } else if !self.is_local_role_set(ð_tx.token_id, &EsdtLocalRole::Mint) { + self.transfer_failed_invalid_token(batch_id, eth_tx.tx_nonce); + must_refund = true; + } else if self.is_above_max_amount(ð_tx.token_id, ð_tx.amount) { + self.transfer_over_max_amount(batch_id, eth_tx.tx_nonce); + must_refund = true; + } else if self.is_account_same_shard_frozen(sc_shard, ð_tx.to, ð_tx.token_id) { + self.transfer_failed_frozen_destination_account(batch_id, eth_tx.tx_nonce); + must_refund = true; + } + + if must_refund { + let refund_tx = self.convert_to_refund_tx(eth_tx); + refund_tx_list.push(refund_tx); + + continue; + } + + self.send() + .esdt_local_mint(ð_tx.token_id, 0, ð_tx.amount); + + // emit event before the actual transfer so we don't have to save the tx_nonces as well + self.transfer_performed_event(batch_id, eth_tx.tx_nonce); + + valid_dest_addresses_list.push(eth_tx.to); + valid_payments_list.push(EsdtTokenPayment::new(eth_tx.token_id, 0, eth_tx.amount)); + } + + let payments_after_wrapping = self.wrap_tokens(valid_payments_list); + self.distribute_payments(valid_dest_addresses_list, payments_after_wrapping); + + self.add_multiple_tx_to_batch(&refund_tx_list); + } + + #[only_owner] + #[endpoint(getAndClearFirstRefundBatch)] + fn get_and_clear_first_refund_batch(&self) -> OptionalValue> { + let opt_current_batch = self.get_first_batch_any_status(); + if matches!(opt_current_batch, OptionalValue::Some(_)) { + let first_batch_id = self.first_batch_id().get(); + let mut first_batch = self.pending_batches(first_batch_id); + + self.clear_first_batch(&mut first_batch); + } + + opt_current_batch + } + + #[only_owner] + #[endpoint(setWrappingContractAddress)] + fn set_wrapping_contract_address(&self, opt_new_address: OptionalValue) { + match opt_new_address { + OptionalValue::Some(sc_addr) => { + require!( + self.blockchain().is_smart_contract(&sc_addr), + "Invalid unwrapping contract address" + ); + + self.wrapping_contract_address().set(&sc_addr); + } + OptionalValue::None => self.wrapping_contract_address().clear(), + } + } + + // private + + fn convert_to_refund_tx(&self, eth_tx: EthTransaction) -> Transaction { + Transaction { + block_nonce: self.blockchain().get_block_nonce(), + nonce: eth_tx.tx_nonce, + from: eth_tx.from.as_managed_buffer().clone(), + to: eth_tx.to.as_managed_buffer().clone(), + token_identifier: eth_tx.token_id, + amount: eth_tx.amount, + is_refund_tx: true, + } + } + + fn is_local_role_set(&self, token_id: &TokenIdentifier, role: &EsdtLocalRole) -> bool { + let roles = self.blockchain().get_esdt_local_roles(token_id); + + roles.has_role(role) + } + + fn is_account_same_shard_frozen( + &self, + sc_shard: u32, + dest_address: &ManagedAddress, + token_id: &TokenIdentifier, + ) -> bool { + let dest_shard = self.blockchain().get_shard_of_address(dest_address); + if sc_shard != dest_shard { + return false; + } + + let token_data = self + .blockchain() + .get_esdt_token_data(dest_address, token_id, 0); + token_data.frozen + } + + fn wrap_tokens(&self, payments: PaymentsVec) -> PaymentsVec { + if self.wrapping_contract_address().is_empty() { + return payments; + } + + self.get_wrapping_contract_proxy_instance() + .wrap_tokens() + .with_multi_token_transfer(payments) + .execute_on_dest_context() + } + + fn distribute_payments( + &self, + dest_addresses: ManagedVec, + payments: PaymentsVec, + ) { + for (dest, p) in dest_addresses.iter().zip(payments.iter()) { + self.send() + .direct_esdt(&dest, &p.token_identifier, 0, &p.amount); + } + } + + // proxies + + #[proxy] + fn wrapping_contract_proxy( + &self, + sc_address: ManagedAddress, + ) -> bridged_tokens_wrapper::Proxy; + + fn get_wrapping_contract_proxy_instance(&self) -> bridged_tokens_wrapper::Proxy { + self.wrapping_contract_proxy(self.wrapping_contract_address().get()) + } + + // storage + + #[view(getWrappingContractAddress)] + #[storage_mapper("wrappingContractAddress")] + fn wrapping_contract_address(&self) -> SingleValueMapper; + + // events + + #[event("transferPerformedEvent")] + fn transfer_performed_event(&self, #[indexed] batch_id: u64, #[indexed] tx_id: u64); + + #[event("transferFailedInvalidDestination")] + fn transfer_failed_invalid_destination(&self, #[indexed] batch_id: u64, #[indexed] tx_id: u64); + + #[event("transferFailedInvalidToken")] + fn transfer_failed_invalid_token(&self, #[indexed] batch_id: u64, #[indexed] tx_id: u64); + + #[event("transferFailedFrozenDestinationAccount")] + fn transfer_failed_frozen_destination_account( + &self, + #[indexed] batch_id: u64, + #[indexed] tx_id: u64, + ); + + #[event("transferOverMaxAmount")] + fn transfer_over_max_amount(&self, #[indexed] batch_id: u64, #[indexed] tx_id: u64); +} diff --git a/multi-transfer-esdt/testnet.toml b/multi-transfer-esdt/testnet.toml new file mode 100644 index 00000000..e69de29b diff --git a/multi-transfer-esdt/tests/scenario_go_test.rs b/multi-transfer-esdt/tests/scenario_go_test.rs new file mode 100644 index 00000000..cdda9c2b --- /dev/null +++ b/multi-transfer-esdt/tests/scenario_go_test.rs @@ -0,0 +1,34 @@ +#[test] +fn batch_transfer_both_executed_go() { + multiversx_sc_scenario::run_go("mandos/batch_transfer_both_executed.scen.json"); +} + +#[test] +fn batch_transfer_both_failed_go() { + multiversx_sc_scenario::run_go("mandos/batch_transfer_both_failed.scen.json"); +} + +#[test] +fn batch_transfer_one_executed_one_failed_go() { + multiversx_sc_scenario::run_go("mandos/batch_transfer_one_executed_one_failed.scen.json"); +} + +#[test] +fn batch_transfer_to_frozen_account_go() { + multiversx_sc_scenario::run_go("mandos/batch_transfer_to_frozen_account.scen.json"); +} + +#[test] +fn setup_accounts_go() { + multiversx_sc_scenario::run_go("mandos/setup_accounts.scen.json"); +} + +#[test] +fn transfer_ok_go() { + multiversx_sc_scenario::run_go("mandos/transfer_ok.scen.json"); +} + +#[test] +fn two_transfers_same_token_go() { + multiversx_sc_scenario::run_go("mandos/two_transfers_same_token.scen.json"); +} diff --git a/multi-transfer-esdt/wasm/Cargo.lock b/multi-transfer-esdt/wasm/Cargo.lock new file mode 100644 index 00000000..fa0324f9 --- /dev/null +++ b/multi-transfer-esdt/wasm/Cargo.lock @@ -0,0 +1,325 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", + "version_check", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bridged-tokens-wrapper" +version = "0.0.0" +dependencies = [ + "multiversx-sc", + "multiversx-sc-modules", + "transaction", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + +[[package]] +name = "eth-address" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "max-bridged-amount-module" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "multi-transfer-esdt" +version = "0.0.0" +dependencies = [ + "bridged-tokens-wrapper", + "max-bridged-amount-module", + "multiversx-sc", + "transaction", + "tx-batch-module", +] + +[[package]] +name = "multi-transfer-esdt-wasm" +version = "0.0.0" +dependencies = [ + "multi-transfer-esdt", + "multiversx-sc-wasm-adapter", +] + +[[package]] +name = "multiversx-sc" +version = "0.39.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31561133b64837f1a7835ae4ba96383ab07d9ce401e703ad2a37aef45789a1e7" +dependencies = [ + "bitflags", + "hashbrown", + "hex-literal", + "multiversx-sc-codec", + "multiversx-sc-derive", + "num-traits", +] + +[[package]] +name = "multiversx-sc-codec" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7638cb46a0e99c636fd55443ac534ff0a5fad0bd772e1037fbac9a75e04c3c9" +dependencies = [ + "arrayvec", + "multiversx-sc-codec-derive", + "wee_alloc", +] + +[[package]] +name = "multiversx-sc-codec-derive" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e976002d51367f16140929c10ee695f95dd8d34c150a45db60d3fcd1328a267a" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "multiversx-sc-derive" +version = "0.39.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23187464277575f8055c92cffbd664592825591f5cebb8dae038d2af04c5e639" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "radix_trie", + "syn", +] + +[[package]] +name = "multiversx-sc-modules" +version = "0.39.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbcb0b06f37396484f01d89295091db401b8b515ab781e3580a6bb333e912591" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "multiversx-sc-wasm-adapter" +version = "0.39.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af5fee54f1498ec8181593cd54a96ef7436543ad78848e2e805b9b62c27c4095" +dependencies = [ + "multiversx-sc", + "wee_alloc", +] + +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "proc-macro2" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "transaction" +version = "0.0.0" +dependencies = [ + "eth-address", + "multiversx-sc", +] + +[[package]] +name = "tx-batch-module" +version = "0.0.0" +dependencies = [ + "multiversx-sc", + "transaction", +] + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/multi-transfer-esdt/wasm/Cargo.toml b/multi-transfer-esdt/wasm/Cargo.toml new file mode 100644 index 00000000..8dcc8c0f --- /dev/null +++ b/multi-transfer-esdt/wasm/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "multi-transfer-esdt-wasm" +version = "0.0.0" +authors = ["you"] +edition = "2018" +publish = false + +[lib] +crate-type = ["cdylib"] + +[workspace] +members = ["."] + +[dev-dependencies] +[profile.release] +codegen-units = 1 +opt-level = "z" +lto = true +debug = false +panic = "abort" +[dependencies.multi-transfer-esdt] +path = ".." + +[dependencies.multiversx-sc-wasm-adapter] +version = "0.41.3" diff --git a/multi-transfer-esdt/wasm/src/lib.rs b/multi-transfer-esdt/wasm/src/lib.rs new file mode 100644 index 00000000..86ea70f1 --- /dev/null +++ b/multi-transfer-esdt/wasm/src/lib.rs @@ -0,0 +1,38 @@ +// Code generated by the multiversx-sc multi-contract system. DO NOT EDIT. + +//////////////////////////////////////////////////// +////////////////// AUTO-GENERATED ////////////////// +//////////////////////////////////////////////////// + +// Init: 1 +// Endpoints: 14 +// Async Callback (empty): 1 +// Total number of exported functions: 16 + +#![no_std] +#![feature(alloc_error_handler, lang_items)] + +multiversx_sc_wasm_adapter::allocator!(); +multiversx_sc_wasm_adapter::panic_handler!(); + +multiversx_sc_wasm_adapter::endpoints! { + multi_transfer_esdt + ( + batchTransferEsdtToken + getAndClearFirstRefundBatch + setWrappingContractAddress + getWrappingContractAddress + setMaxTxBatchSize + setMaxTxBatchBlockDuration + getCurrentTxBatch + getFirstBatchAnyStatus + getBatch + getBatchStatus + getFirstBatchId + getLastBatchId + setMaxBridgedAmount + getMaxBridgedAmount + ) +} + +multiversx_sc_wasm_adapter::empty_callback! {} diff --git a/multisig/.gitignore b/multisig/.gitignore new file mode 100644 index 00000000..9494cb14 --- /dev/null +++ b/multisig/.gitignore @@ -0,0 +1,7 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +*/target/ + +# The mxpy output +output diff --git a/multisig/Cargo.toml b/multisig/Cargo.toml new file mode 100644 index 00000000..6a8ff88d --- /dev/null +++ b/multisig/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "multisig" +version = "0.0.0" +authors = ["Andrei Marinica , Dorin Iancu "] +edition = "2018" +publish = false + +[lib] +path = "src/lib.rs" +[dependencies.transaction] +path = "../common/transaction" + +[dependencies.eth-address] +path = "../common/eth-address" + +[dependencies.fee-estimator-module] +path = "../common/fee-estimator-module" + +[dependencies.token-module] +path = "../common/token-module" + +[dependencies.tx-batch-module] +path = "../common/tx-batch-module" + +[dependencies.max-bridged-amount-module] +path = "../common/max-bridged-amount-module" + +[dependencies.esdt-safe] +path = "../esdt-safe" + +[dependencies.multi-transfer-esdt] +path = "../multi-transfer-esdt" + +[dependencies.multiversx-sc] +version = "0.41.3" + +[dependencies.multiversx-sc-modules] +version = "0.41.3" +[dev-dependencies.multiversx-sc-scenario] +version = "0.41.3" diff --git a/multisig/README.md b/multisig/README.md new file mode 100644 index 00000000..8f0b223f --- /dev/null +++ b/multisig/README.md @@ -0,0 +1,82 @@ +# Multisig Smart Contract (MSC) + +## Abstract +Cryptocurrencies can be one of the safest ways to store and manage wealth and value. By safeguarding a short list of words, the so-called seed or recovery phrase, anyone can protect thousands or millions of dollars in wealth and rest assured that no hacker or government can take it from them. In practice, it’s never so easy. + +One problem is that single-signature addresses rely on protecting a single private key. + +A better solution would use for example 2-of-3 multisig (or any combination of M-of-N for M ≤ N) quorum consisting of three separate private keys, held by three separate people or entities, and requiring any two to sign. This provides both security and redundancy since compromising any one key/person does not break the quorum: if one key is stolen or lost, the other two keyholders can sweep funds to another address (protected by a new quorum) by mutually signing a transaction moving the funds. + +As an example, let us imagine the following scenario. An institution launches a stablecoin. For safety, it is required that 3 out of 5 designated addresses sign any mint or burn transaction. Alice deploys the multisig SC. She adds Bob, Charlie, Dave and Eve as signers to the contract and sets the quorum to a minimum number of signers to 3. A quorum of signatures is also required to add or remove signers after the initial deployment. If for some reason, Eve’s account is compromised, Alice proposes removing Eve’s address from the signers’ board. Charlie and Dave sign, causing Eve’s address to be removed. There are only 4 addresses now registered in the contract. By the same process, signers could add 2 more addresses to their ranks, and increase the required quorum signatures from 3 to 4. + +Thus, essentially the multisig SC (we will refer to it, from now on, as MSC) enables multiple parties to sign or approve an action that takes place - typically a requirement for certain wallets, accounts, and smart contracts to prevent a rogue or hacked individual from performing detrimental actions. + +## Multisig transaction flow +On-chain multisig wallets are made possible by the fact that smart contracts can call other smart contracts. To execute a multisig transaction the flow would be: + +* A proposer or board member proposes an action. +* The proposed action receives an unique id/hash. +* N board members are notified (off-chain) to review the action with the specific id/hash. +* M out of N board members sign and approve the action. +* Any proposer or board member “performs the action”. + +## Design guidelines + +The required guidelines are: +* **No external contracts.** Calling methods of other contracts from within the methods of your own MSC is an amazing feature but should not be required for our simple use case. This also avoids exposing us to bugs. Because any arbitrarily complex function call can be executed, the MSC functions exactly as a standard wallet, but requires multiple signatures. + +* **No libraries.** Extending the last guideline, our contract has no upstream dependencies other than itself. This minimizes the chance of us misunderstanding or misusing some piece of library code. It also forces us to stay simple and eases auditing and eventually formal verification. + +* **Minimal internal state.** Complex applications can be built inside Elrond smart contracts. Storing minimal internal state allows our contract’s code to be simpler, and to be written in a more functional style, which is easier to test and reason about. + +* **Uses cold-storage.** The proposer which creates an action or spends from the contract has no special rights or access to the MSC. Authorization is handled by directly signing messages by the board members’ wallets that can be hardware wallets (Trezor; Ledger, etc.) or software wallets. + +* **Complete end-to-end testing.** The contract itself is exhaustively unit tested, audited and formally verified. + +## Roles +* **Deployer** - This is the address that deploys the MSC. By default, this address is also the owner of the SC, but the owner can be changed later if required, as this is by default supported by the Elrond protocol itself. This is the address that initially set up the configuration of the SC: board members, quorum, etc. It is important to mention that at deployment a very important configuration parameter is the option to allow the SC to be upgradeable or not. It is recommended for most use cases the SC to be non-upgradeable. Leaving the SC upgradable will give the owner of the SC the possibility to upgrade the SC and bypass the board, defeating the purpose of a MSC. If keeping the SC upgradeable is desired, a possible approach would be to make the owner another MSC, and both SCs could maintain the same board, so an upgrade action would need the approval of the board. + +* **Owner** - The deployer is initially the owner of the MSC, but if desired can be changed later by the current owner to a different owner. If the SC is upgradeable, the owner can also upgrade the SC. + +* **Board and quorum** - Multiple addresses need to be previously registered in the MSC, forming its board. A board member needs to be specifically registered as a board member, meaning for example that the owner or deployer of the MSC is not automatically a board member as well. Board members can vote on every action that the MSC performs. Signing a proposed action means the board members agree. Customarily, not all board members will need to sign every action; the MSC will configure how many signatures will be necessary for an action to be performed, the quorum. For instance, such a contract could have 5 board members, but a quorum of 3 would be enough to perform any action (M-of-N or in this case 3-of-5). + +* **Proposer** - The proposer is an address whitelisted in the MSC that can propose any action. An action can be any transaction; for example: send 10 eGLD to the treasury, mint more ESDT, etc. All board members are proposers by default but non-board members can be added as well to the list of whitelisted proposers. The proposers can only propose actions that then need to be approved and signed by the board members. The board member that proposes an action doesn’t need to sign it anymore; it is considered signed. + +## Functionality +The MSC should be able to perform most tasks that a regular account is able to perform. It should also be as general as possible. This means that it should operate with a generic concept of “Action”, that the board needs to sign before being performed. Actions can interact with the MSC itself (let's call them **internal actions**) or with external addresses or other SC (**external actions**). + +External actions have one and only one function, which is to send the action as a transaction whose sender is the MSC. Because any arbitrarily complex function call can be executed, the MSC functions exactly as a standard wallet, but requires multiple signatures. + +The types of internal actions should be the following: + +* Add a new member to the board. +* Remove a member from the board. This is only allowed if the new board size remains larger than the number of required signatures (quorum). Otherwise, a new member needs to be added first. +* Change the quorum: the required number of signatures. Restriction: 1 <= quorum <= board size. +* Add a proposer. +* Remove a proposer. +* Change multisig contract owner (might be relevant for upgrading the MSC). +* Pay functions - by default we recommend the MSC to not be set up as a payable SC and any deposit or send transaction of eGLD or ESDT towards the MSC will need to call the desired pay function (if a transaction is not a call to these 2 functions then it is rejected immediately and the value is sent back to original sender): Deposit and/or Send. By making the MSC not a payable MSC we reduce the risk of users sending into the MSC funds that then are locked in the MSC or need to be manually send back to the user (in case of a mistake). By making the MSC not a payable MSC it also means that any deposit or send transaction needs to explicitly call the deposit or send function of the MSC. + +Any external and internal action will follow these steps and process: + +* **Propose action:** this will generate an action id. The action id is unique. +* **View action:** the board members need to see the action proposed before they approve it. +* **Sign action:** board members are allowed to sign. We might add an expiration date until board members can sign (until block x…). +* **Un-sign action:** board members are allowed to un-sign, i.e. to remove their signature from an action. Actions with 0 signatures are cleared from storage. This is to allow mistakes to be cleared. +* **Perform action (by id/hash)** - can be activated by proposers or board members. It is successful only if enough signatures are present from the board members. Whoever calls “perform action” needs to provide any eGLD required by the target, as well as to pay for gas. If there is a move balance kind of action, who calls the action pays the gas and the amount to be moved is taken from MSC balance. But the gas is always taken from the balance of the one who creates the "perform action" transaction. + +Also, the following view functions will be available: +* **Count pending Actions:** returns the number of existing Actions. +* **List latest N pending Actions:** provides hashes of the latest N pending Actions, most recent being 0 and oldest being N-1. Usually called in tandem with Count. + +## Initializing the MSC + +There are 2 ways to do it: +* Provide all board member addresses and the number of required signatures directly in the constructor. +* Deployer deploys with just herself on the board and required signatures = 1. Then adds all other N-1 signers and sets required signatures to M. This works, but requires many transactions, so the constructor-only approach might be preferred. + +MSC is a deployable SC written in Rust and compiled in WASM. + +## Conclusion + +Multisig accounts are a critical safety feature for all users of the Elrond ecosystem. Decentralised applications will rely heavily upon multisig security. diff --git a/multisig/interaction/README.md b/multisig/interaction/README.md new file mode 100644 index 00000000..54d72848 --- /dev/null +++ b/multisig/interaction/README.md @@ -0,0 +1,22 @@ +## Using snippets script for the bridge SCs + +### Step 1: Update [configs.cfg](config/configs.cfg) +- Update Alice(owner) and initial relayers' pem location. +- Update any other SC setting that you would like to change. + +### Step 2: Run script file: +Run script.sh with a given command. +Available commands are: +- deploy-aggregator +- deploy-wrapper +- deploy-bridge-contracts +- add-relayer +- remove-relayer +- whitelist-token +- set-safe-max-tx +- set-safe-batch-block-duration +- change-quorum +- pause-contracts +- unpause-contracts + +All the commands that are changing any SC settings will automatically update also [configs.cfg](config/configs.cfg). However, there are some points (like token issueing) when the admin will be ask to first update the configs before proceeding with next steps. \ No newline at end of file diff --git a/multisig/interaction/config/aggregator-snippets.sh b/multisig/interaction/config/aggregator-snippets.sh new file mode 100644 index 00000000..3c12fa92 --- /dev/null +++ b/multisig/interaction/config/aggregator-snippets.sh @@ -0,0 +1,43 @@ +deployAggregator() { + CHECK_VARIABLES AGGREGATOR_WASM CHAIN_SPECIFIC_TOKEN ALICE_ADDRESS + + mxpy --verbose contract deploy --bytecode=${AGGREGATOR_WASM} --recall-nonce --pem=${ALICE} \ + --gas-limit=100000000 --arguments 1 0 ${ALICE_ADDRESS} \ + --send --outfile=deploy-price-agregator-testnet.interaction.json --proxy=${PROXY} --chain=${CHAIN_ID} || return + + TRANSACTION=$(mxpy data parse --file="./deploy-price-agregator-testnet.interaction.json" --expression="data['emittedTransactionHash']") + ADDRESS=$(mxpy data parse --file="./deploy-price-agregator-testnet.interaction.json" --expression="data['contractAddress']") + + mxpy data store --key=address-testnet-safe --value=${ADDRESS} + mxpy data store --key=deployTransaction-testnet --value=${TRANSACTION} + + echo "" + echo "Price agregator: ${ADDRESS}" +} + +submitAggregatorBatch() { + CHECK_VARIABLES AGGREGATOR CHAIN_SPECIFIC_TOKEN FEE_AMOUNT NR_DECIMALS_CHAIN_SPECIFIC + + FEE=$(echo "scale=0; $FEE_AMOUNT*10^$NR_DECIMALS_CHAIN_SPECIFIC/1" | bc) + + mxpy --verbose contract call ${AGGREGATOR} --recall-nonce --pem=${ALICE} \ + --gas-limit=15000000 --function="submitBatch" \ + --arguments str:GWEI str:${CHAIN_SPECIFIC_TOKEN_TICKER} ${FEE} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} || return +} + +pauseAggregator() { + CHECK_VARIABLES AGGREGATOR + + mxpy --verbose contract call ${AGGREGATOR} --recall-nonce --pem=${ALICE} \ + --gas-limit=5000000 --function="pause" \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} || return +} + +unpauseAggregator() { + CHECK_VARIABLES AGGREGATOR + + mxpy --verbose contract call ${AGGREGATOR} --recall-nonce --pem=${ALICE} \ + --gas-limit=5000000 --function="unpause" \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} || return +} diff --git a/multisig/interaction/config/configs.cfg b/multisig/interaction/config/configs.cfg new file mode 100644 index 00000000..9b58c555 --- /dev/null +++ b/multisig/interaction/config/configs.cfg @@ -0,0 +1,79 @@ +ESDT_SYSTEM_SC_ADDRESS=erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u + +#============CHAIN============== + +PROXY=https://testnet-gateway.multiversx.com +CHAIN_ID=T + +#============OWNER============== + +ALICE="./wallets/erd1zsha9cvx7gwraytgp740dcjzwy9v5xwnmas77d33uve6sk0rs0vqel4ln5.pem" +ALICE_ADDRESS=erd1zsha9cvx7gwraytgp740dcjzwy9v5xwnmas77d33uve6sk0rs0vqel4ln5 + +#============WASM FILES============== + +AGGREGATOR_WASM="./price-aggregator.wasm" +SAFE_WASM="./esdt-safe.wasm" +BRIDGED_TOKENS_WRAPPER_WASM="./bridged-tokens-wrapper.wasm" +MULTI_TRANSFER_WASM="./multi-transfer-esdt.wasm" +MULTISIG_WASM="./multisig.wasm" + +#============CONTRACT ADDRESSES============== + +AGGREGATOR=erd1qqqqqqqqqqqqqpgqnlxmcs47kunkewcllqh0d72gv5pjhvfvs0vqa57fhf +SAFE=erd1qqqqqqqqqqqqqpgqkhx64czgpc84eftg6q25lrfyv95ndwtcs0vqwusfuh +BRIDGED_TOKENS_WRAPPER=erd1qqqqqqqqqqqqqpgqlgjwk8mpfycxpdf9q2sgzcndtdhdxr5ss0vqgygjmn +MULTI_TRANSFER=erd1qqqqqqqqqqqqqpgqjsh8kss3w67xks7ths5d795q3nz8y52as0vqu0ujzg +MULTISIG=erd1qqqqqqqqqqqqqpgqdcat402y6c62hv07gt04rynjg4668z9fs0vq3qxepp + +#============TOKENS SETTINGS============== +NR_DECIMALS_CHAIN_SPECIFIC=6 +NR_DECIMALS_UNIVERSAL=6 + +UNIVERSAL_TOKEN_TICKER=USDC +UNIVERSAL_TOKEN_DISPLAY_NAME=WrappedUSDC + +CHAIN_SPECIFIC_TOKEN_TICKER=ETHUSDC +CHAIN_SPECIFIC_TOKEN_DISPLAY_NAME=EthereumWrappedUSDC +UNIVERSAL_TOKENS_ALREADY_MINTED=0 + +#============TOKENS TO BE WHITELISTED============== +UNIVERSAL_TOKEN=USDT-92ae4e +CHAIN_SPECIFIC_TOKEN=ETHUSDT-41b065 +ERC20_TOKEN=0xbe388e5276035575e672ce795a87d16976ecaacb + +#============BRIDGE SETTINGS============== + +FEE_AMOUNT=50 # value without decimals +MAX_AMOUNT=100000 # value without decimals + +RELAYER_REQUIRED_STAKE=0 #1000eGLD +SLASH_AMOUNT=0 +QUORUM=3 + +MAX_TX_PER_BATCH=70 +MAX_TX_BLOCK_DURATION_PER_BATCH=100 #10minutes + +#============RELAYERS ADDRESSES============== +RELAYER_ADDR_0=erd1l9hpewk3dd6mz5j0yytjnzqtydukslmsms2d0fn0pg8pc42chqgqk8pm5u +RELAYER_ADDR_1=erd1tfj6fpr5hd5fw8h6efycw42mk3c4wynausntvg6x98kcyn5npkkqmq7t9g +RELAYER_ADDR_2=erd1689ky73s93aptw8ek0wrrzujtus365ygrqgj0hgj4cn09n5k3kuqyhr0j7 +RELAYER_ADDR_3=erd1uae36y2qvrslexcv3mwjqy9lwrlspvkdyjsar2xcj5ctrq7th26q8twvyc +RELAYER_ADDR_4=erd1pkwhtl85ze02ckaf0t6qgycgx863kxyefxyqxumcth0nwtu3a9yqzg2yn2 +RELAYER_ADDR_5=erd197rmyux8ttzuwssh9thsewlzsc766tvpnussqkpew5vm9x63k0kqw56hr7 +RELAYER_ADDR_6=erd1yhqfhsf237uyz4uzzg0duy9hhg8t3j4wtt020c5cy59w2v2jvkgq6nxf0c +RELAYER_ADDR_7=erd1pgvt06tyhlwegkdkzknyhcxfl3wz69wseqeqashm3d83mxjaewzqnug90w +RELAYER_ADDR_8=erd1ujfl0yxff6ey008ef89au4dn486dj549kpjuuu4xxtn9q550x3gqcu6wed +RELAYER_ADDR_9=erd1zsha9cvx7gwraytgp740dcjzwy9v5xwnmas77d33uve6sk0rs0vqel4ln5 + +RELAYER_WALLET0="./walletsRelay/${RELAYER_ADDR_0}.pem" +RELAYER_WALLET1="./walletsRelay/${RELAYER_ADDR_1}.pem" +RELAYER_WALLET2="./walletsRelay/${RELAYER_ADDR_2}.pem" +RELAYER_WALLET3="./walletsRelay/${RELAYER_ADDR_3}.pem" +RELAYER_WALLET4="./walletsRelay/${RELAYER_ADDR_4}.pem" +RELAYER_WALLET5="./walletsRelay/${RELAYER_ADDR_5}.pem" +RELAYER_WALLET6="./walletsRelay/${RELAYER_ADDR_6}.pem" +RELAYER_WALLET7="./walletsRelay/${RELAYER_ADDR_7}.pem" +RELAYER_WALLET8="./walletsRelay/${RELAYER_ADDR_8}.pem" +RELAYER_WALLET9="./walletsRelay/${RELAYER_ADDR_9}.pem" + diff --git a/multisig/interaction/config/helper.cfg b/multisig/interaction/config/helper.cfg new file mode 100644 index 00000000..0d8d105e --- /dev/null +++ b/multisig/interaction/config/helper.cfg @@ -0,0 +1,74 @@ +CONFIG_FILE=$SCRIPTPATH/config/configs.cfg +function confirmation { + FUNC="$1" + echo -e + read -p "Do you want to go on with ${GREEN}"${FUNC}"${NC} (Default No) ? (Yy/Nn)" yn + echo -e + + case $yn in + [Yy]* ) + + echo -e "${GREEN}Proceeding with "${FUNC}"!${NC}" + ${FUNC} + ;; + [Nn]* ) + echo -e "${GREEN}Exiting...${NC}" + ;; + + * ) + echo -e "${GREEN}I'll take that as a no then... ${NC}" + ;; + esac +} + +function confirmation-with-skip { + FUNC="$1" + echo -e + read -p "Do you want to go on with "${FUNC}" (Default No) ? (Yy/Nn)" yn + echo -e + + case $yn in + [Yy]* ) + + echo -e "${GREEN}Proceeding with "${FUNC}"!${NC}" + ${FUNC} + ;; + [Nn]* ) + ;; + + * ) + echo -e "${GREEN}I'll take that as a no then... ${NC}" + ;; + esac +} + +function continue-confirmation { + FUNC="$1" + echo -e + read -p "Enter any key to continue with "${FUNC}"" yn + echo -e + + ${FUNC} +} + +function manual-update-config-file { + source $SCRIPTPATH/config/configs.cfg +} + +function update-config { + TARGET_KEY=$1 + REPLACEMENT_VALUE=$2 + sed "s/^$TARGET_KEY=.*/$TARGET_KEY=$REPLACEMENT_VALUE/" $CONFIG_FILE > $SCRIPTPATH/config/temp.x + mv $SCRIPTPATH/config/temp.x "$CONFIG_FILE" + source $CONFIG_FILE +} + +CHECK_VARIABLES() +{ + VARIABLE_NAMES=("$@") + for VARIABLE_NAME in "${VARIABLE_NAMES[@]}"; do + [ -z "${!VARIABLE_NAME}" ] && echo "$VARIABLE_NAME variable is unset." && VAR_UNSET=true + done + [ -n "$VAR_UNSET" ] && return 1 + return 0 +} \ No newline at end of file diff --git a/multisig/interaction/config/issue-tokens-snippets.sh b/multisig/interaction/config/issue-tokens-snippets.sh new file mode 100644 index 00000000..b800bcde --- /dev/null +++ b/multisig/interaction/config/issue-tokens-snippets.sh @@ -0,0 +1,60 @@ +ESDT_ISSUE_COST=50000000000000000 + +issueUniversalToken() { + CHECK_VARIABLES ESDT_SYSTEM_SC_ADDRESS ESDT_ISSUE_COST UNIVERSAL_TOKEN_DISPLAY_NAME \ + UNIVERSAL_TOKEN_TICKER NR_DECIMALS_UNIVERSAL + + mxpy --verbose contract call ${ESDT_SYSTEM_SC_ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=60000000 --value=${ESDT_ISSUE_COST} --function="issue" \ + --arguments str:${UNIVERSAL_TOKEN_DISPLAY_NAME} str:${UNIVERSAL_TOKEN_TICKER} \ + 0 ${NR_DECIMALS_UNIVERSAL} str:canAddSpecialRoles str:true \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +issueChainSpecificToken() { + CHECK_VARIABLES ESDT_SYSTEM_SC_ADDRESS ESDT_ISSUE_COST CHAIN_SPECIFIC_TOKEN_DISPLAY_NAME \ + CHAIN_SPECIFIC_TOKEN_TICKER NR_DECIMALS_CHAIN_SPECIFIC UNIVERSAL_TOKENS_ALREADY_MINTED + + VALUE_TO_MINT=$(echo "scale=0; $UNIVERSAL_TOKENS_ALREADY_MINTED*10^$NR_DECIMALS_CHAIN_SPECIFIC/1" | bc) + + mxpy --verbose contract call ${ESDT_SYSTEM_SC_ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=60000000 --value=${ESDT_ISSUE_COST} --function="issue" \ + --arguments str:${CHAIN_SPECIFIC_TOKEN_DISPLAY_NAME} str:${CHAIN_SPECIFIC_TOKEN_TICKER} \ + ${VALUE_TO_MINT} ${NR_DECIMALS_CHAIN_SPECIFIC} str:canAddSpecialRoles str:true \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +transferToSC() { + CHECK_VARIABLES BRIDGED_TOKENS_WRAPPER CHAIN_SPECIFIC_TOKEN + + VALUE_TO_MINT=$(echo "scale=0; $UNIVERSAL_TOKENS_ALREADY_MINTED*10^$NR_DECIMALS_CHAIN_SPECIFIC/1" | bc) + + mxpy --verbose contract call ${BRIDGED_TOKENS_WRAPPER} --recall-nonce --pem=${ALICE} \ + --gas-limit=5000000 --function="ESDTTransfer" \ + --arguments str:${CHAIN_SPECIFIC_TOKEN} ${VALUE_TO_MINT} str:depositLiquidity \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +setMintRole() { + mxpy --verbose contract call ${ESDT_SYSTEM_SC_ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=60000000 --function="setSpecialRole" \ + --arguments str:${CHAIN_SPECIFIC_TOKEN} ${ALICE_ADDRESS} str:ESDTRoleLocalMint \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +unSetMintRole() { + mxpy --verbose contract call ${ESDT_SYSTEM_SC_ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=60000000 --function="unSetSpecialRole" \ + --arguments str:${CHAIN_SPECIFIC_TOKEN} ${ALICE_ADDRESS} str:ESDTRoleLocalMint \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +mint() { + CHECK_VARIABLES NR_DECIMALS_CHAIN_SPECIFIC ALICE_ADDRESS CHAIN_SPECIFIC_TOKEN + read -p "Amount to mint(without decimals): " AMOUNT_TO_MINT + VALUE_TO_MINT=$(echo "scale=0; $AMOUNT_TO_MINT*10^$NR_DECIMALS_CHAIN_SPECIFIC/1" | bc) + mxpy --verbose contract call ${ALICE_ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=300000 --function="ESDTLocalMint" \ + --arguments str:${CHAIN_SPECIFIC_TOKEN} ${VALUE_TO_MINT} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} \ No newline at end of file diff --git a/multisig/interaction/config/menu_functions.cfg b/multisig/interaction/config/menu_functions.cfg new file mode 100644 index 00000000..e7585ccf --- /dev/null +++ b/multisig/interaction/config/menu_functions.cfg @@ -0,0 +1,143 @@ +#!/bin/bash +set -e + +source $SCRIPTPATH/config/aggregator-snippets.sh +source $SCRIPTPATH/config/issue-tokens-snippets.sh +source $SCRIPTPATH/config/multisig-snippets.sh +source $SCRIPTPATH/config/multitransfer-snippets.sh +source $SCRIPTPATH/config/relayers-snippets.sh +source $SCRIPTPATH/config/upgrade-snippets.sh +source $SCRIPTPATH/config/wrapped-snippets.sh +source $SCRIPTPATH/config/safe-snippets.sh + +CHECK_VARIABLES ALICE PROXY CHAIN_ID +function deploy-aggregator { + deployAggregator + update-config AGGREGATOR ${ADDRESS} + confirmation-with-skip unpauseAggregator + + echo -e + echo "Aggregator deployed!" + echo -e +} + +function deploy-wrapper { + deployBridgedTokensWrapper + update-config BRIDGED_TOKENS_WRAPPER ${ADDRESS} +} + +function upgrade-wrapper { + wrapper-upgrade +} + +function upgrade-wrapper-universal-token { + wrapper-updateWrappedToken +} + +function upgrade-wrapper-chain-specific-token { + wrapper-updateWhitelistedToken +} + +function deploy-bridge-contracts { + deploySafe + update-config SAFE ${ADDRESS} + confirmation-with-skip deployMultiTransfer + update-config MULTI_TRANSFER ${ADDRESS} + confirmation-with-skip deployMultisig + update-config MULTISIG ${ADDRESS} + confirmation-with-skip changeChildContractsOwnershipSafe + confirmation-with-skip changeChildContractsOwnershipMultiTransfer +} + +function remove-whitelist-token { + echo -e + echo "Update TOKENS TO BE WHITELISTED section in configs.cfg with the SC" + echo -e + confirmation-with-skip manual-update-config-file + confirmation-with-skip unsetLocalRolesBridgedTokensWrapper + + confirmation-with-skip removeWrappedToken + confirmation-with-skip wrapper-blacklistToken + confirmation-with-skip unsetLocalRolesEsdtSafe + confirmation-with-skip unsetLocalRolesMultiTransferEsdt + confirmation-with-skip clearMapping + confirmation-with-skip removeTokenFromWhitelist +} + +function whitelist-token { + echo -e + echo "PREREQUIREMENTS: BRIDGED_TOKENS_WRAPPER needs to have MINT+BURN role for the UNIVERSAL TOKEN" + echo "Check and update TOKENS SETTINGS section in configs.cfg" + echo -e + confirmation-with-skip manual-update-config-file + + confirmation-with-skip issueUniversalToken + confirmation-with-skip issueChainSpecificToken + + echo -e + echo "Update TOKENS TO BE WHITELISTED section in configs.cfg with the SC" + echo -e + confirmation-with-skip manual-update-config-file + confirmation-with-skip setLocalRolesBridgedTokensWrapper + + confirmation-with-skip transferToSC + confirmation-with-skip addWrappedToken + confirmation-with-skip wrapper-whitelistToken + confirmation-with-skip setLocalRolesEsdtSafe + confirmation-with-skip setLocalRolesMultiTransferEsdt + confirmation-with-skip addMapping + confirmation-with-skip addTokenToWhitelist + echo -e + echo "Update FEE_AMOUNT and MAX_AMOUNT in BRIDGE SETTINGS section in configs.cfg" + echo -e + confirmation-with-skip manual-update-config-file + + confirmation-with-skip submitAggregatorBatch + + confirmation-with-skip esdtSafeSetMaxBridgedAmountForToken + confirmation-with-skip multiTransferEsdtSetMaxBridgedAmountForToken +} + +function change-quorum { + read -p "Quorum: " QUORUM + update-config QUORUM ${QUORUM} + changeQuorum +} + +function set-safe-max-tx { + read -p "New batch size: " BATCH_SIZE + update-config MAX_TX_PER_BATCH ${BATCH_SIZE} + esdtSafeSetMaxTxBatchSize +} + +function set-safe-batch-block-duration { + read -p "New batch block duration: " BLOCK_DURATION + update-config MAX_TX_BLOCK_DURATION_PER_BATCH ${BLOCK_DURATION} + esdtSafeSetMaxTxBatchBlockDuration +} + +function pause-contracts { + confirmation-with-skip pause + confirmation-with-skip pauseEsdtSafe + confirmation-with-skip pauseAggregator + confirmation-with-skip wrapper-pause +} + +function unpause-contracts { + confirmation-with-skip unpause + confirmation-with-skip unpauseEsdtSafe + confirmation-with-skip unpauseAggregator + confirmation-with-skip wrapper-unpause +} + +function set-fee { + confirmation-with-skip submitAggregatorBatch +} + +function mint-chain-specific { + confirmation-with-skip setMintRole + confirmation-with-skip mint + update-config UNIVERSAL_TOKENS_ALREADY_MINTED ${AMOUNT_TO_MINT} + confirmation-with-skip transferToSC + confirmation-with-skip unSetMintRole +} \ No newline at end of file diff --git a/multisig/interaction/config/multisig-snippets.sh b/multisig/interaction/config/multisig-snippets.sh new file mode 100644 index 00000000..2037bfc1 --- /dev/null +++ b/multisig/interaction/config/multisig-snippets.sh @@ -0,0 +1,164 @@ +deployMultisig() { + CHECK_VARIABLES RELAYER_ADDR_0 RELAYER_ADDR_1 RELAYER_ADDR_2 RELAYER_ADDR_3 \ + RELAYER_ADDR_4 RELAYER_ADDR_5 RELAYER_ADDR_6 RELAYER_ADDR_7 RELAYER_ADDR_8 \ + RELAYER_ADDR_9 SAFE MULTI_TRANSFER RELAYER_REQUIRED_STAKE SLASH_AMOUNT QUORUM MULTISIG_WASM + + MIN_STAKE=$(echo "$RELAYER_REQUIRED_STAKE*10^18" | bc) + mxpy --verbose contract deploy --bytecode=${MULTISIG_WASM} --recall-nonce --pem=${ALICE} \ + --gas-limit=200000000 \ + --arguments ${SAFE} ${MULTI_TRANSFER} \ + ${MIN_STAKE} ${SLASH_AMOUNT} ${QUORUM} \ + ${RELAYER_ADDR_0} ${RELAYER_ADDR_1} ${RELAYER_ADDR_2} ${RELAYER_ADDR_3} \ + --send --outfile="deploy-testnet.interaction.json" --proxy=${PROXY} --chain=${CHAIN_ID} || return + + TRANSACTION=$(mxpy data parse --file="./deploy-testnet.interaction.json" --expression="data['emitted_tx']['hash']") + ADDRESS=$(mxpy data parse --file="./deploy-testnet.interaction.json" --expression="data['contractAddress']") + + mxpy data store --key=address-testnet-multisig --value=${ADDRESS} + mxpy data store --key=deployTransaction-testnet --value=${TRANSACTION} + + echo "" + echo "Multisig contract address: ${ADDRESS}" +} + +changeChildContractsOwnershipSafe() { + CHECK_VARIABLES SAFE MULTISIG + + mxpy --verbose contract call ${SAFE} --recall-nonce --pem=${ALICE} \ + --gas-limit=10000000 --function="ChangeOwnerAddress" \ + --arguments ${MULTISIG} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +changeChildContractsOwnershipMultiTransfer() { + CHECK_VARIABLES MULTI_TRANSFER MULTISIG + + mxpy --verbose contract call ${MULTI_TRANSFER} --recall-nonce --pem=${ALICE} \ + --gas-limit=10000000 --function="ChangeOwnerAddress" \ + --arguments ${MULTISIG} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +clearMapping() { + CHECK_VARIABLES ERC20_TOKEN CHAIN_SPECIFIC_TOKEN MULTISIG + + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${ALICE} \ + --gas-limit=40000000 --function="clearMapping" \ + --arguments ${ERC20_TOKEN} str:${CHAIN_SPECIFIC_TOKEN} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +addMapping() { + CHECK_VARIABLES ERC20_TOKEN CHAIN_SPECIFIC_TOKEN MULTISIG + + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${ALICE} \ + --gas-limit=40000000 --function="addMapping" \ + --arguments ${ERC20_TOKEN} str:${CHAIN_SPECIFIC_TOKEN} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +addTokenToWhitelist() { + CHECK_VARIABLES CHAIN_SPECIFIC_TOKEN CHAIN_SPECIFIC_TOKEN_TICKER MULTISIG + + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${ALICE} \ + --gas-limit=60000000 --function="esdtSafeAddTokenToWhitelist" \ + --arguments str:${CHAIN_SPECIFIC_TOKEN} str:${CHAIN_SPECIFIC_TOKEN_TICKER} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +removeTokenFromWhitelist() { + CHECK_VARIABLES CHAIN_SPECIFIC_TOKEN CHAIN_SPECIFIC_TOKEN_TICKER MULTISIG + + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${ALICE} \ + --gas-limit=60000000 --function="esdtSafeRemoveTokenFromWhitelist" \ + --arguments str:${CHAIN_SPECIFIC_TOKEN} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +esdtSafeSetMaxTxBatchSize() { + CHECK_VARIABLES MAX_TX_PER_BATCH MULTISIG + + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${ALICE} \ + --gas-limit=30000000 --function="esdtSafeSetMaxTxBatchSize" \ + --arguments ${MAX_TX_PER_BATCH} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +esdtSafeSetMaxTxBatchBlockDuration() { + CHECK_VARIABLES MAX_TX_BLOCK_DURATION_PER_BATCH MULTISIG + + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${ALICE} \ + --gas-limit=30000000 --function="esdtSafeSetMaxTxBatchBlockDuration" \ + --arguments ${MAX_TX_BLOCK_DURATION_PER_BATCH} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +clearMapping() { + CHECK_VARIABLES ERC20_TOKEN CHAIN_SPECIFIC_TOKEN MULTISIG + + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${ALICE} \ + --gas-limit=40000000 --function="clearMapping" \ + --arguments ${ERC20_TOKEN} str:${CHAIN_SPECIFIC_TOKEN} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +changeQuorum() { + CHECK_VARIABLES QUORUM MULTISIG + + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${ALICE} \ + --gas-limit=40000000 --function="changeQuorum" \ + --arguments ${QUORUM} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +pause() { + CHECK_VARIABLES MULTISIG + + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${ALICE} \ + --gas-limit=40000000 --function="pause" \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +pauseEsdtSafe() { + CHECK_VARIABLES MULTISIG + + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${ALICE} \ + --gas-limit=40000000 --function="pauseEsdtSafe" \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +unpause() { + CHECK_VARIABLES MULTISIG + + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${ALICE} \ + --gas-limit=40000000 --function="unpause" \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +unpauseEsdtSafe() { + CHECK_VARIABLES MULTISIG + + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${ALICE} \ + --gas-limit=40000000 --function="unpauseEsdtSafe" \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +esdtSafeSetMaxBridgedAmountForToken() { + CHECK_VARIABLES MAX_AMOUNT NR_DECIMALS_CHAIN_SPECIFIC CHAIN_SPECIFIC_TOKEN MULTISIG + + MAX=$(echo "scale=0; $MAX_AMOUNT*10^$NR_DECIMALS_CHAIN_SPECIFIC/1" | bc) + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${ALICE} \ + --gas-limit=40000000 --function="esdtSafeSetMaxBridgedAmountForToken" \ + --arguments str:${CHAIN_SPECIFIC_TOKEN} ${MAX} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +multiTransferEsdtSetMaxBridgedAmountForToken() { + CHECK_VARIABLES MAX_AMOUNT NR_DECIMALS_CHAIN_SPECIFIC CHAIN_SPECIFIC_TOKEN MULTISIG + + MAX=$(echo "scale=0; $MAX_AMOUNT*10^$NR_DECIMALS_CHAIN_SPECIFIC/1" | bc) + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${ALICE} \ + --gas-limit=40000000 --function="multiTransferEsdtSetMaxBridgedAmountForToken" \ + --arguments str:${CHAIN_SPECIFIC_TOKEN} ${MAX} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} diff --git a/multisig/interaction/config/multitransfer-snippets.sh b/multisig/interaction/config/multitransfer-snippets.sh new file mode 100644 index 00000000..38304735 --- /dev/null +++ b/multisig/interaction/config/multitransfer-snippets.sh @@ -0,0 +1,32 @@ +deployMultiTransfer() { + CHECK_VARIABLES MULTI_TRANSFER_WASM BRIDGED_TOKENS_WRAPPER + + mxpy --verbose contract deploy --bytecode=${MULTI_TRANSFER_WASM} --recall-nonce --pem=${ALICE} \ + --gas-limit=100000000 \ + --arguments ${BRIDGED_TOKENS_WRAPPER} --metadata-payable \ + --send --outfile="deploy-multitransfer-testnet.interaction.json" --proxy=${PROXY} --chain=${CHAIN_ID} || return + + ADDRESS=$(mxpy data parse --file="./deploy-multitransfer-testnet.interaction.json" --expression="data['contractAddress']") + mxpy data store --key=address-testnet-multitransfer --value=${ADDRESS} + + echo "" + echo "Multi transfer contract address: ${ADDRESS}" +} + +setLocalRolesMultiTransferEsdt() { + CHECK_VARIABLES ESDT_SYSTEM_SC_ADDRESS CHAIN_SPECIFIC_TOKEN MULTI_TRANSFER + + mxpy --verbose contract call ${ESDT_SYSTEM_SC_ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=60000000 --function="setSpecialRole" \ + --arguments str:${CHAIN_SPECIFIC_TOKEN} ${MULTI_TRANSFER} str:ESDTRoleLocalMint \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +unsetLocalRolesMultiTransferEsdt() { + CHECK_VARIABLES ESDT_SYSTEM_SC_ADDRESS CHAIN_SPECIFIC_TOKEN MULTI_TRANSFER + + mxpy --verbose contract call ${ESDT_SYSTEM_SC_ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=60000000 --function="unSetSpecialRole" \ + --arguments str:${CHAIN_SPECIFIC_TOKEN} ${MULTI_TRANSFER} str:ESDTRoleLocalMint \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} \ No newline at end of file diff --git a/multisig/interaction/config/relayers-snippets.sh b/multisig/interaction/config/relayers-snippets.sh new file mode 100644 index 00000000..3528a641 --- /dev/null +++ b/multisig/interaction/config/relayers-snippets.sh @@ -0,0 +1,84 @@ +addBoardMember() { + CHECK_VARIABLES MULTISIG + + read -p "Relayer address: " RELAYER_ADDR + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${ALICE} \ + --gas-limit=35000000 --function="addBoardMember" --arguments ${RELAYER_ADDR} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +removeBoardMember() { + CHECK_VARIABLES MULTISIG + + read -p "Relayer address: " RELAYER_ADDR + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${ALICE} \ + --gas-limit=35000000 --function="removeUser" --arguments ${RELAYER_ADDR} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +unstake() { + CHECK_VARIABLES MULTISIG RELAYER_REQUIRED_STAKE + + read -p "Relayer address: " RELAYER_ADDR + MIN_STAKE=$(echo "$RELAYER_REQUIRED_STAKE*10^18" | bc) + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem="./walletsRelay/${RELAYER_ADDR}.pem" \ + --gas-limit=35000000 --function="unstake" \ + --arguments ${MIN_STAKE} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +stake() { + CHECK_VARIABLES MULTISIG RELAYER_REQUIRED_STAKE \ + RELAYER_WALLET0 RELAYER_WALLET1 RELAYER_WALLET2 RELAYER_WALLET3 RELAYER_WALLET4 \ + RELAYER_WALLET5 RELAYER_WALLET6 RELAYER_WALLET7 RELAYER_WALLET8 RELAYER_WALLET9 + + MIN_STAKE=$(echo "$RELAYER_REQUIRED_STAKE*10^18" | bc) + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${RELAYER_WALLET0} \ + --gas-limit=35000000 --function="stake" --value=${MIN_STAKE} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} + echo "---------------------------------------------------------" + echo "---------------------------------------------------------" + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${RELAYER_WALLET1} \ + --gas-limit=35000000 --function="stake" --value=${MIN_STAKE} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} + echo "---------------------------------------------------------" + echo "---------------------------------------------------------" + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${RELAYER_WALLET2} \ + --gas-limit=35000000 --function="stake" --value=${MIN_STAKE} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} + echo "---------------------------------------------------------" + echo "---------------------------------------------------------" + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${RELAYER_WALLET3} \ + --gas-limit=35000000 --function="stake" --value=${MIN_STAKE} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} + echo "---------------------------------------------------------" + echo "---------------------------------------------------------" + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${RELAYER_WALLET4} \ + --gas-limit=35000000 --function="stake" --value=${MIN_STAKE} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} + echo "---------------------------------------------------------" + echo "---------------------------------------------------------" + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${RELAYER_WALLET5} \ + --gas-limit=35000000 --function="stake" --value=${MIN_STAKE} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} + echo "---------------------------------------------------------" + echo "---------------------------------------------------------" + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${RELAYER_WALLET6} \ + --gas-limit=35000000 --function="stake" --value=${MIN_STAKE} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} + echo "---------------------------------------------------------" + echo "---------------------------------------------------------" + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${RELAYER_WALLET7} \ + --gas-limit=35000000 --function="stake" --value=${MIN_STAKE} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} + echo "---------------------------------------------------------" + echo "---------------------------------------------------------" + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${RELAYER_WALLET8} \ + --gas-limit=35000000 --function="stake" --value=${MIN_STAKE} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} + echo "---------------------------------------------------------" + echo "---------------------------------------------------------" + mxpy --verbose contract call ${MULTISIG} --recall-nonce --pem=${RELAYER_WALLET9} \ + --gas-limit=35000000 --function="stake" --value=${MIN_STAKE} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} \ No newline at end of file diff --git a/multisig/interaction/config/safe-snippets.sh b/multisig/interaction/config/safe-snippets.sh new file mode 100644 index 00000000..636c5cbc --- /dev/null +++ b/multisig/interaction/config/safe-snippets.sh @@ -0,0 +1,35 @@ +deploySafe() { + CHECK_VARIABLES SAFE_WASM AGGREGATOR + + mxpy --verbose contract deploy --bytecode=${SAFE_WASM} --recall-nonce --pem=${ALICE} \ + --gas-limit=150000000 \ + --arguments ${AGGREGATOR} 1 \ + --send --outfile="deploy-safe-testnet.interaction.json" --proxy=${PROXY} --chain=${CHAIN_ID} || return + + TRANSACTION=$(mxpy data parse --file="./deploy-safe-testnet.interaction.json" --expression="data['emittedTransactionHash']") + ADDRESS=$(mxpy data parse --file="./deploy-safe-testnet.interaction.json" --expression="data['contractAddress']") + + mxpy data store --key=address-testnet-safe --value=${ADDRESS} + mxpy data store --key=deployTransaction-testnet --value=${TRANSACTION} + + echo "" + echo "Safe contract address: ${ADDRESS}" +} + +setLocalRolesEsdtSafe() { + CHECK_VARIABLES ESDT_SYSTEM_SC_ADDRESS CHAIN_SPECIFIC_TOKEN SAFE + + mxpy --verbose contract call ${ESDT_SYSTEM_SC_ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=60000000 --function="setSpecialRole" \ + --arguments str:${CHAIN_SPECIFIC_TOKEN} ${SAFE} str:ESDTRoleLocalBurn \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +unsetLocalRolesEsdtSafe() { + CHECK_VARIABLES ESDT_SYSTEM_SC_ADDRESS CHAIN_SPECIFIC_TOKEN SAFE + + mxpy --verbose contract call ${ESDT_SYSTEM_SC_ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=60000000 --function="unSetSpecialRole" \ + --arguments str:${CHAIN_SPECIFIC_TOKEN} ${SAFE} str:ESDTRoleLocalBurn \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} \ No newline at end of file diff --git a/multisig/interaction/config/upgrade-snippets.sh b/multisig/interaction/config/upgrade-snippets.sh new file mode 100644 index 00000000..5eeb077f --- /dev/null +++ b/multisig/interaction/config/upgrade-snippets.sh @@ -0,0 +1,53 @@ +#TODO: check & updates upgrade snippets +deploySafeForUpgrade() { + getAggregatorAddressHex + + local ESDT_SAFE_ETH_TX_GAS_LIMIT=20000 # gives us 200$ for elrond->eth + + mxpy --verbose contract deploy --project=${PROJECT_SAFE} --recall-nonce --pem=${ALICE} \ + --gas-limit=150000000 \ + --arguments 0x${AGGREGATOR_ADDRESS_HEX} ${ESDT_SAFE_ETH_TX_GAS_LIMIT} \ + --send --outfile="deploy-safe-upgrade.interaction.json" --proxy=${PROXY} --chain=${CHAIN_ID} || return + + ADDRESS=$(mxpy data parse --file="./deploy-safe-upgrade.interaction.json" --expression="data['contractAddress']") + + echo "" + echo "Safe contract address: ${ADDRESS}" +} + + +upgradeSafeContract() { + getEsdtSafeAddressHex + getAggregatorAddressHex + local ESDT_SAFE_ETH_TX_GAS_LIMIT=20000 + + local NEW_SAFE_BECH=$(mxpy data parse --file="./deploy-safe-upgrade.interaction.json" --expression="data['contractAddress']") + local NEW_SAFE_ADDR=$(mxpy wallet bech32 --decode $NEW_SAFE_BECH) + + + + mxpy --verbose contract call ${ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=400000000 --function="upgradeChildContractFromSource" \ + --arguments 0x${ESDT_SAFE_ADDRESS_HEX} 0x${NEW_SAFE_ADDR} 0x00 \ + 0x${AGGREGATOR_ADDRESS_HEX} ${ESDT_SAFE_ETH_TX_GAS_LIMIT} \ + --send --outfile="upgradesafe-child-sc-spam.json" --proxy=${PROXY} --chain=${CHAIN_ID} +} + +upgrade() { + mxpy --verbose contract upgrade ${ADDRESS} --project=${PROJECT} --recall-nonce --pem=${ALICE} \ + --gas-limit=100000000 --send --outfile="upgrade.json" --proxy=${PROXY} --chain=${CHAIN_ID} || return +} + +upgradeMultisig() { + getMultiTransferEsdtAddressHex + getEsdtSafeAddressHex + getMultiTransferEsdtAddressHex + + local SLASH_AMOUNT=0x00 # 0 + MIN_STAKE=$(echo "$RELAYER_REQUIRED_STAKE*10^18" | bc) + mxpy --verbose contract upgrade ${ADDRESS} --bytecode=../output/multisig.wasm --recall-nonce --pem=${ALICE} \ + --arguments 0x${ESDT_SAFE_ADDRESS_HEX} 0x${MULTI_TRANSFER_ESDT_ADDRESS_HEX} \ + ${local} ${SLASH_AMOUNT} 0x07 \ + --gas-limit=200000000 --send --outfile="upgrade-multisig.json" --proxy=${PROXY} --chain=${CHAIN_ID} || return + +} \ No newline at end of file diff --git a/multisig/interaction/config/wrapped-snippets.sh b/multisig/interaction/config/wrapped-snippets.sh new file mode 100644 index 00000000..4985d888 --- /dev/null +++ b/multisig/interaction/config/wrapped-snippets.sh @@ -0,0 +1,129 @@ +# 1. deployBridgedTokensWrapper +# 3. setLocalRolesBridgedTokensWrapper # - keep in mind we need to do this with the token owner +# 4. addWrappedToken +# 5. whitelistToken +# If the SC already exists, skip the first step +# If we want to add another chain, do only the last step + +deployBridgedTokensWrapper() { + CHECK_VARIABLES BRIDGED_TOKENS_WRAPPER_WASM + + mxpy --verbose contract deploy --bytecode=${BRIDGED_TOKENS_WRAPPER_WASM} --recall-nonce --pem=${ALICE} \ + --gas-limit=30000000 \ + --send --outfile="deploy-bridged-tokens-wrapper-testnet.interaction.json" --proxy=${PROXY} --chain=${CHAIN_ID} || return + + TRANSACTION=$(mxpy data parse --file="./deploy-bridged-tokens-wrapper-testnet.interaction.json" --expression="data['emittedTransactionHash']") + ADDRESS=$(mxpy data parse --file="./deploy-bridged-tokens-wrapper-testnet.interaction.json" --expression="data['contractAddress']") + + mxpy data store --key=address-testnet-bridged-tokens-wrapper --value=${ADDRESS} + mxpy data store --key=deployTransaction-testnet --value=${TRANSACTION} + + echo "" + echo "Bridged tokens wrapper SC: ${ADDRESS}" +} + +setLocalRolesBridgedTokensWrapper() { + CHECK_VARIABLES ESDT_SYSTEM_SC_ADDRESS UNIVERSAL_TOKEN BRIDGED_TOKENS_WRAPPER + + mxpy --verbose contract call ${ESDT_SYSTEM_SC_ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=60000000 --function="setSpecialRole" \ + --arguments str:${UNIVERSAL_TOKEN} ${BRIDGED_TOKENS_WRAPPER} str:ESDTRoleLocalMint str:ESDTRoleLocalBurn\ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +unsetLocalRolesBridgedTokensWrapper() { + CHECK_VARIABLES ESDT_SYSTEM_SC_ADDRESS UNIVERSAL_TOKEN BRIDGED_TOKENS_WRAPPER + + mxpy --verbose contract call ${ESDT_SYSTEM_SC_ADDRESS} --recall-nonce --pem=${ALICE} \ + --gas-limit=60000000 --function="unSetSpecialRole" \ + --arguments str:${UNIVERSAL_TOKEN} ${BRIDGED_TOKENS_WRAPPER} str:ESDTRoleLocalMint str:ESDTRoleLocalBurn\ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +addWrappedToken() { + CHECK_VARIABLES BRIDGED_TOKENS_WRAPPER UNIVERSAL_TOKEN NR_DECIMALS_UNIVERSAL + + mxpy --verbose contract call ${BRIDGED_TOKENS_WRAPPER} --recall-nonce --pem=${ALICE} \ + --gas-limit=6000000 --function="addWrappedToken" \ + --arguments str:${UNIVERSAL_TOKEN} ${NR_DECIMALS_UNIVERSAL} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +removeWrappedToken() { + CHECK_VARIABLES BRIDGED_TOKENS_WRAPPER UNIVERSAL_TOKEN + + mxpy --verbose contract call ${BRIDGED_TOKENS_WRAPPER} --recall-nonce --pem=${ALICE} \ + --gas-limit=6000000 --function="removeWrappedToken" \ + --arguments str:${UNIVERSAL_TOKEN} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +removeWrappedToken() { + CHECK_VARIABLES BRIDGED_TOKENS_WRAPPER UNIVERSAL_TOKEN + + mxpy --verbose contract call ${BRIDGED_TOKENS_WRAPPER} --recall-nonce --pem=${ALICE} \ + --gas-limit=6000000 --function="removeWrappedToken" \ + --arguments str:${UNIVERSAL_TOKEN} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +wrapper-whitelistToken() { + CHECK_VARIABLES BRIDGED_TOKENS_WRAPPER CHAIN_SPECIFIC_TOKEN NR_DECIMALS_CHAIN_SPECIFIC UNIVERSAL_TOKEN + + mxpy --verbose contract call ${BRIDGED_TOKENS_WRAPPER} --recall-nonce --pem=${ALICE} \ + --gas-limit=6000000 --function="whitelistToken" \ + --arguments str:${CHAIN_SPECIFIC_TOKEN} ${NR_DECIMALS_CHAIN_SPECIFIC} str:${UNIVERSAL_TOKEN} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +wrapper-blacklistToken() { + CHECK_VARIABLES BRIDGED_TOKENS_WRAPPER CHAIN_SPECIFIC_TOKEN UNIVERSAL_TOKEN + + mxpy --verbose contract call ${BRIDGED_TOKENS_WRAPPER} --recall-nonce --pem=${ALICE} \ + --gas-limit=6000000 --function="blacklistToken" \ + --arguments str:${CHAIN_SPECIFIC_TOKEN} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +wrapper-updateWrappedToken() { + CHECK_VARIABLES BRIDGED_TOKENS_WRAPPER UNIVERSAL_TOKEN NR_DECIMALS_UNIVERSAL + + mxpy --verbose contract call ${BRIDGED_TOKENS_WRAPPER} --recall-nonce --pem=${ALICE} \ + --gas-limit=6000000 --function="updateWrappedToken" \ + --arguments str:${UNIVERSAL_TOKEN} ${NR_DECIMALS_UNIVERSAL} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + +wrapper-updateWhitelistedToken() { + CHECK_VARIABLES BRIDGED_TOKENS_WRAPPER CHAIN_SPECIFIC_TOKEN NR_DECIMALS_CHAIN_SPECIFIC + + mxpy --verbose contract call ${BRIDGED_TOKENS_WRAPPER} --recall-nonce --pem=${ALICE} \ + --gas-limit=6000000 --function="updateWhitelistedToken" \ + --arguments str:${CHAIN_SPECIFIC_TOKEN} ${NR_DECIMALS_CHAIN_SPECIFIC} \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} +} + + +wrapper-unpause() { + CHECK_VARIABLES BRIDGED_TOKENS_WRAPPER + + mxpy --verbose contract call ${BRIDGED_TOKENS_WRAPPER} --recall-nonce --pem=${ALICE} \ + --gas-limit=5000000 --function="unpause" \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} || return +} + +wrapper-pause() { + CHECK_VARIABLES BRIDGED_TOKENS_WRAPPER + + mxpy --verbose contract call ${BRIDGED_TOKENS_WRAPPER} --recall-nonce --pem=${ALICE} \ + --gas-limit=5000000 --function="pause" \ + --send --proxy=${PROXY} --chain=${CHAIN_ID} || return +} + +wrapper-upgrade() { + CHECK_VARIABLES BRIDGED_TOKENS_WRAPPER BRIDGED_TOKENS_WRAPPER_WASM + + mxpy --verbose contract upgrade ${BRIDGED_TOKENS_WRAPPER} --bytecode=${BRIDGED_TOKENS_WRAPPER_WASM} --recall-nonce --pem=${ALICE} \ + --gas-limit=50000000 --send \ + --outfile="upgrade-bridged-tokens-wrapper.json" --proxy=${PROXY} --chain=${CHAIN_ID} || return +} \ No newline at end of file diff --git a/multisig/interaction/script.sh b/multisig/interaction/script.sh new file mode 100755 index 00000000..ed684a2a --- /dev/null +++ b/multisig/interaction/script.sh @@ -0,0 +1,102 @@ +#!/bin/bash +set -e + +#Make script aware of its location +SCRIPTPATH="$( cd "$(dirname -- "$0")" ; pwd -P )" + +source $SCRIPTPATH/config/configs.cfg +source $SCRIPTPATH/config/helper.cfg +source $SCRIPTPATH/config/menu_functions.cfg + +case "$1" in +'deploy-aggregator') + confirmation deploy-aggregator + ;; + +'deploy-wrapper') + confirmation deploy-wrapper + ;; + +'upgrade-wrapper') + confirmation upgrade-wrapper + ;; + +'deploy-bridge-contracts') + echo -e + echo "PREREQUIREMENTS: AGGREGATOR & BRIDGED_TOKENS_WRAPPER deployed" + echo -e + confirmation deploy-bridge-contracts + ;; + +'add-relayer') + confirmation addBoardMember + ;; + +'remove-relayer') + confirmation removeBoardMember + ;; + +'whitelist-token') + echo -e + echo "PREREQUIREMENTS: BRIDGED_TOKENS_WRAPPER needs to have MINT+BURN role for the UNIVERSAL TOKEN" + echo "Check and update TOKENS SETTINGS section in configs.cfg" + source $SCRIPTPATH/config/configs.cfg + echo -e + confirmation whitelist-token + ;; + +'remove-whitelist-token') + echo -e + echo "PREREQUIREMENTS: BRIDGED_TOKENS_WRAPPER needs to have MINT+BURN role for the UNIVERSAL TOKEN" + echo "Check and update TOKENS SETTINGS section in configs.cfg" + source $SCRIPTPATH/config/configs.cfg + echo -e + confirmation remove-whitelist-token + ;; + +'set-safe-max-tx') + confirmation set-safe-max-tx + ;; + +'set-safe-batch-block-duration') + confirmation set-safe-batch-block-duration + ;; + +'change-quorum') + confirmation change-quorum + ;; + +'pause-contracts') + confirmation pause-contracts + ;; + +'unpause-contracts') + confirmation unpause-contracts + ;; + +'set-swap-fee') + confirmation set-fee + ;; + +'mint-chain-specific') + confirmation mint-chain-specific + ;; + +'upgrade-wrapper-universal-token') + confirmation upgrade-wrapper-universal-token + ;; + +'upgrade-wrapper-chain-specific-token') + confirmation upgrade-wrapper-chain-specific-token + ;; + +*) + echo "Usage: Invalid choice: '"$1"'" + echo -e + echo "Choose from:" + echo " { \"deploy-aggregator\", \"deploy-wrapper\", \"upgrade-wrapper\", \"deploy-bridge-contracts\", \"add-relayer\", \"remove-relayer\", \"whitelist-token\", " + echo " \"remove-whitelist-token\", \"set-safe-max-tx\", \"set-safe-batch-block-duration\", \"change-quorum\", \"pause-contracts\", \"unpause-contracts\", " + echo " \"set-swap-fee\", \"mint-chain-specific\", \"upgrade-wrapper-universal-token\", \"upgrade-wrapper-chain-specific-token\" }" + ;; + +esac \ No newline at end of file diff --git a/multisig/mandos/change_token_config.scen.json b/multisig/mandos/change_token_config.scen.json new file mode 100644 index 00000000..8a9d0c73 --- /dev/null +++ b/multisig/mandos/change_token_config.scen.json @@ -0,0 +1,249 @@ +{ + "name": "change token config", + "steps": [ + { + "step": "externalSteps", + "path": "setup.scen.json" + }, + { + "step": "scCall", + "txId": "change-fee-estimator-sc-address", + "tx": { + "from": "address:owner", + "to": "sc:multisig", + "value": "0", + "function": "changeFeeEstimatorContractAddress", + "arguments": [ + "sc:egld_esdt_swap" + ], + "gasLimit": "55,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": "*", + "storage": { + "str:feeEstimatorContractAddress": "sc:egld_esdt_swap", + "+": "" + }, + "code": "*" + }, + "+": {} + } + }, + { + "step": "scCall", + "txId": "change-default-price-per-gas-unit", + "tx": { + "from": "address:owner", + "to": "sc:multisig", + "value": "0", + "function": "changeDefaultPricePerGasUnit", + "arguments": [ + "str:ETH-123456", + "100" + ], + "gasLimit": "55,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": "*", + "storage": { + "str:defaultPricePerGasUnit|nested:str:ETH-123456": "100", + "+": "" + }, + "code": "*" + }, + "+": {} + } + }, + { + "step": "scCall", + "txId": "change-token-ticker", + "tx": { + "from": "address:owner", + "to": "sc:multisig", + "value": "0", + "function": "changeTokenTicker", + "arguments": [ + "str:ETH-123456", + "str:WETH" + ], + "gasLimit": "55,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": "*", + "storage": { + "str:tokenTicker|nested:str:ETH-123456": "str:WETH", + "+": "" + }, + "code": "*" + }, + "+": {} + } + }, + { + "step": "scCall", + "txId": "add-mapping-1", + "tx": { + "from": "address:owner", + "to": "sc:multisig", + "value": "0", + "function": "addMapping", + "arguments": [ + "0x0102030405060708091011121314151617181920", + "str:ETH-123456" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "add-mapping-2", + "tx": { + "from": "address:owner", + "to": "sc:multisig", + "value": "0", + "function": "addMapping", + "arguments": [ + "0x0102030405060708091011121314151617181999", + "str:EGLD-123456" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "add-mapping-invalid", + "tx": { + "from": "address:owner", + "to": "sc:multisig", + "value": "0", + "function": "addMapping", + "arguments": [ + "0x0000030405060708091011121314151617181999", + "str:EGLD-123456" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:Mapping already exists for token ID", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "scQuery", + "txId": "query-token-id", + "tx": { + "to": "sc:multisig", + "function": "getTokenIdForErc20Address", + "arguments": [ + "0x0102030405060708091011121314151617181999" + ] + }, + "expect": { + "out": [ + "str:EGLD-123456" + ] + } + }, + { + "step": "scQuery", + "txId": "query-erc20-address", + "tx": { + "to": "sc:multisig", + "function": "getErc20AddressForTokenId", + "arguments": [ + "str:EGLD-123456" + ] + }, + "expect": { + "out": [ + "0x0102030405060708091011121314151617181999" + ] + } + }, + { + "step": "scCall", + "txId": "remove-mapping-invalid", + "tx": { + "from": "address:owner", + "to": "sc:multisig", + "value": "0", + "function": "clearMapping", + "arguments": [ + "0x0102030405060708091011121314151617181920", + "str:EGLD-123456" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:Invalid mapping", + "out": [], + "gas": "*", + "refund": "*" + } + } + ] +} diff --git a/multisig/mandos/create_elrond_to_ethereum_tx_batch.scen.json b/multisig/mandos/create_elrond_to_ethereum_tx_batch.scen.json new file mode 100644 index 00000000..1cd5744f --- /dev/null +++ b/multisig/mandos/create_elrond_to_ethereum_tx_batch.scen.json @@ -0,0 +1,199 @@ +{ + "name": "batch tx from Elrond to Ethereum", + "steps": [ + { + "step": "externalSteps", + "path": "setup.scen.json" + }, + { + "step": "scCall", + "txId": "first-create-transaction", + "tx": { + "from": "address:user", + "to": "sc:esdt_safe", + "esdt": { + "tokenIdentifier": "str:EGLD-123456", + "value": "1,500,400" + }, + "function": "createTransaction", + "arguments": [ + "0x0102030405060708091011121314151617181920" + ], + "gasLimit": "60,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "out": [], + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:user": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:EGLD-123456": "499,600", + "str:ETH-123456": "1,000,000" + }, + "storage": {} + }, + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:EGLD-123456": { + "balance": "1,500,400", + "roles": [ + "ESDTRoleLocalBurn" + ] + }, + "str:ETH-123456": { + "balance": "0", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:pendingBatches|u64:1|str:.item|u32:1": { + "1-block_nonce": "u64:0", + "2-nonce": "u64:1", + "3-from": "u32:32|address:user", + "4-to": "u32:20|0x0102030405060708091011121314151617181920", + "5-token_identifier": "nested:str:EGLD-123456", + "6-amount": "biguint:400", + "7-is_refund_tx": "u8:0" + }, + "str:firstBatchId": "1", + "str:lastBatchId": "1", + "str:accumulatedTransactionFees|nested:str:EGLD-123456": "1,500,000", + "+": "" + }, + "code": "*" + }, + "+": {} + } + }, + { + "step": "scCall", + "txId": "second-create-transaction", + "tx": { + "from": "address:user", + "to": "sc:esdt_safe", + "esdt": { + "tokenIdentifier": "str:ETH-123456", + "value": "500,000" + }, + "function": "createTransaction", + "arguments": [ + "0x0102030405060708091011121314151617181920" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "out": [], + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:user": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:EGLD-123456": "499,600", + "str:ETH-123456": "500,000" + }, + "storage": {} + }, + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:EGLD-123456": { + "balance": "1,500,400", + "roles": [ + "ESDTRoleLocalBurn" + ] + }, + "str:ETH-123456": { + "balance": "500,000", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:pendingBatches|u64:1|str:.item|u32:1": { + "1-block_nonce": "u64:0", + "2-nonce": "u64:1", + "3-from": "u32:32|address:user", + "4-to": "u32:20|0x0102030405060708091011121314151617181920", + "5-token_identifier": "nested:str:EGLD-123456", + "6-amount": "biguint:400", + "7-is_refund_tx": "u8:0" + }, + "str:pendingBatches|u64:1|str:.item|u32:2": { + "1-block_nonce": "u64:0", + "2-nonce": "u64:2", + "3-from": "u32:32|address:user", + "4-to": "u32:20|0x0102030405060708091011121314151617181920", + "5-token_identifier": "nested:str:ETH-123456", + "6-amount": "biguint:350,000", + "7-is_refund_tx": "u8:0" + }, + "str:firstBatchId": "1", + "str:lastBatchId": "1", + "str:accumulatedTransactionFees|nested:str:EGLD-123456": "1,500,000", + "str:accumulatedTransactionFees|nested:str:ETH-123456": "150,000", + "+": "" + }, + "code": "*" + }, + "+": {} + } + }, + { + "step": "setState", + "currentBlockInfo": { + "blockNonce": "500" + } + }, + { + "step": "scQuery", + "txId": "get-current-tx-batch", + "tx": { + "to": "sc:multisig", + "function": "getCurrentTxBatch", + "arguments": [] + }, + "expect": { + "out": [ + "1", + "0", + "1", + "address:user", + "0x0102030405060708091011121314151617181920", + "str:EGLD-123456", + "400", + "0", + "2", + "address:user", + "0x0102030405060708091011121314151617181920", + "str:ETH-123456", + "350,000" + ] + } + } + ] +} diff --git a/multisig/mandos/ethereum_to_elrond_tx_batch_ok.scen.json b/multisig/mandos/ethereum_to_elrond_tx_batch_ok.scen.json new file mode 100644 index 00000000..86fe3a03 --- /dev/null +++ b/multisig/mandos/ethereum_to_elrond_tx_batch_ok.scen.json @@ -0,0 +1,129 @@ +{ + "name": "create ethereum to elrond tx batch", + "steps": [ + { + "step": "externalSteps", + "path": "setup.scen.json" + }, + { + "step": "scCall", + "txId": "propose-transfer-ok", + "tx": { + "from": "address:relayer1", + "to": "sc:multisig", + "value": "0", + "function": "proposeMultiTransferEsdtBatch", + "arguments": [ + "1", + "0x0102030405060708091011121314151617181920", "address:user", "str:EGLD-123456", "500,000", "1", + "0x0102030405060708091011121314151617181920", "address:user", "str:ETH-123456", "500,000", "2" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [ + "1" + ], + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "sc:multisig": { + "nonce": "*", + "balance": "*", + "storage": { + "str:action_data.item|u32:1": { + "1-action_type": "u8:2", + "2-batch_id": "u64:1", + "3-transfers_len": "u32:2", + "4-transfers_vec": [ + { + "1-from": "0x0102030405060708091011121314151617181920", + "2-to": "address:user", + "3-token_id": "nested:str:EGLD-123456", + "4-amount": "biguint:500,000", + "5-tx_nonce": "u64:1" + }, + { + "1-from": "0x0102030405060708091011121314151617181920", + "2-to": "address:user", + "3-token_id": "nested:str:ETH-123456", + "4-amount": "biguint:500,000", + "5-tx_nonce": "u64:2" + } + ] + }, + "+": "" + }, + "code": "*" + }, + "+": {} + } + }, + { + "step": "scCall", + "txId": "second-relayer-sign", + "tx": { + "from": "address:relayer2", + "to": "sc:multisig", + "value": "0", + "function": "sign", + "arguments": [ + "1" + ], + "gasLimit": "35,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "perform-action-transfer", + "tx": { + "from": "address:relayer1", + "to": "sc:multisig", + "value": "0", + "function": "performAction", + "arguments": [ + "1" + ], + "gasLimit": "100,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:user": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:EGLD-123456": "2,500,000", + "str:ETH-123456": "1,500,000" + }, + "storage": {} + }, + "+": {} + } + } + ] +} diff --git a/multisig/mandos/ethereum_to_elrond_tx_batch_rejected.scen.json b/multisig/mandos/ethereum_to_elrond_tx_batch_rejected.scen.json new file mode 100644 index 00000000..f3d8f739 --- /dev/null +++ b/multisig/mandos/ethereum_to_elrond_tx_batch_rejected.scen.json @@ -0,0 +1,295 @@ +{ + "name": "create ethereum to elrond tx batch - rejected", + "steps": [ + { + "step": "externalSteps", + "path": "setup.scen.json" + }, + { + "step": "scCall", + "txId": "try-propose-wrong-batch-id", + "tx": { + "from": "address:relayer1", + "to": "sc:multisig", + "value": "0", + "function": "proposeMultiTransferEsdtBatch", + "arguments": [ + "2", + "0x0102030405060708091011121314151617181920", + "sc:egld_esdt_swap", + "str:EGLD-123456", + "2,000,000", + "u64:1", + "0x0102030405060708091011121314151617181920", + "sc:egld_esdt_swap", + "str:ETH-123456", + "2,000,000", + "u64:2" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:Can only propose for next batch ID", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "try-propose-wrong-tx-id", + "tx": { + "from": "address:relayer1", + "to": "sc:multisig", + "value": "0", + "function": "proposeMultiTransferEsdtBatch", + "arguments": [ + "1", + "0x0102030405060708091011121314151617181920", + "sc:egld_esdt_swap", + "str:EGLD-123456", + "2,000,000", + "u64:2", + "0x0102030405060708091011121314151617181920", + "sc:egld_esdt_swap", + "str:ETH-123456", + "2,000,000", + "u64:3" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:Invalid Tx ID", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "propose-transfer-to-smart-contract", + "tx": { + "from": "address:relayer1", + "to": "sc:multisig", + "value": "0", + "function": "proposeMultiTransferEsdtBatch", + "arguments": [ + "1", + "0x0102030405060708091011121314151617181920", + "sc:egld_esdt_swap", + "str:EGLD-123456", + "2,000,000", + "u64:1", + "0x0102030405060708091011121314151617181920", + "sc:egld_esdt_swap", + "str:ETH-123456", + "2,000,000", + "u64:2" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [ + "1" + ], + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "sc:multisig": { + "nonce": "*", + "balance": "*", + "storage": { + "str:action_data.item|u32:1": { + "1-action_type": "u8:2", + "2-batch_id": "u64:1", + "3-transfers_len": "u32:2", + "4-transfers_vec": [ + { + "1-from": "0x0102030405060708091011121314151617181920", + "2-to": "sc:egld_esdt_swap", + "3-token_id": "nested:str:EGLD-123456", + "4-amount": "biguint:2,000,000", + "5-tx_id": "u64:1" + }, + { + "1-from": "0x0102030405060708091011121314151617181920", + "2-to": "sc:egld_esdt_swap", + "3-token_id": "nested:str:ETH-123456", + "4-amount": "biguint:2,000,000", + "5-tx_id": "u64:2" + } + ] + }, + "+": "" + }, + "code": "*" + }, + "+": {} + } + }, + { + "step": "scCall", + "txId": "second-relayer-sign", + "tx": { + "from": "address:relayer2", + "to": "sc:multisig", + "value": "0", + "function": "sign", + "arguments": [ + "1" + ], + "gasLimit": "35,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "perform-action-transfer", + "comment": "output is from execute_on_dest_context results being propagated to the initial caller", + "tx": { + "from": "address:relayer1", + "to": "sc:multisig", + "value": "0", + "function": "performAction", + "arguments": [ + "1" + ], + "gasLimit": "100,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "scQuery", + "txId": "get-current-refund-tx-batch", + "tx": { + "to": "sc:multisig", + "function": "getCurrentRefundBatch", + "arguments": [] + }, + "expect": { + "out": [ + "1", + "0", + "1", + "0x0102030405060708091011121314151617181920", + "sc:egld_esdt_swap", + "str:EGLD-123456", + "2,000,000", + "0", + "2", + "0x0102030405060708091011121314151617181920", + "sc:egld_esdt_swap", + "str:ETH-123456", + "2,000,000" + ] + } + }, + { + "step": "scCall", + "txId": "move-refund-batch-to-safe", + "tx": { + "from": "address:owner", + "to": "sc:multisig", + "value": "0", + "function": "moveRefundBatchToSafe", + "arguments": [], + "gasLimit": "200,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "scQuery", + "txId": "get-current-refund-tx-batch-after-move", + "tx": { + "to": "sc:multisig", + "function": "getCurrentRefundBatch", + "arguments": [] + }, + "expect": { + "out": [] + } + }, + { + "step": "checkState", + "accounts": { + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:EGLD-123456": { + "balance": "0", + "roles": [ + "ESDTRoleLocalBurn" + ] + }, + "str:ETH-123456": { + "balance": "0", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:pendingBatches|u64:1|str:.item|u32:1": { + "1-block_nonce": "u64:0", + "2-nonce": "u64:1", + "3-from": "u32:32|sc:egld_esdt_swap", + "4-to": "u32:20|0x0102030405060708091011121314151617181920", + "5-token_identifier": "nested:str:EGLD-123456", + "6-amount": "biguint:500,000", + "7-is_refund_tx": "u8:1" + }, + "str:pendingBatches|u64:1|str:.item|u32:2": { + "1-block_nonce": "u64:0", + "2-nonce": "u64:2", + "3-from": "u32:32|sc:egld_esdt_swap", + "4-to": "u32:20|0x0102030405060708091011121314151617181920", + "5-token_identifier": "nested:str:ETH-123456", + "6-amount": "biguint:1,850,000", + "7-is_refund_tx": "u8:1" + }, + "str:firstBatchId": "1", + "str:lastBatchId": "1", + "str:accumulatedTransactionFees|nested:str:EGLD-123456": "0", + "str:accumulatedTransactionFees|nested:str:ETH-123456": "0", + "+": "" + }, + "code": "*" + }, + "+": {} + } + } + ] +} diff --git a/multisig/mandos/execute_elrond_to_ethereum_tx_batch.scen.json b/multisig/mandos/execute_elrond_to_ethereum_tx_batch.scen.json new file mode 100644 index 00000000..77368000 --- /dev/null +++ b/multisig/mandos/execute_elrond_to_ethereum_tx_batch.scen.json @@ -0,0 +1,255 @@ +{ + "name": "execute transactions successfuly", + "steps": [ + { + "step": "externalSteps", + "path": "create_elrond_to_ethereum_tx_batch.scen.json" + }, + { + "step": "scCall", + "txId": "propose-set-tx-batch-executed", + "tx": { + "from": "address:relayer1", + "to": "sc:multisig", + "value": "0", + "function": "proposeEsdtSafeSetCurrentTransactionBatchStatus", + "arguments": [ + "1", + "3", + "3" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [ + "1" + ], + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "sc:multisig": { + "nonce": "*", + "balance": "*", + "storage": { + "str:action_data.item|u32:1": { + "1-action_type": "u8:1", + "2-batch_id": "u64:1", + "3-tx_batch_status_len": "u32:2", + "4-tx_batch_status_vec": "u8:3|u8:3" + }, + "+": "" + }, + "code": "*" + }, + "+": {} + } + }, + { + "step": "scCall", + "txId": "second-relayer-sign", + "tx": { + "from": "address:relayer2", + "to": "sc:multisig", + "value": "0", + "function": "sign", + "arguments": [ + "1" + ], + "gasLimit": "35,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "perform-action-set-tx-batch-executed", + "tx": { + "from": "address:relayer1", + "to": "sc:multisig", + "value": "0", + "function": "performAction", + "arguments": [ + "1" + ], + "gasLimit": "75,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "user-claim-refund-egld", + "tx": { + "from": "address:user", + "to": "sc:esdt_safe", + "value": "0", + "function": "claimRefund", + "arguments": [ + "str:EGLD-123456" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:Nothing to refund", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "user-claim-refund-eth", + "tx": { + "from": "address:user", + "to": "sc:esdt_safe", + "value": "0", + "function": "claimRefund", + "arguments": [ + "str:ETH-123456" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:Nothing to refund", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "comment": "the tokens are burned after successful tx execution", + "accounts": { + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:EGLD-123456": { + "balance": "1,500,000", + "roles": [ + "ESDTRoleLocalBurn" + ] + }, + "str:ETH-123456": { + "balance": "150,000", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:transactionStatus|address:user|u32:1": "", + "str:transactionsByNonce|address:user|str:.item|u32:1": "", + "str:transactionStatus|address:user|u32:2": "", + "str:transactionsByNonce|address:user|str:.item|u32:2": "", + "str:accumulatedTransactionFees|nested:str:EGLD-123456": "1,500,000", + "str:accumulatedTransactionFees|nested:str:ETH-123456": "150,000", + "+": "" + }, + "code": "*" + }, + "+": {} + } + }, + { + "step": "scCall", + "txId": "owner-distribute-fees", + "tx": { + "from": "address:owner", + "to": "sc:multisig", + "value": "0", + "function": "distributeFeesFromChildContracts", + "arguments": [ + "address:relayer1", + "4000", + "address:relayer2", + "6000" + ], + "gasLimit": "200,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:relayer1": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:EGLD-123456": { + "balance": "600,000" + }, + "str:ETH-123456": { + "balance": "60,000" + } + }, + "storage": {} + }, + "address:relayer2": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:EGLD-123456": { + "balance": "900,000" + }, + "str:ETH-123456": { + "balance": "90,000" + } + }, + "storage": {} + }, + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:EGLD-123456": { + "balance": "0", + "roles": [ + "ESDTRoleLocalBurn" + ] + }, + "str:ETH-123456": { + "balance": "0", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:accumulatedTransactionFees|nested:str:EGLD-123456": "0", + "str:accumulatedTransactionFees|nested:str:ETH-123456": "0", + "+": "" + }, + "code": "*" + }, + "+": {} + } + } + ] +} diff --git a/multisig/mandos/get_empty_batch.scen.json b/multisig/mandos/get_empty_batch.scen.json new file mode 100644 index 00000000..faf0914c --- /dev/null +++ b/multisig/mandos/get_empty_batch.scen.json @@ -0,0 +1,43 @@ +{ + "name": "get empty batch test - should return empty and not error", + "steps": [ + { + "step": "externalSteps", + "path": "setup.scen.json" + }, + { + "step": "scCall", + "txId": "get-with-sc-call", + "tx": { + "from": "address:user", + "to": "sc:multisig", + "value": "", + "function": "getCurrentTxBatch", + "arguments": [], + "gasLimit": "100,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "scQuery", + "txId": "get-with-sc-query", + "tx": { + "to": "sc:multisig", + "function": "getCurrentTxBatch", + "arguments": [] + }, + "expect": { + "status": "0", + "message": "", + "out": [] + } + } + ] +} diff --git a/multisig/mandos/reject_elrond_to_ethereum_tx_batch.scen.json b/multisig/mandos/reject_elrond_to_ethereum_tx_batch.scen.json new file mode 100644 index 00000000..1150e544 --- /dev/null +++ b/multisig/mandos/reject_elrond_to_ethereum_tx_batch.scen.json @@ -0,0 +1,285 @@ +{ + "name": "reject transactions", + "steps": [ + { + "step": "externalSteps", + "path": "create_elrond_to_ethereum_tx_batch.scen.json" + }, + { + "step": "scCall", + "txId": "propose-set-tx-batch-rejected", + "tx": { + "from": "address:relayer1", + "to": "sc:multisig", + "value": "0", + "function": "proposeEsdtSafeSetCurrentTransactionBatchStatus", + "arguments": [ + "1", + "4", + "4" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [ + "1" + ], + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "sc:multisig": { + "nonce": "*", + "balance": "*", + "storage": { + "str:action_data.item|u32:1": { + "1-action_type": "u8:1", + "2-batch_id": "u64:1", + "3-tx_batch_status_len": "u32:2", + "4-tx_batch_status_vec": "u8:4|u8:4" + }, + "+": "" + }, + "code": "*" + }, + "+": {} + } + }, + { + "step": "scCall", + "txId": "second-relayer-sign", + "tx": { + "from": "address:relayer2", + "to": "sc:multisig", + "value": "0", + "function": "sign", + "arguments": [ + "1" + ], + "gasLimit": "35,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "perform-action-set-tx-batch-rejected", + "tx": { + "from": "address:relayer1", + "to": "sc:multisig", + "value": "0", + "function": "performAction", + "arguments": [ + "1" + ], + "gasLimit": "75,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scQuery", + "txId": "user-query-refund-amounts", + "tx": { + "to": "sc:esdt_safe", + "function": "getRefundAmounts", + "arguments": [ + "address:user" + ] + }, + "expect": { + "out": [ + "str:EGLD-123456", + "400", + "str:ETH-123456", + "350,000" + ] + } + }, + { + "step": "scCall", + "txId": "user-claim-refund-egld", + "tx": { + "from": "address:user", + "to": "sc:esdt_safe", + "value": "0", + "function": "claimRefund", + "arguments": [ + "str:EGLD-123456" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": "*", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "user-claim-refund-eth", + "tx": { + "from": "address:user", + "to": "sc:esdt_safe", + "value": "0", + "function": "claimRefund", + "arguments": [ + "str:ETH-123456" + ], + "gasLimit": "50,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": "*", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "comment": "the tokens are returned to the user (except the tx fees)", + "accounts": { + "address:user": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:EGLD-123456": "500,000", + "str:ETH-123456": "850,000" + }, + "storage": {} + }, + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:EGLD-123456": { + "balance": "1,500,000", + "roles": [ + "ESDTRoleLocalBurn" + ] + }, + "str:ETH-123456": { + "balance": "150,000", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:transactionStatus|address:user|u32:1": "", + "str:transactionsByNonce|address:user|str:.item|u32:1": "", + "str:transactionStatus|address:user|u32:2": "", + "str:transactionsByNonce|address:user|str:.item|u32:2": "", + "str:accumulatedTransactionFees|nested:str:EGLD-123456": "1,500,000", + "str:accumulatedTransactionFees|nested:str:ETH-123456": "150,000", + "+": "" + }, + "code": "*" + }, + "+": {} + } + }, + { + "step": "scCall", + "txId": "owner-distribute-fees", + "tx": { + "from": "address:owner", + "to": "sc:multisig", + "value": "0", + "function": "distributeFeesFromChildContracts", + "arguments": [ + "address:relayer1", + "4000", + "address:relayer2", + "6000" + ], + "gasLimit": "200,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:relayer1": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:EGLD-123456": { + "balance": "600,000" + }, + "str:ETH-123456": { + "balance": "60,000" + } + }, + "storage": {} + }, + "address:relayer2": { + "nonce": "*", + "balance": "0", + "esdt": { + "str:EGLD-123456": { + "balance": "900,000" + }, + "str:ETH-123456": { + "balance": "90,000" + } + }, + "storage": {} + }, + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:EGLD-123456": { + "balance": "0", + "roles": [ + "ESDTRoleLocalBurn" + ] + }, + "str:ETH-123456": { + "balance": "0", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:accumulatedTransactionFees|nested:str:EGLD-123456": "0", + "str:accumulatedTransactionFees|nested:str:ETH-123456": "0", + "+": "" + }, + "code": "*" + }, + "+": {} + } + } + ] +} diff --git a/multisig/mandos/setup.scen.json b/multisig/mandos/setup.scen.json new file mode 100644 index 00000000..9739684e --- /dev/null +++ b/multisig/mandos/setup.scen.json @@ -0,0 +1,305 @@ +{ + "name": "setup accounts", + "steps": [ + { + "step": "externalSteps", + "path": "../../price-aggregator/mandos/oracle_gwei_in_eth_and_egld_submit.scen.json" + }, + { + "step": "setState", + "comment": "simulate child contracts deploy + setting local roles + change ownership. Changing ownership will be done after the multisig deploy in the real setup, but this is way less verbose in mandos.", + "accounts": { + "sc:multi_transfer": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:EGLD-123456": { + "balance": "0", + "roles": [ + "ESDTRoleLocalMint" + ] + }, + "str:ETH-123456": { + "balance": "0", + "roles": [ + "ESDTRoleLocalMint" + ] + } + }, + "storage": { + "str:maxTxBatchSize": "10", + "str:maxTxBatchBlockDuration": "3,600", + + "str:firstBatchId": "1", + "str:lastBatchId": "1" + }, + "owner": "sc:multisig", + "code": "file:../../multi-transfer-esdt/output/multi-transfer-esdt.wasm" + }, + "sc:esdt_safe": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:EGLD-123456": { + "balance": "0", + "roles": [ + "ESDTRoleLocalBurn" + ] + }, + "str:ETH-123456": { + "balance": "0", + "roles": [ + "ESDTRoleLocalBurn" + ] + } + }, + "storage": { + "str:feeEstimatorContractAddress": "sc:price_aggregator", + "str:maxTxBatchSize": "10", + "str:maxTxBatchBlockDuration": "100", + "str:ethTxGasLimit": "150,000", + + "str:firstBatchId": "1", + "str:lastBatchId": "1", + + "str:tokenTicker|nested:str:GWEI": "str:GWEI", + "str:tokenTicker|nested:str:EGLD-123456": "str:EGLD", + "str:tokenTicker|nested:str:ETH-123456": "str:ETH", + + "str:tokenWhitelist.index|nested:str:EGLD-123456": "1", + "str:tokenWhitelist.item|u32:1": "str:EGLD-123456", + "str:tokenWhitelist.index|nested:str:ETH-123456": "2", + "str:tokenWhitelist.item|u32:2": "str:ETH-123456", + "str:tokenWhitelist.len": "2" + }, + "owner": "sc:multisig", + "code": "file:../../esdt-safe/output/esdt-safe.wasm" + } + } + }, + { + "step": "setState", + "accounts": { + "address:owner": { + "nonce": "0", + "balance": "0", + "storage": {} + }, + "address:relayer1": { + "nonce": "0", + "balance": "1000", + "storage": {} + }, + "address:relayer2": { + "nonce": "0", + "balance": "1000", + "storage": {} + }, + "address:user": { + "nonce": "0", + "balance": "0", + "esdt": { + "str:EGLD-123456": "2,000,000", + "str:ETH-123456": "1,000,000" + }, + "storage": {} + } + }, + "newAddresses": [ + { + "creatorAddress": "address:owner", + "creatorNonce": "0", + "newAddress": "sc:multisig" + } + ] + }, + { + "step": "scDeploy", + "txId": "deploy", + "tx": { + "from": "address:owner", + "contractCode": "file:../output/multisig.wasm", + "value": "0", + "arguments": [ + "sc:esdt_safe", + "sc:multi_transfer", + "1000", + "500", + "2", + "address:relayer1", + "address:relayer2" + ], + "gasLimit": "40,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "sc:multisig": { + "nonce": "*", + "balance": "0", + "storage": { + "str:esdtSafeAddress": "sc:esdt_safe", + "str:multiTransferEsdtAddress": "sc:multi_transfer", + + "str:num_board_members": "2", + "str:quorum": "2", + "str:requiredStakeAmount": "1000", + "str:slashAmount": "500", + "str:user_role|u32:1": "1", + "str:user_role|u32:2": "1", + + "str:user_address_to_id|address:relayer1": "1", + "str:user_address_to_id|address:relayer2": "2", + "str:user_count": "2", + "str:user_id_to_address|u32:1": "address:relayer1", + "str:user_id_to_address|u32:2": "address:relayer2", + + "str:pause_module:paused": "true" + }, + "code": "file:../output/multisig.wasm" + }, + "+": {} + } + }, + { + "step": "scCall", + "txId": "first-relayer-stake", + "tx": { + "from": "address:relayer1", + "to": "sc:multisig", + "value": "1000", + "function": "stake", + "arguments": [], + "gasLimit": "35,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "scQuery", + "txId": "getAllStakedRelayers - only one staked", + "tx": { + "to": "sc:multisig", + "function": "getAllStakedRelayers", + "arguments": [] + }, + "expect": { + "status": "0", + "message": "", + "out": [ + "address:relayer1" + ] + } + }, + { + "step": "scCall", + "txId": "second-relayer-stake", + "tx": { + "from": "address:relayer2", + "to": "sc:multisig", + "value": "1000", + "function": "stake", + "arguments": [], + "gasLimit": "35,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:relayer1": { + "nonce": "1", + "balance": "0", + "storage": {} + }, + "address:relayer2": { + "nonce": "1", + "balance": "0", + "storage": {} + }, + "sc:multisig": { + "nonce": "*", + "balance": "2000", + "storage": { + "str:amountStaked|address:relayer1": "1000", + "str:amountStaked|address:relayer2": "1000", + "+": "" + }, + "code": "file:../output/multisig.wasm" + }, + "+": {} + } + }, + { + "step": "scQuery", + "txId": "getAllStakedRelayers - both staked", + "tx": { + "to": "sc:multisig", + "function": "getAllStakedRelayers", + "arguments": [] + }, + "expect": { + "status": "0", + "message": "", + "out": [ + "address:relayer1", + "address:relayer2" + ] + } + }, + { + "step": "scQuery", + "txId": "get-all-known-tokens", + "tx": { + "to": "sc:esdt_safe", + "function": "getAllKnownTokens" + }, + "expect": { + "out": [ + "str:EGLD-123456", + "str:ETH-123456" + ] + } + }, + { + "step": "scCall", + "txId": "unpause multisig", + "tx": { + "from": "address:owner", + "to": "sc:multisig", + "function": "unpause", + "arguments": [], + "gasLimit": "100,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "out": [], + "message": "", + "gas": "*", + "refund": "*" + } + } + ] +} diff --git a/multisig/mandos/unstake.scen.json b/multisig/mandos/unstake.scen.json new file mode 100644 index 00000000..4ba80b64 --- /dev/null +++ b/multisig/mandos/unstake.scen.json @@ -0,0 +1,196 @@ +{ + "name": "unstake test", + "steps": [ + { + "step": "externalSteps", + "path": "setup.scen.json" + }, + { + "step": "scCall", + "txId": "board-member-unstake", + "tx": { + "from": "address:relayer1", + "to": "sc:multisig", + "value": "0", + "function": "unstake", + "arguments": [ + "1000" + ], + "gasLimit": "35,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:can't unstake, must keep minimum amount as insurance", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "change-quorum", + "comment": "must change quorum before attempting to remove user", + "tx": { + "from": "address:owner", + "to": "sc:multisig", + "value": "0", + "function": "changeQuorum", + "arguments": [ + "1" + ], + "gasLimit": "35,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "sc:multisig": { + "nonce": "*", + "balance": "*", + "storage": { + "str:quorum": "1", + "+": "" + }, + "code": "*" + }, + "+": {} + } + }, + { + "step": "scCall", + "txId": "remove-user", + "tx": { + "from": "address:owner", + "to": "sc:multisig", + "value": "0", + "function": "removeUser", + "arguments": [ + "address:relayer2" + ], + "gasLimit": "40,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "sc:multisig": { + "nonce": "*", + "balance": "2000", + "storage": { + "str:quorum": "1", + "str:user_role|u32:1": "1", + "str:user_role|u32:2": "0", + "+": "" + }, + "code": "*" + }, + "+": {} + } + }, + { + "step": "scCall", + "txId": "unstake-too-much", + "tx": { + "from": "address:relayer2", + "to": "sc:multisig", + "value": "0", + "function": "unstake", + "arguments": [ + "1500" + ], + "gasLimit": "30,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:can't unstake more than amount staked", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "txId": "unstake-ok", + "tx": { + "from": "address:relayer2", + "to": "sc:multisig", + "value": "0", + "function": "unstake", + "arguments": [ + "1000" + ], + "gasLimit": "35,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "0", + "message": "", + "out": [], + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:relayer2": { + "nonce": "*", + "balance": "1000", + "storage": {} + }, + "sc:multisig": { + "nonce": "*", + "balance": "1000", + "storage": { + "str:quorum": "1", + "str:user_role|u32:1": "1", + "str:user_role|u32:2": "0", + "+": "" + }, + "code": "*" + }, + "+": {} + } + }, + { + "step": "scCall", + "txId": "try-propose-after-removed", + "tx": { + "from": "address:relayer2", + "to": "sc:multisig", + "value": "0", + "function": "proposeMultiTransferEsdtBatch", + "arguments": [ + "1" + ], + "gasLimit": "40,000,000", + "gasPrice": "0" + }, + "expect": { + "status": "4", + "message": "str:only board members can propose", + "out": [], + "gas": "*", + "refund": "*" + } + } + ] +} diff --git a/multisig/meta/Cargo.toml b/multisig/meta/Cargo.toml new file mode 100644 index 00000000..d57b4414 --- /dev/null +++ b/multisig/meta/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "multisig-meta" +version = "0.0.0" +authors = ["Andrei Marinica , Dorin Iancu "] +edition = "2018" +publish = false +[dependencies.multisig] +path = ".." + +[dependencies.multiversx-sc-meta] +version = "0.41.3" diff --git a/multisig/meta/src/main.rs b/multisig/meta/src/main.rs new file mode 100644 index 00000000..69a0a648 --- /dev/null +++ b/multisig/meta/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + multiversx_sc_meta::cli_main::(); +} diff --git a/multisig/multiversx.json b/multisig/multiversx.json new file mode 100644 index 00000000..8d77ca31 --- /dev/null +++ b/multisig/multiversx.json @@ -0,0 +1,3 @@ +{ + "language": "rust" +} diff --git a/multisig/src/action.rs b/multisig/src/action.rs new file mode 100644 index 00000000..d2bf8850 --- /dev/null +++ b/multisig/src/action.rs @@ -0,0 +1,28 @@ +use multiversx_sc::api::ManagedTypeApi; +use multiversx_sc::types::ManagedVec; +use transaction::transaction_status::TransactionStatus; +use transaction::EthTransaction; + +multiversx_sc::derive_imports!(); + +#[derive(NestedEncode, NestedDecode, TopEncode, TopDecode, TypeAbi)] +pub enum Action { + Nothing, + SetCurrentTransactionBatchStatus { + esdt_safe_batch_id: u64, + tx_batch_status: ManagedVec, + }, + BatchTransferEsdtToken { + eth_batch_id: u64, + transfers: ManagedVec>, + }, +} + +impl Action { + /// Only pending actions are kept in storage, + /// both executed and discarded actions are removed (converted to `Nothing`). + /// So this is equivalent to `action != Action::Nothing`. + pub fn is_pending(&self) -> bool { + !matches!(*self, Action::Nothing) + } +} diff --git a/multisig/src/lib.rs b/multisig/src/lib.rs new file mode 100644 index 00000000..9b5903ca --- /dev/null +++ b/multisig/src/lib.rs @@ -0,0 +1,370 @@ +#![no_std] +#![allow(clippy::too_many_arguments)] + +mod action; +mod multisig_general; +mod queries; +mod setup; +mod storage; +mod user_role; +mod util; + +use action::Action; +use token_module::{AddressPercentagePair, INVALID_PERCENTAGE_SUM_OVER_ERR_MSG, PERCENTAGE_TOTAL}; +use transaction::transaction_status::TransactionStatus; +use transaction::TxBatchSplitInFields; +use transaction::*; +use user_role::UserRole; + +use esdt_safe::ProxyTrait as _; +use multi_transfer_esdt::ProxyTrait as _; +use token_module::ProxyTrait as _; +use tx_batch_module::ProxyTrait as _; + +multiversx_sc::imports!(); + +/// Multi-signature smart contract implementation. +/// Acts like a wallet that needs multiple signers for any action performed. +#[multiversx_sc::contract] +pub trait Multisig: + multisig_general::MultisigGeneralModule + + setup::SetupModule + + storage::StorageModule + + util::UtilModule + + queries::QueriesModule + + multiversx_sc_modules::pause::PauseModule +{ + /// EsdtSafe and MultiTransferEsdt are expected to be deployed and configured separately, + /// and then having their ownership changed to this Multisig SC. + #[init] + fn init( + &self, + esdt_safe_sc_address: ManagedAddress, + multi_transfer_sc_address: ManagedAddress, + required_stake: BigUint, + slash_amount: BigUint, + quorum: usize, + board: MultiValueEncoded, + ) { + let mut duplicates = false; + let board_len = board.len(); + self.user_mapper() + .get_or_create_users(board.into_iter(), |user_id, new_user| { + if !new_user { + duplicates = true; + } + self.user_id_to_role(user_id).set(UserRole::BoardMember); + }); + require!(!duplicates, "duplicate board member"); + + self.num_board_members() + .update(|nr_board_members| *nr_board_members += board_len); + self.change_quorum(quorum); + + require!( + slash_amount <= required_stake, + "slash amount must be less than or equal to required stake" + ); + self.required_stake_amount().set(&required_stake); + self.slash_amount().set(&slash_amount); + + require!( + self.blockchain().is_smart_contract(&esdt_safe_sc_address), + "Esdt Safe address is not a Smart Contract address" + ); + self.esdt_safe_address().set(&esdt_safe_sc_address); + + require!( + self.blockchain() + .is_smart_contract(&multi_transfer_sc_address), + "Multi Transfer address is not a Smart Contract address" + ); + self.multi_transfer_esdt_address() + .set(&multi_transfer_sc_address); + + self.set_paused(true); + } + + /// Distributes the accumulated fees to the given addresses. + /// Expected arguments are pairs of (address, percentage), + /// where percentages must add up to the PERCENTAGE_TOTAL constant + #[only_owner] + #[endpoint(distributeFeesFromChildContracts)] + fn distribute_fees_from_child_contracts( + &self, + dest_address_percentage_pairs: MultiValueEncoded>, + ) { + let mut args = ManagedVec::new(); + let mut total_percentage = 0u64; + + for pair in dest_address_percentage_pairs { + let (dest_address, percentage) = pair.into_tuple(); + + require!( + !self.blockchain().is_smart_contract(&dest_address), + "Cannot transfer to smart contract dest_address" + ); + + total_percentage += percentage as u64; + args.push(AddressPercentagePair { + address: dest_address, + percentage, + }); + } + + require!( + total_percentage == PERCENTAGE_TOTAL as u64, + INVALID_PERCENTAGE_SUM_OVER_ERR_MSG + ); + + let _: IgnoreValue = self + .get_esdt_safe_proxy_instance() + .distribute_fees(args) + .execute_on_dest_context(); + } + + /// Board members have to stake a certain amount of EGLD + /// before being allowed to sign actions + #[payable("EGLD")] + #[endpoint] + fn stake(&self, #[payment] payment: BigUint) { + let caller = self.blockchain().get_caller(); + let caller_role = self.user_role(&caller); + require!( + caller_role == UserRole::BoardMember, + "Only board members can stake" + ); + + self.amount_staked(&caller) + .update(|amount_staked| *amount_staked += payment); + } + + #[endpoint] + fn unstake(&self, amount: BigUint) { + let caller = self.blockchain().get_caller(); + let amount_staked = self.amount_staked(&caller).get(); + require!( + amount <= amount_staked, + "can't unstake more than amount staked" + ); + + let remaining_stake = &amount_staked - &amount; + if self.user_role(&caller) == UserRole::BoardMember { + let required_stake_amount = self.required_stake_amount().get(); + require!( + remaining_stake >= required_stake_amount, + "can't unstake, must keep minimum amount as insurance" + ); + } + + self.amount_staked(&caller).set(&remaining_stake); + self.send().direct_egld(&caller, &amount); + } + + // ESDT Safe SC calls + + /// After a batch is processed on the Ethereum side, + /// the EsdtSafe expects a list of statuses of said transactions (success or failure). + /// + /// This endpoint proposes an action to set the statuses to a certain list of values. + /// Nothing is changed in the EsdtSafe contract until the action is signed and executed. + #[endpoint(proposeEsdtSafeSetCurrentTransactionBatchStatus)] + fn propose_esdt_safe_set_current_transaction_batch_status( + &self, + esdt_safe_batch_id: u64, + tx_batch_status: MultiValueEncoded, + ) -> usize { + let call_result: OptionalValue> = self + .get_esdt_safe_proxy_instance() + .get_current_tx_batch() + .execute_on_dest_context(); + let (current_batch_id, current_batch_transactions) = match call_result { + OptionalValue::Some(batch) => batch.into_tuple(), + OptionalValue::None => sc_panic!("Current batch is empty"), + }; + let statuses_vec = tx_batch_status.to_vec(); + + require!( + self.action_id_for_set_current_transaction_batch_status(esdt_safe_batch_id) + .get(&statuses_vec) + .is_none(), + "Action already proposed" + ); + + let current_batch_len = current_batch_transactions.raw_len() / TX_MULTIRESULT_NR_FIELDS; + let status_batch_len = statuses_vec.len(); + require!( + current_batch_len == status_batch_len, + "Number of statuses provided must be equal to number of transactions in current batch" + ); + require!( + esdt_safe_batch_id == current_batch_id, + "Current EsdtSafe tx batch does not have the provided ID" + ); + + let action_id = self.propose_action(Action::SetCurrentTransactionBatchStatus { + esdt_safe_batch_id, + tx_batch_status: statuses_vec.clone(), + }); + + self.action_id_for_set_current_transaction_batch_status(esdt_safe_batch_id) + .insert(statuses_vec, action_id); + + action_id + } + + // Multi-transfer ESDT SC calls + + /// Proposes a batch of Ethereum -> Elrond transfers. + /// Transactions have to be separated by fields, in the following order: + /// Sender Address, Destination Address, Token ID, Amount, Tx Nonce + #[endpoint(proposeMultiTransferEsdtBatch)] + fn propose_multi_transfer_esdt_batch( + &self, + eth_batch_id: u64, + transfers: MultiValueEncoded>, + ) -> usize { + let next_eth_batch_id = self.last_executed_eth_batch_id().get() + 1; + require!( + eth_batch_id == next_eth_batch_id, + "Can only propose for next batch ID" + ); + + let transfers_as_eth_tx = self.transfers_multi_value_to_eth_tx_vec(transfers); + self.require_valid_eth_tx_ids(&transfers_as_eth_tx); + + let batch_hash = self.hash_eth_tx_batch(&transfers_as_eth_tx); + require!( + self.batch_id_to_action_id_mapping(eth_batch_id) + .get(&batch_hash) + .is_none(), + "This batch was already proposed" + ); + + let action_id = self.propose_action(Action::BatchTransferEsdtToken { + eth_batch_id, + transfers: transfers_as_eth_tx, + }); + + self.batch_id_to_action_id_mapping(eth_batch_id) + .insert(batch_hash, action_id); + + action_id + } + + /// Failed Ethereum -> Elrond transactions are saved in the MultiTransfer SC + /// as "refund transactions", and stored in batches, using the same mechanism as EsdtSafe. + /// + /// This function moves the first refund batch into the EsdtSafe SC, + /// converting the transactions into Elrond -> Ethereum transactions + /// and adding them into EsdtSafe batches + #[only_owner] + #[endpoint(moveRefundBatchToSafe)] + fn move_refund_batch_to_safe(&self) { + let opt_refund_batch_fields: OptionalValue> = self + .get_multi_transfer_esdt_proxy_instance() + .get_and_clear_first_refund_batch() + .execute_on_dest_context(); + + if let OptionalValue::Some(refund_batch_fields) = opt_refund_batch_fields { + let (_batch_id, all_tx_fields) = refund_batch_fields.into_tuple(); + let mut refund_batch = ManagedVec::new(); + + for tx_fields in all_tx_fields { + refund_batch.push(Transaction::from(tx_fields)); + } + + let _: IgnoreValue = self + .get_esdt_safe_proxy_instance() + .add_refund_batch(refund_batch) + .execute_on_dest_context(); + } + } + + /// Proposers and board members use this to launch signed actions. + #[endpoint(performAction)] + fn perform_action_endpoint(&self, action_id: usize) { + require!( + !self.action_mapper().item_is_empty(action_id), + "Action was already executed" + ); + + let caller_address = self.blockchain().get_caller(); + let caller_role = self.get_user_role(&caller_address); + require!( + caller_role.is_board_member(), + "only board members can perform actions" + ); + require!( + self.quorum_reached(action_id), + "quorum has not been reached" + ); + require!(self.not_paused(), "No actions may be executed while paused"); + + self.perform_action(action_id); + } + + // private + + fn perform_action(&self, action_id: usize) { + let action = self.action_mapper().get(action_id); + self.clear_action(action_id); + + match action { + Action::Nothing => {} + Action::SetCurrentTransactionBatchStatus { + esdt_safe_batch_id, + tx_batch_status, + } => { + let mut action_ids_mapper = + self.action_id_for_set_current_transaction_batch_status(esdt_safe_batch_id); + + // if there's only one proposed action, + // the action was already cleared at the beginning of this function + if action_ids_mapper.len() > 1 { + for act_id in action_ids_mapper.values() { + self.clear_action(act_id); + } + } + + action_ids_mapper.clear(); + + let _: IgnoreValue = self + .get_esdt_safe_proxy_instance() + .set_transaction_batch_status( + esdt_safe_batch_id, + MultiValueEncoded::from(tx_batch_status), + ) + .execute_on_dest_context(); + } + Action::BatchTransferEsdtToken { + eth_batch_id, + transfers, + } => { + let mut action_ids_mapper = self.batch_id_to_action_id_mapping(eth_batch_id); + + // if there's only one proposed action, + // the action was already cleared at the beginning of this function + if action_ids_mapper.len() > 1 { + for act_id in action_ids_mapper.values() { + self.clear_action(act_id); + } + } + + action_ids_mapper.clear(); + self.last_executed_eth_batch_id().update(|id| *id += 1); + + let last_tx_index = transfers.len() - 1; + let last_tx = transfers.get(last_tx_index); + self.last_executed_eth_tx_id().set(last_tx.tx_nonce); + + let transfers_multi: MultiValueEncoded> = + transfers.into(); + let _: IgnoreValue = self + .get_multi_transfer_esdt_proxy_instance() + .batch_transfer_esdt_token(eth_batch_id, transfers_multi) + .execute_on_dest_context(); + } + } + } +} diff --git a/multisig/src/multisig_general.rs b/multisig/src/multisig_general.rs new file mode 100644 index 00000000..c044e663 --- /dev/null +++ b/multisig/src/multisig_general.rs @@ -0,0 +1,73 @@ +multiversx_sc::imports!(); + +use crate::action::Action; +use crate::user_role::UserRole; + +#[multiversx_sc::module] +pub trait MultisigGeneralModule: + crate::util::UtilModule + crate::storage::StorageModule + multiversx_sc_modules::pause::PauseModule +{ + /// Used by board members to sign actions. + #[endpoint] + fn sign(&self, action_id: usize) { + require!( + !self.action_mapper().item_is_empty_unchecked(action_id), + "action does not exist" + ); + + let caller_address = self.blockchain().get_caller(); + let caller_id = self.user_mapper().get_user_id(&caller_address); + let caller_role = self.user_id_to_role(caller_id).get(); + require!(caller_role.is_board_member(), "only board members can sign"); + require!(self.has_enough_stake(&caller_address), "not enough stake"); + + let _ = self.action_signer_ids(action_id).insert(caller_id); + } + + fn propose_action(&self, action: Action) -> usize { + let caller_address = self.blockchain().get_caller(); + let caller_id = self.user_mapper().get_user_id(&caller_address); + let caller_role = self.user_id_to_role(caller_id).get(); + require!( + caller_role.is_board_member(), + "only board members can propose" + ); + + require!(self.not_paused(), "No actions may be proposed while paused"); + + let action_id = self.action_mapper().push(&action); + if self.has_enough_stake(&caller_address) { + let _ = self.action_signer_ids(action_id).insert(caller_id); + } + + action_id + } + + fn clear_action(&self, action_id: usize) { + self.action_mapper().clear_entry_unchecked(action_id); + self.action_signer_ids(action_id).clear(); + } + + fn add_board_member(&self, user_address: &ManagedAddress) { + let user_id = self.user_mapper().get_or_create_user(user_address); + let old_role = self.user_id_to_role(user_id).get(); + + if !old_role.is_board_member() { + self.num_board_members().update(|value| *value += 1); + self.user_id_to_role(user_id).set(UserRole::BoardMember); + } + } + + fn remove_board_member(&self, user_address: &ManagedAddress) { + let user_id = self.user_mapper().get_user_id(user_address); + if user_id == 0 { + return; + } + + let old_role = self.user_id_to_role(user_id).get(); + if old_role.is_board_member() { + self.num_board_members().update(|value| *value -= 1); + self.user_id_to_role(user_id).set(UserRole::None); + } + } +} diff --git a/multisig/src/queries.rs b/multisig/src/queries.rs new file mode 100644 index 00000000..dc5c15ad --- /dev/null +++ b/multisig/src/queries.rs @@ -0,0 +1,193 @@ +multiversx_sc::imports!(); + +use crate::{action::Action, user_role::UserRole}; +use transaction::{transaction_status::TransactionStatus, EthTxAsMultiValue, TxBatchSplitInFields}; + +use tx_batch_module::ProxyTrait as _; + +/// Note: Additional queries can be found in the Storage module +#[multiversx_sc::module] +pub trait QueriesModule: crate::storage::StorageModule + crate::util::UtilModule { + /// Returns the current EsdtSafe batch. + /// + /// First result is the batch ID, then pairs of 6 results, representing transactions + /// split by fields: + /// + /// Block Nonce, Tx Nonce, Sender Address, Receiver Address, Token ID, Amount + #[view(getCurrentTxBatch)] + fn get_current_tx_batch(&self) -> OptionalValue> { + self.get_esdt_safe_proxy_instance() + .get_current_tx_batch() + .execute_on_dest_context() + } + + /// Returns a batch of failed Ethereum -> Elrond transactions. + /// The result format is the same as getCurrentTxBatch + #[view(getCurrentRefundBatch)] + fn get_current_refund_batch(&self) -> OptionalValue> { + self.get_multi_transfer_esdt_proxy_instance() + .get_first_batch_any_status() + .execute_on_dest_context() + } + + /// Actions are cleared after execution, so an empty entry means the action was executed already + /// Returns "false" if the action ID is invalid + #[view(wasActionExecuted)] + fn was_action_executed(&self, action_id: usize) -> bool { + if self.is_valid_action_id(action_id) { + self.action_mapper().item_is_empty(action_id) + } else { + false + } + } + + /// Used for Ethereum -> Elrond batches. + /// If the mapping was made, it means that the transfer action was proposed in the past. + /// To check if it was executed as well, use the wasActionExecuted view + #[view(wasTransferActionProposed)] + fn was_transfer_action_proposed( + &self, + eth_batch_id: u64, + transfers: MultiValueEncoded>, + ) -> bool { + let action_id = self.get_action_id_for_transfer_batch(eth_batch_id, transfers); + + self.is_valid_action_id(action_id) + } + + /// Used for Ethereum -> Elrond batches. + /// If `wasActionExecuted` returns true, then this can be used to get the action ID. + /// Will return 0 if the transfers were not proposed + #[view(getActionIdForTransferBatch)] + fn get_action_id_for_transfer_batch( + &self, + eth_batch_id: u64, + transfers: MultiValueEncoded>, + ) -> usize { + let transfers_as_struct = self.transfers_multi_value_to_eth_tx_vec(transfers); + let batch_hash = self.hash_eth_tx_batch(&transfers_as_struct); + + self.batch_id_to_action_id_mapping(eth_batch_id) + .get(&batch_hash) + .unwrap_or(0) + } + + /// Used for Elrond -> Ethereum batches. + /// Returns "true" if an action was already proposed for the given batch, + /// with these exact transaction statuses, in this exact order + #[view(wasSetCurrentTransactionBatchStatusActionProposed)] + fn was_set_current_transaction_batch_status_action_proposed( + &self, + esdt_safe_batch_id: u64, + expected_tx_batch_status: MultiValueEncoded, + ) -> bool { + self.is_valid_action_id(self.get_action_id_for_set_current_transaction_batch_status( + esdt_safe_batch_id, + expected_tx_batch_status, + )) + } + + /// If `wasSetCurrentTransactionBatchStatusActionProposed` return true, + /// this can be used to get the action ID. + /// Will return 0 if the set status action was not proposed + #[view(getActionIdForSetCurrentTransactionBatchStatus)] + fn get_action_id_for_set_current_transaction_batch_status( + &self, + esdt_safe_batch_id: u64, + expected_tx_batch_status: MultiValueEncoded, + ) -> usize { + self.action_id_for_set_current_transaction_batch_status(esdt_safe_batch_id) + .get(&expected_tx_batch_status.to_vec()) + .unwrap_or(0) + } + + /// Returns `true` (`1`) if the user has signed the action. + /// Does not check whether or not the user is still a board member and the signature valid. + #[view] + fn signed(&self, user: ManagedAddress, action_id: usize) -> bool { + let user_id = self.user_mapper().get_user_id(&user); + if user_id == 0 { + false + } else { + self.action_signer_ids(action_id).contains(&user_id) + } + } + + /// Indicates user rights. + /// `0` = no rights, + /// `1` = can propose. Can also sign if they have enough stake. + #[view(userRole)] + fn user_role(&self, user: &ManagedAddress) -> UserRole { + self.get_user_role(user) + } + + /// Lists all board members + #[view(getAllBoardMembers)] + fn get_all_board_members(&self) -> MultiValueEncoded { + self.get_all_users_with_role(UserRole::BoardMember) + } + + /// Lists all board members that staked the correct amount. + /// A board member with not enough stake can propose, but cannot sign. + #[view(getAllStakedRelayers)] + fn get_all_staked_relayers(&self) -> MultiValueEncoded { + let relayers = self.get_all_board_members().to_vec(); + let mut staked_relayers = ManagedVec::new(); + + for relayer in &relayers { + if self.has_enough_stake(&relayer) { + staked_relayers.push(relayer); + } + } + + staked_relayers.into() + } + + /// Gets the number of signatures for the action with the given ID + #[view(getActionSignerCount)] + fn get_action_signer_count(&self, action_id: usize) -> usize { + self.action_signer_ids(action_id).len() + } + + /// It is possible for board members to lose their role. + /// They are not automatically removed from all actions when doing so, + /// therefore the contract needs to re-check every time when actions are performed. + /// This function is used to validate the signers before performing an action. + /// It also makes it easy to check before performing an action. + #[view(getActionValidSignerCount)] + fn get_action_valid_signer_count(&self, action_id: usize) -> usize { + self.action_signer_ids(action_id) + .iter() + .filter(|signer_id| { + let signer_role = self.user_id_to_role(*signer_id).get(); + let signer_address = self + .user_mapper() + .get_user_address(*signer_id) + .unwrap_or_default(); + + signer_role.is_board_member() && self.has_enough_stake(&signer_address) + }) + .count() + } + + /// Returns `true` (`1`) if `getActionValidSignerCount >= getQuorum`. + #[view(quorumReached)] + fn quorum_reached(&self, action_id: usize) -> bool { + let quorum = self.quorum().get(); + let valid_signers_count = self.get_action_valid_signer_count(action_id); + valid_signers_count >= quorum + } + + /// The index of the last proposed action. + /// 0 means that no action was ever proposed yet. + #[view(getActionLastIndex)] + fn get_action_last_index(&self) -> usize { + self.action_mapper().len() + } + + /// Serialized action data of an action with index. + #[view(getActionData)] + fn get_action_data(&self, action_id: usize) -> Action { + self.action_mapper().get(action_id) + } +} diff --git a/multisig/src/setup.rs b/multisig/src/setup.rs new file mode 100644 index 00000000..4fa369b0 --- /dev/null +++ b/multisig/src/setup.rs @@ -0,0 +1,321 @@ +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +use eth_address::EthAddress; + +use fee_estimator_module::ProxyTrait as _; +use max_bridged_amount_module::ProxyTrait as _; +use multi_transfer_esdt::ProxyTrait as _; +use multiversx_sc_modules::pause::ProxyTrait as _; +use token_module::ProxyTrait as _; +use tx_batch_module::ProxyTrait as _; + +#[multiversx_sc::module] +pub trait SetupModule: + crate::multisig_general::MultisigGeneralModule + + crate::storage::StorageModule + + crate::util::UtilModule + + multiversx_sc_modules::pause::PauseModule +{ + #[only_owner] + #[endpoint(upgradeChildContractFromSource)] + fn upgrade_child_contract_from_source( + &self, + child_sc_address: ManagedAddress, + source_address: ManagedAddress, + is_payable: bool, + init_args: MultiValueEncoded, + ) { + let mut metadata = CodeMetadata::UPGRADEABLE; + if is_payable { + // TODO: Replace with PayableBySc when it's available + metadata |= CodeMetadata::PAYABLE; + } + + let gas = self.blockchain().get_gas_left(); + Self::Api::send_api_impl().upgrade_from_source_contract( + &child_sc_address, + gas, + &BigUint::zero(), + &source_address, + metadata, + &init_args.to_arg_buffer(), + ); + } + + #[only_owner] + #[endpoint(addBoardMember)] + fn add_board_member_endpoint(&self, board_member: ManagedAddress) { + self.add_board_member(&board_member); + } + + #[only_owner] + #[endpoint(removeUser)] + fn remove_user(&self, board_member: ManagedAddress) { + self.remove_board_member(&board_member); + let num_board_members = self.num_board_members().get(); + require!(num_board_members > 0, "cannot remove all board members"); + require!( + self.quorum().get() <= num_board_members, + "quorum cannot exceed board size" + ); + } + + /// Cuts a fixed amount from a board member's stake. + /// This should be used only in cases where the board member + /// is being actively malicious. + /// + /// After stake is cut, the board member would have to stake again + /// to be able to sign actions. + #[only_owner] + #[endpoint(slashBoardMember)] + fn slash_board_member(&self, board_member: ManagedAddress) { + self.remove_user(board_member.clone()); + + let slash_amount = self.slash_amount().get(); + + // remove slashed amount from user stake amountself + self.amount_staked(&board_member) + .update(|stake| *stake -= &slash_amount); + + // add it to total slashed amount pool + self.slashed_tokens_amount() + .update(|slashed_amt| *slashed_amt += slash_amount); + } + + #[only_owner] + #[endpoint(changeQuorum)] + fn change_quorum(&self, new_quorum: usize) { + require!( + new_quorum <= self.num_board_members().get(), + "quorum cannot exceed board size" + ); + self.quorum().set(new_quorum); + } + + /// Maps an ESDT token to an ERC20 address. Used by relayers. + #[only_owner] + #[endpoint(addMapping)] + fn add_mapping(&self, erc20_address: EthAddress, token_id: TokenIdentifier) { + require!( + self.erc20_address_for_token_id(&token_id).is_empty(), + "Mapping already exists for token ID" + ); + require!( + self.token_id_for_erc20_address(&erc20_address).is_empty(), + "Mapping already exists for ERC20 token" + ); + + self.erc20_address_for_token_id(&token_id) + .set(&erc20_address); + self.token_id_for_erc20_address(&erc20_address) + .set(&token_id); + } + + #[only_owner] + #[endpoint(clearMapping)] + fn clear_mapping(&self, erc20_address: EthAddress, token_id: TokenIdentifier) { + require!( + !self.erc20_address_for_token_id(&token_id).is_empty(), + "Mapping does not exist for ERC20 token" + ); + require!( + !self.token_id_for_erc20_address(&erc20_address).is_empty(), + "Mapping does not exist for token id" + ); + + let mapped_erc_20 = self.erc20_address_for_token_id(&token_id).get(); + let mapped_token_id = self.token_id_for_erc20_address(&erc20_address).get(); + + require!( + erc20_address.raw_addr == mapped_erc_20.raw_addr && token_id == mapped_token_id, + "Invalid mapping" + ); + + self.erc20_address_for_token_id(&token_id).clear(); + self.token_id_for_erc20_address(&erc20_address).clear(); + } + + #[only_owner] + #[endpoint(pauseEsdtSafe)] + fn pause_esdt_safe(&self) { + let _: IgnoreValue = self + .get_esdt_safe_proxy_instance() + .pause_endpoint() + .execute_on_dest_context(); + } + + #[only_owner] + #[endpoint(unpauseEsdtSafe)] + fn unpause_esdt_safe(&self) { + let _: IgnoreValue = self + .get_esdt_safe_proxy_instance() + .unpause_endpoint() + .execute_on_dest_context(); + } + + #[only_owner] + #[endpoint(changeFeeEstimatorContractAddress)] + fn change_fee_estimator_contract_address(&self, new_address: ManagedAddress) { + let _: IgnoreValue = self + .get_esdt_safe_proxy_instance() + .set_fee_estimator_contract_address(new_address) + .execute_on_dest_context(); + } + + /// Sets the gas limit being used for Ethereum transactions + /// This is used in the EsdtSafe contract to determine the fee amount + /// + /// fee_amount = eth_gas_limit * price_per_gas_unit + /// + /// where price_per_gas_unit is queried from the aggregator (fee estimator SC) + #[only_owner] + #[endpoint(changeElrondToEthGasLimit)] + fn change_elrond_to_eth_gas_limit(&self, new_gas_limit: BigUint) { + let _: IgnoreValue = self + .get_esdt_safe_proxy_instance() + .set_eth_tx_gas_limit(new_gas_limit) + .execute_on_dest_context(); + } + + /// Default price being used if the aggregator lacks a mapping for this token + /// or the aggregator address is not set + #[only_owner] + #[endpoint(changeDefaultPricePerGasUnit)] + fn change_default_price_per_gas_unit(&self, token_id: TokenIdentifier, new_value: BigUint) { + let _: IgnoreValue = self + .get_esdt_safe_proxy_instance() + .set_default_price_per_gas_unit(token_id, new_value) + .execute_on_dest_context(); + } + + /// Token ticker being used when querying the aggregator for GWEI prices + #[only_owner] + #[endpoint(changeTokenTicker)] + fn change_token_ticker(&self, token_id: TokenIdentifier, new_ticker: ManagedBuffer) { + let _: IgnoreValue = self + .get_esdt_safe_proxy_instance() + .set_token_ticker(token_id, new_ticker) + .execute_on_dest_context(); + } + + #[only_owner] + #[endpoint(esdtSafeAddTokenToWhitelist)] + fn esdt_safe_add_token_to_whitelist( + &self, + token_id: TokenIdentifier, + ticker: ManagedBuffer, + opt_default_price_per_gas_unit: OptionalValue, + ) { + let _: IgnoreValue = self + .get_esdt_safe_proxy_instance() + .add_token_to_whitelist(token_id, ticker, opt_default_price_per_gas_unit) + .execute_on_dest_context(); + } + + #[only_owner] + #[endpoint(esdtSafeRemoveTokenFromWhitelist)] + fn esdt_safe_remove_token_from_whitelist(&self, token_id: TokenIdentifier) { + let _: IgnoreValue = self + .get_esdt_safe_proxy_instance() + .remove_token_from_whitelist(token_id) + .execute_on_dest_context(); + } + + /// Sets maximum batch size for the EsdtSafe SC. + /// If a batch reaches this amount of transactions, it is considered full, + /// and a new incoming transaction will be put into a new batch. + #[only_owner] + #[endpoint(esdtSafeSetMaxTxBatchSize)] + fn esdt_safe_set_max_tx_batch_size(&self, new_max_tx_batch_size: usize) { + let _: IgnoreValue = self + .get_esdt_safe_proxy_instance() + .set_max_tx_batch_size(new_max_tx_batch_size) + .execute_on_dest_context(); + } + + /// Sets the maximum block duration in which an EsdtSafe batch accepts transactions + /// For a batch to be considered "full", it has to either reach `maxTxBatchSize` transactions, + /// or have txBatchBlockDuration blocks pass since the first tx was added in the batch + #[only_owner] + #[endpoint(esdtSafeSetMaxTxBatchBlockDuration)] + fn esdt_safe_set_max_tx_batch_block_duration(&self, new_max_tx_batch_block_duration: u64) { + let _: IgnoreValue = self + .get_esdt_safe_proxy_instance() + .set_max_tx_batch_block_duration(new_max_tx_batch_block_duration) + .execute_on_dest_context(); + } + + /// Sets the maximum bridged amount for the token for the Elrond -> Ethereum direction. + /// Any attempt to transfer over this amount will be rejected. + #[only_owner] + #[endpoint(esdtSafeSetMaxBridgedAmountForToken)] + fn esdt_safe_set_max_bridged_amount_for_token( + &self, + token_id: TokenIdentifier, + max_amount: BigUint, + ) { + let _: IgnoreValue = self + .get_esdt_safe_proxy_instance() + .set_max_bridged_amount(token_id, max_amount) + .execute_on_dest_context(); + } + + /// Same as the function above, but for Ethereum -> Elrond transactions. + #[only_owner] + #[endpoint(multiTransferEsdtSetMaxBridgedAmountForToken)] + fn multi_transfer_esdt_set_max_bridged_amount_for_token( + &self, + token_id: TokenIdentifier, + max_amount: BigUint, + ) { + let _: IgnoreValue = self + .get_multi_transfer_esdt_proxy_instance() + .set_max_bridged_amount(token_id, max_amount) + .execute_on_dest_context(); + } + + /// Any failed Ethereum -> Elrond transactions are added into so-called "refund batches" + /// This configures the size of a batch. + #[only_owner] + #[endpoint(multiTransferEsdtSetMaxRefundTxBatchSize)] + fn multi_transfer_esdt_set_max_refund_tx_batch_size(&self, new_max_tx_batch_size: usize) { + let _: IgnoreValue = self + .get_multi_transfer_esdt_proxy_instance() + .set_max_tx_batch_size(new_max_tx_batch_size) + .execute_on_dest_context(); + } + + /// Max block duration for refund batches. Default is "infinite" (u64::MAX) + /// and only max batch size matters + #[only_owner] + #[endpoint(multiTransferEsdtSetMaxRefundTxBatchBlockDuration)] + fn multi_transfer_esdt_set_max_refund_tx_batch_block_duration( + &self, + new_max_tx_batch_block_duration: u64, + ) { + let _: IgnoreValue = self + .get_multi_transfer_esdt_proxy_instance() + .set_max_tx_batch_block_duration(new_max_tx_batch_block_duration) + .execute_on_dest_context(); + } + + /// Sets the wrapping contract address. + /// This contract is used to map multiple tokens to a universal one. + /// Useful in cases where a single token (USDC for example) + /// is being transferred from multiple chains. + /// + /// They will all have different token IDs, but can be swapped 1:1 in the wrapping SC. + /// The wrapping is done automatically, so the user only receives the universal token. + #[only_owner] + #[endpoint(multiTransferEsdtSetWrappingContractAddress)] + fn multi_transfer_esdt_set_wrapping_contract_address( + &self, + opt_wrapping_contract_address: OptionalValue, + ) { + let _: IgnoreValue = self + .get_multi_transfer_esdt_proxy_instance() + .set_wrapping_contract_address(opt_wrapping_contract_address) + .execute_on_dest_context(); + } +} diff --git a/multisig/src/storage.rs b/multisig/src/storage.rs new file mode 100644 index 00000000..bbc353c2 --- /dev/null +++ b/multisig/src/storage.rs @@ -0,0 +1,102 @@ +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +use eth_address::EthAddress; +use transaction::transaction_status::TransactionStatus; + +use crate::action::Action; +use crate::user_role::UserRole; + +pub type EthBatchHash = ManagedByteArray; // keccak256(ManagedVec) + +#[multiversx_sc::module] +pub trait StorageModule { + /// Minimum number of signatures needed to perform any action. + #[view(getQuorum)] + #[storage_mapper("quorum")] + fn quorum(&self) -> SingleValueMapper; + + #[storage_mapper("user")] + fn user_mapper(&self) -> UserMapper; + + #[storage_mapper("user_role")] + fn user_id_to_role(&self, user_id: usize) -> SingleValueMapper; + + /// Denormalized board member count. + /// It is kept in sync with the user list by the contract. + #[view(getNumBoardMembers)] + #[storage_mapper("num_board_members")] + fn num_board_members(&self) -> SingleValueMapper; + + #[storage_mapper("action_data")] + fn action_mapper(&self) -> VecMapper>; + + #[storage_mapper("action_signer_ids")] + fn action_signer_ids(&self, action_id: usize) -> UnorderedSetMapper; + + /// The required amount to stake for accepting relayer position + #[view(getRequiredStakeAmount)] + #[storage_mapper("requiredStakeAmount")] + fn required_stake_amount(&self) -> SingleValueMapper; + + /// Staked amount by each board member. + #[view(getAmountStaked)] + #[storage_mapper("amountStaked")] + fn amount_staked(&self, board_member_address: &ManagedAddress) -> SingleValueMapper; + + /// Amount of stake slashed if a relayer is misbehaving + #[view(getSlashAmount)] + #[storage_mapper("slashAmount")] + fn slash_amount(&self) -> SingleValueMapper; + + /// Total slashed tokens accumulated + #[view(getSlashedTokensAmount)] + #[storage_mapper("slashedTokensAmount")] + fn slashed_tokens_amount(&self) -> SingleValueMapper; + + #[view(getLastExecutedEthBatchId)] + #[storage_mapper("lastExecutedEthBatchId")] + fn last_executed_eth_batch_id(&self) -> SingleValueMapper; + + #[view(getLastExecutedEthTxId)] + #[storage_mapper("lastExecutedEthTxId")] + fn last_executed_eth_tx_id(&self) -> SingleValueMapper; + + #[storage_mapper("batchIdToActionIdMapping")] + fn batch_id_to_action_id_mapping( + &self, + batch_id: u64, + ) -> MapMapper, usize>; + + #[storage_mapper("actionIdForSetCurrentTransactionBatchStatus")] + fn action_id_for_set_current_transaction_batch_status( + &self, + esdt_safe_batch_id: u64, + ) -> MapMapper, usize>; + + /// Mapping between ERC20 Ethereum address and Elrond ESDT Token Identifiers + + #[view(getErc20AddressForTokenId)] + #[storage_mapper("erc20AddressForTokenId")] + fn erc20_address_for_token_id( + &self, + token_id: &TokenIdentifier, + ) -> SingleValueMapper>; + + #[view(getTokenIdForErc20Address)] + #[storage_mapper("tokenIdForErc20Address")] + fn token_id_for_erc20_address( + &self, + erc20_address: &EthAddress, + ) -> SingleValueMapper; + + // SC addresses + + #[view(getEsdtSafeAddress)] + #[storage_mapper("esdtSafeAddress")] + fn esdt_safe_address(&self) -> SingleValueMapper; + + #[view(getMultiTransferEsdtAddress)] + #[storage_mapper("multiTransferEsdtAddress")] + fn multi_transfer_esdt_address(&self) -> SingleValueMapper; +} diff --git a/multisig/src/user_role.rs b/multisig/src/user_role.rs new file mode 100644 index 00000000..813974a6 --- /dev/null +++ b/multisig/src/user_role.rs @@ -0,0 +1,14 @@ +multiversx_sc::derive_imports!(); + +#[derive(TopEncode, TopDecode, TypeAbi, Clone, Copy, PartialEq)] +pub enum UserRole { + None, + BoardMember, +} + +impl UserRole { + #[inline(always)] + pub fn is_board_member(&self) -> bool { + matches!(*self, UserRole::BoardMember) + } +} diff --git a/multisig/src/util.rs b/multisig/src/util.rs new file mode 100644 index 00000000..96fc007b --- /dev/null +++ b/multisig/src/util.rs @@ -0,0 +1,106 @@ +multiversx_sc::imports!(); + +use transaction::{EthTransaction, EthTxAsMultiValue}; + +use crate::storage::EthBatchHash; +use crate::user_role::UserRole; + +#[multiversx_sc::module] +pub trait UtilModule: crate::storage::StorageModule { + fn get_user_role(&self, user: &ManagedAddress) -> UserRole { + let user_id = self.user_mapper().get_user_id(user); + if user_id == 0 { + UserRole::None + } else { + self.user_id_to_role(user_id).get() + } + } + + fn is_valid_action_id(&self, action_id: usize) -> bool { + let min_id = 1; + let max_id = self.action_mapper().len(); + + action_id >= min_id && action_id <= max_id + } + + fn get_all_users_with_role(&self, role: UserRole) -> MultiValueEncoded { + let mut result = ManagedVec::new(); + let num_users = self.user_mapper().get_user_count(); + for user_id in 1..=num_users { + if self.user_id_to_role(user_id).get() == role { + if let Some(address) = self.user_mapper().get_user_address(user_id) { + result.push(address); + } + } + } + result.into() + } + + fn has_enough_stake(&self, board_member_address: &ManagedAddress) -> bool { + let required_stake = self.required_stake_amount().get(); + let amount_staked = self.amount_staked(board_member_address).get(); + + amount_staked >= required_stake + } + + fn transfers_multi_value_to_eth_tx_vec( + &self, + transfers: MultiValueEncoded>, + ) -> ManagedVec> { + let mut transfers_as_eth_tx = ManagedVec::new(); + for transfer in transfers { + let (from, to, token_id, amount, tx_nonce) = transfer.into_tuple(); + + transfers_as_eth_tx.push(EthTransaction { + from, + to, + token_id, + amount, + tx_nonce, + }); + } + + transfers_as_eth_tx + } + + fn require_valid_eth_tx_ids(&self, eth_tx_vec: &ManagedVec>) { + let last_executed_eth_tx_id = self.last_executed_eth_tx_id().get(); + let mut current_expected_tx_id = last_executed_eth_tx_id + 1; + + for eth_tx in eth_tx_vec { + require!(eth_tx.tx_nonce == current_expected_tx_id, "Invalid Tx ID"); + current_expected_tx_id += 1; + } + } + + fn hash_eth_tx_batch( + &self, + eth_tx_batch: &ManagedVec>, + ) -> EthBatchHash { + let mut serialized = ManagedBuffer::new(); + if eth_tx_batch.top_encode(&mut serialized).is_err() { + sc_panic!("Failed to serialized batch"); + } + + self.crypto().keccak256(&serialized) + } + + // proxies + + #[proxy] + fn esdt_safe_proxy(&self, sc_address: ManagedAddress) -> esdt_safe::Proxy; + + #[proxy] + fn multi_transfer_esdt_proxy( + &self, + sc_address: ManagedAddress, + ) -> multi_transfer_esdt::Proxy; + + fn get_esdt_safe_proxy_instance(&self) -> esdt_safe::Proxy { + self.esdt_safe_proxy(self.esdt_safe_address().get()) + } + + fn get_multi_transfer_esdt_proxy_instance(&self) -> multi_transfer_esdt::Proxy { + self.multi_transfer_esdt_proxy(self.multi_transfer_esdt_address().get()) + } +} diff --git a/multisig/tests/scenario_go_test.rs b/multisig/tests/scenario_go_test.rs new file mode 100644 index 00000000..9605e9d4 --- /dev/null +++ b/multisig/tests/scenario_go_test.rs @@ -0,0 +1,46 @@ +#[test] +fn create_elrond_to_ethereum_tx_batch_go() { + multiversx_sc_scenario::run_go("mandos/create_elrond_to_ethereum_tx_batch.scen.json"); +} + +#[test] +fn ethereum_to_elrond_tx_batch_ok_go() { + multiversx_sc_scenario::run_go("mandos/ethereum_to_elrond_tx_batch_ok.scen.json"); +} + +#[test] +fn ethereum_to_elrond_tx_batch_rejected_go() { + multiversx_sc_scenario::run_go("mandos/ethereum_to_elrond_tx_batch_rejected.scen.json"); +} + +#[test] +fn execute_elrond_to_ethereum_tx_batch_go() { + multiversx_sc_scenario::run_go("mandos/execute_elrond_to_ethereum_tx_batch.scen.json"); +} + +#[test] +fn get_empty_batch_go() { + multiversx_sc_scenario::run_go("mandos/get_empty_batch.scen.json"); +} + +#[test] +fn reject_elrond_to_ethereum_tx_batch_go() { + multiversx_sc_scenario::run_go("mandos/reject_elrond_to_ethereum_tx_batch.scen.json"); +} + +#[test] +fn setup_go() { + multiversx_sc_scenario::run_go("mandos/setup.scen.json"); +} + +#[test] +fn unstake_go() { + multiversx_sc_scenario::run_go("mandos/unstake.scen.json"); +} + +/* +#[test] +fn upgrade_child_sc_go() { + multiversx_sc_scenario::run_go("mandos/upgrade_child_sc.scen.json"); +} +*/ diff --git a/multisig/wasm/Cargo.lock b/multisig/wasm/Cargo.lock new file mode 100644 index 00000000..deda533d --- /dev/null +++ b/multisig/wasm/Cargo.lock @@ -0,0 +1,370 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", + "version_check", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bridged-tokens-wrapper" +version = "0.0.0" +dependencies = [ + "multiversx-sc", + "multiversx-sc-modules", + "transaction", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + +[[package]] +name = "esdt-safe" +version = "0.0.0" +dependencies = [ + "eth-address", + "fee-estimator-module", + "max-bridged-amount-module", + "multiversx-sc", + "multiversx-sc-modules", + "token-module", + "transaction", + "tx-batch-module", +] + +[[package]] +name = "eth-address" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "fee-estimator-module" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "max-bridged-amount-module" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "multi-transfer-esdt" +version = "0.0.0" +dependencies = [ + "bridged-tokens-wrapper", + "max-bridged-amount-module", + "multiversx-sc", + "transaction", + "tx-batch-module", +] + +[[package]] +name = "multisig" +version = "0.0.0" +dependencies = [ + "esdt-safe", + "eth-address", + "fee-estimator-module", + "max-bridged-amount-module", + "multi-transfer-esdt", + "multiversx-sc", + "multiversx-sc-modules", + "token-module", + "transaction", + "tx-batch-module", +] + +[[package]] +name = "multisig-wasm" +version = "0.0.0" +dependencies = [ + "multisig", + "multiversx-sc-wasm-adapter", +] + +[[package]] +name = "multiversx-sc" +version = "0.39.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31561133b64837f1a7835ae4ba96383ab07d9ce401e703ad2a37aef45789a1e7" +dependencies = [ + "bitflags", + "hashbrown", + "hex-literal", + "multiversx-sc-codec", + "multiversx-sc-derive", + "num-traits", +] + +[[package]] +name = "multiversx-sc-codec" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7638cb46a0e99c636fd55443ac534ff0a5fad0bd772e1037fbac9a75e04c3c9" +dependencies = [ + "arrayvec", + "multiversx-sc-codec-derive", + "wee_alloc", +] + +[[package]] +name = "multiversx-sc-codec-derive" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e976002d51367f16140929c10ee695f95dd8d34c150a45db60d3fcd1328a267a" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "multiversx-sc-derive" +version = "0.39.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23187464277575f8055c92cffbd664592825591f5cebb8dae038d2af04c5e639" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "radix_trie", + "syn", +] + +[[package]] +name = "multiversx-sc-modules" +version = "0.39.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbcb0b06f37396484f01d89295091db401b8b515ab781e3580a6bb333e912591" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "multiversx-sc-wasm-adapter" +version = "0.39.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af5fee54f1498ec8181593cd54a96ef7436543ad78848e2e805b9b62c27c4095" +dependencies = [ + "multiversx-sc", + "wee_alloc", +] + +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "proc-macro2" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "token-module" +version = "0.0.0" +dependencies = [ + "fee-estimator-module", + "multiversx-sc", +] + +[[package]] +name = "transaction" +version = "0.0.0" +dependencies = [ + "eth-address", + "multiversx-sc", +] + +[[package]] +name = "tx-batch-module" +version = "0.0.0" +dependencies = [ + "multiversx-sc", + "transaction", +] + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/multisig/wasm/Cargo.toml b/multisig/wasm/Cargo.toml new file mode 100644 index 00000000..cbe703b5 --- /dev/null +++ b/multisig/wasm/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "multisig-wasm" +version = "0.0.0" +authors = ["Andrei Marinica , Dorin Iancu "] +edition = "2018" +publish = false + +[lib] +crate-type = ["cdylib"] +[profile.release] +codegen-units = 1 +opt-level = "z" +lto = true +debug = false +panic = "abort" +[dependencies.multisig] +path = ".." + +[dependencies.multiversx-sc-wasm-adapter] +version = "0.41.3" + +[workspace] +members = ["."] diff --git a/multisig/wasm/src/lib.rs b/multisig/wasm/src/lib.rs new file mode 100644 index 00000000..4cf423c2 --- /dev/null +++ b/multisig/wasm/src/lib.rs @@ -0,0 +1,85 @@ +// Code generated by the multiversx-sc multi-contract system. DO NOT EDIT. + +//////////////////////////////////////////////////// +////////////////// AUTO-GENERATED ////////////////// +//////////////////////////////////////////////////// + +// Init: 1 +// Endpoints: 61 +// Async Callback (empty): 1 +// Total number of exported functions: 63 + +#![no_std] +#![feature(alloc_error_handler, lang_items)] + +multiversx_sc_wasm_adapter::allocator!(); +multiversx_sc_wasm_adapter::panic_handler!(); + +multiversx_sc_wasm_adapter::endpoints! { + multisig + ( + distributeFeesFromChildContracts + stake + unstake + proposeEsdtSafeSetCurrentTransactionBatchStatus + proposeMultiTransferEsdtBatch + moveRefundBatchToSafe + performAction + sign + upgradeChildContractFromSource + addBoardMember + removeUser + slashBoardMember + changeQuorum + addMapping + clearMapping + pauseEsdtSafe + unpauseEsdtSafe + changeFeeEstimatorContractAddress + changeElrondToEthGasLimit + changeDefaultPricePerGasUnit + changeTokenTicker + esdtSafeAddTokenToWhitelist + esdtSafeRemoveTokenFromWhitelist + esdtSafeSetMaxTxBatchSize + esdtSafeSetMaxTxBatchBlockDuration + esdtSafeSetMaxBridgedAmountForToken + multiTransferEsdtSetMaxBridgedAmountForToken + multiTransferEsdtSetMaxRefundTxBatchSize + multiTransferEsdtSetMaxRefundTxBatchBlockDuration + multiTransferEsdtSetWrappingContractAddress + getQuorum + getNumBoardMembers + getRequiredStakeAmount + getAmountStaked + getSlashAmount + getSlashedTokensAmount + getLastExecutedEthBatchId + getLastExecutedEthTxId + getErc20AddressForTokenId + getTokenIdForErc20Address + getEsdtSafeAddress + getMultiTransferEsdtAddress + getCurrentTxBatch + getCurrentRefundBatch + wasActionExecuted + wasTransferActionProposed + getActionIdForTransferBatch + wasSetCurrentTransactionBatchStatusActionProposed + getActionIdForSetCurrentTransactionBatchStatus + signed + userRole + getAllBoardMembers + getAllStakedRelayers + getActionSignerCount + getActionValidSignerCount + quorumReached + getActionLastIndex + getActionData + pause + unpause + isPaused + ) +} + +multiversx_sc_wasm_adapter::empty_callback! {} diff --git a/neversea/Cargo.toml b/neversea/Cargo.toml deleted file mode 100644 index ebc29439..00000000 --- a/neversea/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "neversea" -version = "0.0.0" -authors = [ "you",] -edition = "2018" -publish = false - -[lib] -path = "src/neversea.rs" - -[dependencies.elrond-wasm] -version = "0.36.0" - -[dev-dependencies.elrond-wasm-debug] -version = "0.36.0" diff --git a/neversea/meta/Cargo.toml b/neversea/meta/Cargo.toml deleted file mode 100644 index 75923613..00000000 --- a/neversea/meta/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "neversea-meta" -version = "0.0.0" -edition = "2018" -publish = false -authors = [ "you",] - -[dev-dependencies] - -[dependencies.neversea] -path = ".." - -[dependencies.elrond-wasm-debug] -version = "0.36.0" diff --git a/neversea/meta/src/main.rs b/neversea/meta/src/main.rs deleted file mode 100644 index 067c8ebe..00000000 --- a/neversea/meta/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - elrond_wasm_debug::meta::perform::(); -} diff --git a/neversea/src/neversea.rs b/neversea/src/neversea.rs deleted file mode 100644 index 8e1bc1dd..00000000 --- a/neversea/src/neversea.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![no_std] - -elrond_wasm::imports!(); - -#[elrond_wasm::derive::contract] -pub trait Neversea { - #[init] - fn init(&self, initial_registration_fee: BigUint) { - self.registration_fee().set(BigUint::from(initial_registration_fee)); - } - - /// Add desired amount to the storage variable. - #[endpoint] - fn register(&self) { - let caller = self.blockchain().get_caller(); - let payment_amount = self.call_value().egld_value(); - require!(payment_amount == BigUint::from(self.registration_fee().get()), "Registration fee is incorrect; please check and try again"); - self.participants().insert(caller); - } - - #[view(getParticipants)] - #[storage_mapper("participants")] - fn participants(&self) -> UnorderedSetMapper; - - #[view(getRegistrationFee)] - #[storage_mapper("registration_fee")] - fn registration_fee(&self) -> SingleValueMapper; - - -} diff --git a/neversea/wallets/observers/observerKey00.pem b/neversea/wallets/observers/observerKey00.pem deleted file mode 100644 index d864d6bb..00000000 --- a/neversea/wallets/observers/observerKey00.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for 9efb9802f23d3f0eb3ccbbc872925253cb30920b1ae72150c920fb8df05aae2f4d75d2437bbc94bc27750bf4cdaaef0301ba31ba71b8f7f0cffa4d93b04fc5b45f3fef2aab616743bb5383f63312d2da40e4bf611b3c3ebef9469e99cf303011----- -MzJlZWVmNDFhMDI2NDQ1ZTY0NzZiMTEwZjhiYThhYjg1MmVjNWI4MzI3MGUwMzA5 -MmQyMTgwZDFjMjQyZDU2MA== ------END PRIVATE KEY for 9efb9802f23d3f0eb3ccbbc872925253cb30920b1ae72150c920fb8df05aae2f4d75d2437bbc94bc27750bf4cdaaef0301ba31ba71b8f7f0cffa4d93b04fc5b45f3fef2aab616743bb5383f63312d2da40e4bf611b3c3ebef9469e99cf303011----- diff --git a/neversea/wallets/observers/observerKey01.pem b/neversea/wallets/observers/observerKey01.pem deleted file mode 100644 index e9c8ee6d..00000000 --- a/neversea/wallets/observers/observerKey01.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for 266afa7253c4f678ef147bdf9020b0a74544ad1072f3bb8845e21878b4bfb3015e3a6ff77b6dcc151d22f90c0e7d7719fdb2d4f99e1cc6b859197a3c315a8ddc6dd032a36133d81949301866c8c33b091eb6bcb834b5a60a8c61503715fcbd0f----- -N2VkZDIzY2JhZWJkOWVhOWFkNmRmNDkxMjIwY2E0YjVjZTMyODhhMmU2NWYyMjcz -NmI4NDgwYTc0Zjc2M2E3MQ== ------END PRIVATE KEY for 266afa7253c4f678ef147bdf9020b0a74544ad1072f3bb8845e21878b4bfb3015e3a6ff77b6dcc151d22f90c0e7d7719fdb2d4f99e1cc6b859197a3c315a8ddc6dd032a36133d81949301866c8c33b091eb6bcb834b5a60a8c61503715fcbd0f----- diff --git a/neversea/wallets/observers/observerKey02.pem b/neversea/wallets/observers/observerKey02.pem deleted file mode 100644 index 9f5f0e31..00000000 --- a/neversea/wallets/observers/observerKey02.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for 7c1d7e50301894946def4a9128ddc051f0f0343d733cb76cddb92dcaf62eed178c6bf5714dfb1537259d827e31a6690eb2a5a140c93ba95bed533a83ac036b64769bf74077c06ed2aec584c41fb95747173c0e2b2d301ade6b8631f191a51c80----- -OWUxYzgwNTExYmU0NTgyNWJhNWJjOGY5NTI3OTExOTk5ODBiYzYyODI3ZTc4OTk3 -YmY2YjRhMmM5M2NjNzA2Mw== ------END PRIVATE KEY for 7c1d7e50301894946def4a9128ddc051f0f0343d733cb76cddb92dcaf62eed178c6bf5714dfb1537259d827e31a6690eb2a5a140c93ba95bed533a83ac036b64769bf74077c06ed2aec584c41fb95747173c0e2b2d301ade6b8631f191a51c80----- diff --git a/neversea/wallets/observers/observerKey03.pem b/neversea/wallets/observers/observerKey03.pem deleted file mode 100644 index bb7f0c82..00000000 --- a/neversea/wallets/observers/observerKey03.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for 16ab240028c0c900bf7193fae7d5f180f898a7041a52994d6357dce28857a5e7bdfc12e85fa712c69bd6cb70215f490f1fbecffee0a9d0c997e7240e8f4a380a192a1872f4e06a2f0c5892919f4fe10cecbffcecea07c67bf96e96a85b8aeb18----- -NzcwMmQ3NWE0MGY0ZDQzOTFkYjJhOGZkMjk0ZGNiZTljYzk1ZDg2MThkNjYwNDk1 -YTNiODJmY2U1MzQ4MGI1MQ== ------END PRIVATE KEY for 16ab240028c0c900bf7193fae7d5f180f898a7041a52994d6357dce28857a5e7bdfc12e85fa712c69bd6cb70215f490f1fbecffee0a9d0c997e7240e8f4a380a192a1872f4e06a2f0c5892919f4fe10cecbffcecea07c67bf96e96a85b8aeb18----- diff --git a/neversea/wallets/observers/observerKey04.pem b/neversea/wallets/observers/observerKey04.pem deleted file mode 100644 index de1c40f3..00000000 --- a/neversea/wallets/observers/observerKey04.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for c5c99dc118ac45180f51623a3b6a510fcc2ed0f5b475f0089b9165bed6e0eebb26fdf9e9d3d29589a3ee3ddb696f800acbb64eb92035b6ee96c2a9b6d42d5163335dd7a26531a3a9de94f58ecd1d988fcb293dbe6594b014615898d58ea3a704----- -NjA1MDI3YzZjYzc1YjZhODFkZjMyMWExZDg0OGUwNjdjYjJlNzg1MjFmZTUxOWE3 -OWM4YzhlNWM2NjgyNDYzNw== ------END PRIVATE KEY for c5c99dc118ac45180f51623a3b6a510fcc2ed0f5b475f0089b9165bed6e0eebb26fdf9e9d3d29589a3ee3ddb696f800acbb64eb92035b6ee96c2a9b6d42d5163335dd7a26531a3a9de94f58ecd1d988fcb293dbe6594b014615898d58ea3a704----- diff --git a/neversea/wallets/observers/observerKey05.pem b/neversea/wallets/observers/observerKey05.pem deleted file mode 100644 index f743b414..00000000 --- a/neversea/wallets/observers/observerKey05.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for f6434d45a1c8873d2a0a62f2235ad7b452b07bb01df68ccf2bbbe4ffecf50d55ce42af03feba6ce2f8f8439b28978b0da7bf61ee7d659884033298dc9d6e629c1425b76ccce8fc7a1af33d9e464c446182425a32d7cf1d2d3cd0a60263af1109----- -MmQ4ZmJjNTBmNTRiNzc5YjFiYjRjZmI3MjMwYTU5MDdmZThjMjU4ZmU3NDgyZDkx -ZGE1OWVhNjY5Yzg4MWUwYw== ------END PRIVATE KEY for f6434d45a1c8873d2a0a62f2235ad7b452b07bb01df68ccf2bbbe4ffecf50d55ce42af03feba6ce2f8f8439b28978b0da7bf61ee7d659884033298dc9d6e629c1425b76ccce8fc7a1af33d9e464c446182425a32d7cf1d2d3cd0a60263af1109----- diff --git a/neversea/wallets/observers/observerKey06.pem b/neversea/wallets/observers/observerKey06.pem deleted file mode 100644 index ec26b9ce..00000000 --- a/neversea/wallets/observers/observerKey06.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for 53cfaf08d2b3c4eb0c5900a84de974cd7f3a2aaa89128015d414eb9774a733435f14a065cdd65cf2aa27d352debdf009dae86baef54ae43181ed2eaf2b22f6e294c05a667c17708db766233c778f2f1585abadb1744ffecaf403c82804c80783----- -MjA3NmEwNjkyNTRjOGExMWJjZWNkNzk5YzI0ZWFmMDcwNjQ3NmFhNzNlYmI4ZmM1 -ZDg2YTMzNWRjYzZiOTMxYg== ------END PRIVATE KEY for 53cfaf08d2b3c4eb0c5900a84de974cd7f3a2aaa89128015d414eb9774a733435f14a065cdd65cf2aa27d352debdf009dae86baef54ae43181ed2eaf2b22f6e294c05a667c17708db766233c778f2f1585abadb1744ffecaf403c82804c80783----- diff --git a/neversea/wallets/observers/observerKey07.pem b/neversea/wallets/observers/observerKey07.pem deleted file mode 100644 index 69aa8bd0..00000000 --- a/neversea/wallets/observers/observerKey07.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for fc4a2b8af95bce42642451639e52c9aa6cae2ed66e8f4fff4820f554988d5435554af2afbe718e309f6b365c6776c60c6af8a3803934389a991522fa3f84bf82a893a063fe635755f6c9aebab259b950ea306bfbf80d6ea918831e38c1c19e85----- -ZmM3ZDkwMzM2ZGNhZTQ2ZDNjYzY3NjczYTczYzEzZTI2NDI1MWYwOTZmOGM4NTY1 -NzhmZmFhNmZkYjg5NmQ2YQ== ------END PRIVATE KEY for fc4a2b8af95bce42642451639e52c9aa6cae2ed66e8f4fff4820f554988d5435554af2afbe718e309f6b365c6776c60c6af8a3803934389a991522fa3f84bf82a893a063fe635755f6c9aebab259b950ea306bfbf80d6ea918831e38c1c19e85----- diff --git a/neversea/wallets/observers/observerKey08.pem b/neversea/wallets/observers/observerKey08.pem deleted file mode 100644 index 9fb4f147..00000000 --- a/neversea/wallets/observers/observerKey08.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for a8cd91bc3c40e7ef01114bda5c7be72723d7c84b4cad2515b6b9b14a1d1b23651c9ccd2dc893e588e6d5eaa635a2df01f9dab5312ce8041c085a575b817e7ab4f83b8ba654344ec9541878c5ee7dca6714f0c82b760aafc488758ec9142ad087----- -ODEwMWEzNThmNjNjNmNlMTdjM2FlMmRmZjU2NjNkZmIyOTBhMzMzMzI3ZGNlY2Rl -MWQyMTIwYmFlY2IzZGQ1MQ== ------END PRIVATE KEY for a8cd91bc3c40e7ef01114bda5c7be72723d7c84b4cad2515b6b9b14a1d1b23651c9ccd2dc893e588e6d5eaa635a2df01f9dab5312ce8041c085a575b817e7ab4f83b8ba654344ec9541878c5ee7dca6714f0c82b760aafc488758ec9142ad087----- diff --git a/neversea/wallets/observers/observerKey09.pem b/neversea/wallets/observers/observerKey09.pem deleted file mode 100644 index 5f4ad283..00000000 --- a/neversea/wallets/observers/observerKey09.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for cc7910e60f9cf86bead820b6a6cf7acbf743c143efbc213b08501009b80fc75edde78a9655006b7be107af514556fc034633fb01c8915c9de9257b8db5afd3fb7a696aadd9a7a815c0d8946f4f600f1d9290cdfcaf295c704baa7d1dc61c0703----- -MWVhODY0YWU2YTRkOTM5YzAzZDA3MzM4M2I2MjczOWUxZjNlYmNkOWJkN2ZlYzU0 -ZWQ3MmE5ZTRlYzI1NzAyMQ== ------END PRIVATE KEY for cc7910e60f9cf86bead820b6a6cf7acbf743c143efbc213b08501009b80fc75edde78a9655006b7be107af514556fc034633fb01c8915c9de9257b8db5afd3fb7a696aadd9a7a815c0d8946f4f600f1d9290cdfcaf295c704baa7d1dc61c0703----- diff --git a/neversea/wallets/observers/observerKey10.pem b/neversea/wallets/observers/observerKey10.pem deleted file mode 100644 index 8ee13014..00000000 --- a/neversea/wallets/observers/observerKey10.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for 6b04b1d8ef8bec3d61626e81309b28fbd4c4318113897ba1db4c6d0b5e8d10cce0fd0c2bff8f56473bb23a43b0df750706674a734bea66dab6a67f5c3ce19d6e238e83342e6fa8db2f9809ef9280ac3691a43cb898953508f283b8074a1fc109----- -ZmE3MWExNWVkM2UxMTEwZjBhYjYxY2E1NGZkMzc0N2Y4MTQ1MmU4ZWZhOWE2ZTUx -ZjVmZDIwNjE5MmI5OWYyYQ== ------END PRIVATE KEY for 6b04b1d8ef8bec3d61626e81309b28fbd4c4318113897ba1db4c6d0b5e8d10cce0fd0c2bff8f56473bb23a43b0df750706674a734bea66dab6a67f5c3ce19d6e238e83342e6fa8db2f9809ef9280ac3691a43cb898953508f283b8074a1fc109----- diff --git a/neversea/wallets/observers/observerKey11.pem b/neversea/wallets/observers/observerKey11.pem deleted file mode 100644 index 47d6b8e9..00000000 --- a/neversea/wallets/observers/observerKey11.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for a83933cce974c31f20ffd42305f6c6b7ed765ff2dbd90338a822f516ef34706652e200ecaf0ef0f8b9ca2e19d65685021547b27808a07ffbeb6f528827a706554b4606b6ceab6d77e838c0578f0ae41d571ab4e69e3fcc8de49613f341fa938d----- -MTk4N2UwNTlmYzVkOWRhNDJiMzAzYzkyZWJkYzk3ZjNlZWExYzNiZWRiY2VlOWNm -NDI0MmYzNTA0OWFkMGIzNQ== ------END PRIVATE KEY for a83933cce974c31f20ffd42305f6c6b7ed765ff2dbd90338a822f516ef34706652e200ecaf0ef0f8b9ca2e19d65685021547b27808a07ffbeb6f528827a706554b4606b6ceab6d77e838c0578f0ae41d571ab4e69e3fcc8de49613f341fa938d----- diff --git a/neversea/wallets/users/alice.json b/neversea/wallets/users/alice.json deleted file mode 100644 index 9e83170c..00000000 --- a/neversea/wallets/users/alice.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "0dc10c02-b59b-4bac-9710-6b2cfa4284ba", - "address": "0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", - "bech32": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", - "crypto": { - "ciphertext": "4c41ef6fdfd52c39b1585a875eb3c86d30a315642d0e35bb8205b6372c1882f135441099b11ff76345a6f3a930b5665aaf9f7325a32c8ccd60081c797aa2d538", - "cipherparams": { - "iv": "033182afaa1ebaafcde9ccc68a5eac31" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "4903bd0e7880baa04fc4f886518ac5c672cdc745a6bd13dcec2b6c12e9bffe8d", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "5b4a6f14ab74ba7ca23db6847e28447f0e6a7724ba9664cf425df707a84f5a8b" - } -} diff --git a/neversea/wallets/users/alice.pem b/neversea/wallets/users/alice.pem deleted file mode 100644 index d27bb68b..00000000 --- a/neversea/wallets/users/alice.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th----- -NDEzZjQyNTc1ZjdmMjZmYWQzMzE3YTc3ODc3MTIxMmZkYjgwMjQ1ODUwOTgxZTQ4 -YjU4YTRmMjVlMzQ0ZThmOTAxMzk0NzJlZmY2ODg2NzcxYTk4MmYzMDgzZGE1ZDQy -MWYyNGMyOTE4MWU2Mzg4ODIyOGRjODFjYTYwZDY5ZTE= ------END PRIVATE KEY for erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th----- \ No newline at end of file diff --git a/neversea/wallets/users/bob.json b/neversea/wallets/users/bob.json deleted file mode 100644 index 439b394a..00000000 --- a/neversea/wallets/users/bob.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "85fdc8a7-7119-479d-b7fb-ab4413ed038d", - "address": "8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8", - "bech32": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", - "crypto": { - "ciphertext": "c2664a31350aaf6a00525560db75c254d0aea65dc466441356c1dd59253cceb9e83eb05730ef3f42a11573c9a0e33dd952d488f00535b35357bb41d127b1eb82", - "cipherparams": { - "iv": "18378411e31f6c4e99f1435d9ab82831" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "18304455ac2dbe2a2018bda162bd03ef95b81622e99d8275c34a6d5e6932a68b", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "23756172195ac483fa29025dc331bc7aa2c139533922a8dc08642eb0a677541f" - } -} diff --git a/neversea/wallets/users/bob.pem b/neversea/wallets/users/bob.pem deleted file mode 100644 index 00b5bc4e..00000000 --- a/neversea/wallets/users/bob.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx----- -YjhjYTZmODIwM2ZiNGI1NDVhOGU4M2M1Mzg0ZGEwMzNjNDE1ZGIxNTViNTNmYjVi -OGViYTdmZjVhMDM5ZDYzOTgwNDlkNjM5ZTVhNjk4MGQxY2QyMzkyYWJjY2U0MTAy -OWNkYTc0YTE1NjM1MjNhMjAyZjA5NjQxY2MyNjE4Zjg= ------END PRIVATE KEY for erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx----- \ No newline at end of file diff --git a/neversea/wallets/users/carol.json b/neversea/wallets/users/carol.json deleted file mode 100644 index 3614a5ba..00000000 --- a/neversea/wallets/users/carol.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "65894f35-d142-41d2-9335-6ad02e0ed0be", - "address": "b2a11555ce521e4944e09ab17549d85b487dcd26c84b5017a39e31a3670889ba", - "bech32": "erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8", - "crypto": { - "ciphertext": "bdfb984a1e7c7460f0a289749609730cdc99d7ce85b59305417c2c0f007b2a6aaa7203dd94dbf27315bced39b0b281769fbc70b01e6e57f89ae2f2a9e9100007", - "cipherparams": { - "iv": "258ed2b4dc506b4dc9d274b0449b0eb0" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "4f2f5530ce28dc0210962589b908f52714f75c8fb79ff18bdd0024c43c7a220b", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "f8de52e2627024eaa33f2ee5eadcd3d3815e10dd274ea966dc083d000cc8b258" - } -} diff --git a/neversea/wallets/users/carol.pem b/neversea/wallets/users/carol.pem deleted file mode 100644 index 5551c9c0..00000000 --- a/neversea/wallets/users/carol.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8----- -ZTI1M2E1NzFjYTE1M2RjMmFlZTg0NTgxOWY3NGJjYzk3NzNiMDU4NmVkZWFkMTVh -OTRjYjcyMzVhNTAyNzQzNmIyYTExNTU1Y2U1MjFlNDk0NGUwOWFiMTc1NDlkODVi -NDg3ZGNkMjZjODRiNTAxN2EzOWUzMWEzNjcwODg5YmE= ------END PRIVATE KEY for erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8----- \ No newline at end of file diff --git a/neversea/wallets/users/dan.json b/neversea/wallets/users/dan.json deleted file mode 100644 index 15eb9f79..00000000 --- a/neversea/wallets/users/dan.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "fc8b9b89-2227-41ec-afd1-5e6853feb7b2", - "address": "b13a017423c366caff8cecfb77a12610a130f4888134122c7937feae0d6d7d17", - "bech32": "erd1kyaqzaprcdnv4luvanah0gfxzzsnpaygsy6pytrexll2urtd05ts9vegu7", - "crypto": { - "ciphertext": "b2a6fffd935ea3c9bc8ac136fdb548554faf0c5e4b78b347305499c29a4ae71dfca565297bfcf85df6faf850d2a42368178937b69a0e95ee62d8f112f98ad918", - "cipherparams": { - "iv": "46e398d62d85faa06d2f424303772ea3" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "58b3e16f0fa600ca004bddd68c6487e63beb0df42d050e2eea3c838d9c664224", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "141fc44faa4376187278cef7e6a5b8b1a3d5d4932f0590bac9f0772bf21dd358" - } -} diff --git a/neversea/wallets/users/dan.pem b/neversea/wallets/users/dan.pem deleted file mode 100644 index f112d8f6..00000000 --- a/neversea/wallets/users/dan.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd1kyaqzaprcdnv4luvanah0gfxzzsnpaygsy6pytrexll2urtd05ts9vegu7----- -NTRlNDc5NzQzZTUxMjhlMGUyOWRiNGQ0YTIxZDc1OWI2OTAxMjQxZmFjNzdjNTAy -YzhlZjcwZGM3OWExMmE4YmIxM2EwMTc0MjNjMzY2Y2FmZjhjZWNmYjc3YTEyNjEw -YTEzMGY0ODg4MTM0MTIyYzc5MzdmZWFlMGQ2ZDdkMTc= ------END PRIVATE KEY for erd1kyaqzaprcdnv4luvanah0gfxzzsnpaygsy6pytrexll2urtd05ts9vegu7----- \ No newline at end of file diff --git a/neversea/wallets/users/eve.json b/neversea/wallets/users/eve.json deleted file mode 100644 index b95f2582..00000000 --- a/neversea/wallets/users/eve.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "e770c455-a23b-4dcd-a7a5-0e22375dc233", - "address": "3af8d9c9423b2577c6252722c1d90212a4111f7203f9744f76fcfa1d0a310033", - "bech32": "erd18tudnj2z8vjh0339yu3vrkgzz2jpz8mjq0uhgnmklnap6z33qqeszq2yn4", - "crypto": { - "ciphertext": "ebabff99b80b867154cfc24726d47a58a6dfc2bf056e9fe0257841287f2f496c443533583d2cbf28e796f38186b3145774007876e93eca83d815b396cfe7f89a", - "cipherparams": { - "iv": "abc1ada087b472a0154e869f16ad3b2f" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "d1a57ea919706bdad2916bc4ebf5bc072843b1333818808a424d34b8cd851491", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "55b8bd2cada5766704ac9b70742cace95f5faa66a7f5edf7c9e3b31755710caf" - } -} diff --git a/neversea/wallets/users/eve.pem b/neversea/wallets/users/eve.pem deleted file mode 100644 index dcbe731f..00000000 --- a/neversea/wallets/users/eve.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd18tudnj2z8vjh0339yu3vrkgzz2jpz8mjq0uhgnmklnap6z33qqeszq2yn4----- -NjliODJmMzlhMTQ2MWI0MzFmOGZhMjY3Zjg3YTBiZjc5NDAyN2NlYTM3ZGM2NjE5 -MGU2NzQwYTZlNGNjY2RmMDNhZjhkOWM5NDIzYjI1NzdjNjI1MjcyMmMxZDkwMjEy -YTQxMTFmNzIwM2Y5NzQ0Zjc2ZmNmYTFkMGEzMTAwMzM= ------END PRIVATE KEY for erd18tudnj2z8vjh0339yu3vrkgzz2jpz8mjq0uhgnmklnap6z33qqeszq2yn4----- \ No newline at end of file diff --git a/neversea/wallets/users/frank.json b/neversea/wallets/users/frank.json deleted file mode 100644 index afb4a0f6..00000000 --- a/neversea/wallets/users/frank.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "df70f3ef-bb40-4afd-8751-77b26b29356d", - "address": "b37f5d130beb8885b90ab574a8bfcdd894ca531a7d3d1f3431158d77d6185fbb", - "bech32": "erd1kdl46yctawygtwg2k462307dmz2v55c605737dp3zkxh04sct7asqylhyv", - "crypto": { - "ciphertext": "50e0992457078c3b1b5512e003ab911065fb64429dbc0dbec4d425670ecf410807de7ac5558568f70fac0c003ee9d090831e4e0801add602b89f0163735d11e8", - "cipherparams": { - "iv": "264b13aee12e96a25ad5dc91518cdfdb" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "fc05efc44099f837d3fdf1741f86f97abe394ed6c9882ff26d35bc6deab72a53", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "5308637cdac70085b775f4c7e55229530b8139aef4c3f26b7855cbc96317584c" - } -} diff --git a/neversea/wallets/users/frank.pem b/neversea/wallets/users/frank.pem deleted file mode 100644 index 6d04639d..00000000 --- a/neversea/wallets/users/frank.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd1kdl46yctawygtwg2k462307dmz2v55c605737dp3zkxh04sct7asqylhyv----- -NzgyMGE5MTE4OWM3MjdjMDc1YjQ5OWM1MjNmMGRjNDAzZmVkMzc5OTBlYzhkMzdm -NGJkZWNiOGI2OGZkMmE5N2IzN2Y1ZDEzMGJlYjg4ODViOTBhYjU3NGE4YmZjZGQ4 -OTRjYTUzMWE3ZDNkMWYzNDMxMTU4ZDc3ZDYxODVmYmI= ------END PRIVATE KEY for erd1kdl46yctawygtwg2k462307dmz2v55c605737dp3zkxh04sct7asqylhyv----- \ No newline at end of file diff --git a/neversea/wallets/users/grace.json b/neversea/wallets/users/grace.json deleted file mode 100644 index 81d640dc..00000000 --- a/neversea/wallets/users/grace.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "9aff338f-f504-403c-86cd-5e623bc81c42", - "address": "1e8a8b6b49de5b7be10aaa158a5a6a4abb4b56cc08f524bb5e6cd5f211ad3e13", - "bech32": "erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede", - "crypto": { - "ciphertext": "b8f4fe8790a980fb78e5b126c52e81009910101210420da1da0d558313a45eb8803b57825bc19a2a8b92d7af147f97a0bf261ce152bc61cccd6e34df4aa9ab09", - "cipherparams": { - "iv": "0be21668939564c5b2ac122a71ce97a4" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "7c5b1a6f91d257a11b327b9745a27bb4708867e17a1cce942cd9a3a3d74e7bf0", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "a3c251303a253d82ae2be6535a65d0584f6bb92a84199d7f0c6eff249b46ce9a" - } -} diff --git a/neversea/wallets/users/grace.pem b/neversea/wallets/users/grace.pem deleted file mode 100644 index 7b0836a5..00000000 --- a/neversea/wallets/users/grace.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede----- -ODY5Yzk4ZGYwYjExZmRhMDgwNGQ3ODM1NGVjYTQzMWY4N2E2ZTkxMGZhZTE0MDgx -ZjczOGMzMTZhMjVlYTQwOTFlOGE4YjZiNDlkZTViN2JlMTBhYWExNThhNWE2YTRh -YmI0YjU2Y2MwOGY1MjRiYjVlNmNkNWYyMTFhZDNlMTM= ------END PRIVATE KEY for erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede----- \ No newline at end of file diff --git a/neversea/wallets/users/heidi.json b/neversea/wallets/users/heidi.json deleted file mode 100644 index 7608b981..00000000 --- a/neversea/wallets/users/heidi.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "1b55836f-946f-4dc3-946d-3c27e5096873", - "address": "6e224118d9068ae626878a1cfbebcb6a95a4715db86d1b51e06a04226cf30fd6", - "bech32": "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha", - "crypto": { - "ciphertext": "04d8c15a32a8f5917980f8a1cc16adae29c1241ac42c917d154dbaced47c3208d372378eaa39e201785db891dfe397e33639cf23abcbeddd7e2ce38944e7e30c", - "cipherparams": { - "iv": "053caa5b1b236e786af178421fc0b829" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "ec9bbfb8bceb60545b4500cb7990897aaf1ab51fa0154e7a2917c30759a4cfe5", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "b326a517fea8e38af156e2c7d256144ada0f8ab6740d04faf2a8f98a54dff6c9" - } -} diff --git a/neversea/wallets/users/heidi.pem b/neversea/wallets/users/heidi.pem deleted file mode 100644 index a8a693f2..00000000 --- a/neversea/wallets/users/heidi.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha----- -YjMzODcyMjMzMjFjNTg3YzM1MTE5OGFkYzI1MzFjODIwYTM3OGQzNzQ4OTU2YjNk -YWUyZTBmNWExZGFhN2NhMTZlMjI0MTE4ZDkwNjhhZTYyNjg3OGExY2ZiZWJjYjZh -OTVhNDcxNWRiODZkMWI1MWUwNmEwNDIyNmNmMzBmZDY= ------END PRIVATE KEY for erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha----- \ No newline at end of file diff --git a/neversea/wallets/users/ivan.json b/neversea/wallets/users/ivan.json deleted file mode 100644 index ba0f7f34..00000000 --- a/neversea/wallets/users/ivan.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "0b80d732-d4e6-4145-b5bb-698bdd323b3c", - "address": "899451b361a83e89d73b4096d3c90c209b27874c9c7cb01bb08b0bb4dc15693d", - "bech32": "erd13x29rvmp4qlgn4emgztd8jgvyzdj0p6vn37tqxas3v9mfhq4dy7shalqrx", - "crypto": { - "ciphertext": "2c7b56ea9ebec244382018818065a78f19cec328596b28eca2fa51defd22098f3c0164fbcb63b144f0c416fbefbf147e4df17c41eaed7caba2feb7fcdb7ec4e5", - "cipherparams": { - "iv": "f20e0d3fa965ef6c3084fdb712c7fcd9" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "a12bab6dd6df4eb7b841214ab26ad7724b8be3a52873f7a3565ba65b85831028", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "0a1c6c7a17c39b4882d00910b4bca532dd215b1e0b914f9624920ae1ac8b38c6" - } -} diff --git a/neversea/wallets/users/ivan.pem b/neversea/wallets/users/ivan.pem deleted file mode 100644 index e9cdb61a..00000000 --- a/neversea/wallets/users/ivan.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd13x29rvmp4qlgn4emgztd8jgvyzdj0p6vn37tqxas3v9mfhq4dy7shalqrx----- -MDhjNjE1NTUyMjFmMWYxMjE0M2Q3MWJiZWQ4MzFkMjk0N2U0OTRhMjJjZmQxN2I1 -NzQxMzhiY2M5ODQ1MzZkMzg5OTQ1MWIzNjFhODNlODlkNzNiNDA5NmQzYzkwYzIw -OWIyNzg3NGM5YzdjYjAxYmIwOGIwYmI0ZGMxNTY5M2Q= ------END PRIVATE KEY for erd13x29rvmp4qlgn4emgztd8jgvyzdj0p6vn37tqxas3v9mfhq4dy7shalqrx----- \ No newline at end of file diff --git a/neversea/wallets/users/judy.json b/neversea/wallets/users/judy.json deleted file mode 100644 index 286c9ffd..00000000 --- a/neversea/wallets/users/judy.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "a3108434-5d2c-4dca-9f94-29e822697e20", - "address": "4a101a0f8f95f1218683900801cd971c6028b1597a771b2ed367d1ede09d9d2a", - "bech32": "erd1fggp5ru0jhcjrp5rjqyqrnvhr3sz3v2e0fm3ktknvlg7mcyan54qzccnan", - "crypto": { - "ciphertext": "8ae4807bc0d836606bf015ffde34274ddc454a61448bff6fc7a250a8530c5d478bc3988b8c3a3c631eb657e7fc5289cdfdcbb8f77be6faefb32ca77179636c73", - "cipherparams": { - "iv": "41f1833a14d4675cab19dd96bca5eb44" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "6f5f765b00ece2f9dc654f008a21f7458d11aa53b56d119c90a29a9cc46c64f6", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "01314543ab6866bba660952b697bc4555bc040770537be7409ceff83057b170f" - } -} diff --git a/neversea/wallets/users/judy.pem b/neversea/wallets/users/judy.pem deleted file mode 100644 index 23808c01..00000000 --- a/neversea/wallets/users/judy.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd1fggp5ru0jhcjrp5rjqyqrnvhr3sz3v2e0fm3ktknvlg7mcyan54qzccnan----- -MDgzZmQ2ZjAxYzRjMGZiMjUzY2QzOGMxMTE5OThiNGEzYjUzZDQyOTAwOGI4Mjhl -NzZhYzk3ZGE2ZjU4NTgyYTRhMTAxYTBmOGY5NWYxMjE4NjgzOTAwODAxY2Q5NzFj -NjAyOGIxNTk3YTc3MWIyZWQzNjdkMWVkZTA5ZDlkMmE= ------END PRIVATE KEY for erd1fggp5ru0jhcjrp5rjqyqrnvhr3sz3v2e0fm3ktknvlg7mcyan54qzccnan----- \ No newline at end of file diff --git a/neversea/wallets/users/mallory.json b/neversea/wallets/users/mallory.json deleted file mode 100644 index 1416c2f1..00000000 --- a/neversea/wallets/users/mallory.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "6756aa76-5934-4dc6-90c7-c261db98fe44", - "address": "1454931ffa758ab35654a5206b28e9dbf1fb8df8f9ced093bb2887eb39f7e7af", - "bech32": "erd1z32fx8l6wk9tx4j555sxk28fm0clhr0cl88dpyam9zr7kw0hu7hsx2j524", - "crypto": { - "ciphertext": "b05925b7f3c3eaec07258987848346355f66be347e748defb7064b34f3104a625e1f0b0046ec89618dcf70caa75a5c62a1a8b10563a0854bb068d99427697d4a", - "cipherparams": { - "iv": "673d0b58985d74e061288a41e9a5c0d8" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "a313394c586b66d5fb1fad55fb02528113e2cce9f5663a9af4ca8fb794ad7ddf", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "54039bd973c972e7d21b87589fc6cb5758e72a73afcc92944d5d14ed403a895c" - } -} diff --git a/neversea/wallets/users/mallory.pem b/neversea/wallets/users/mallory.pem deleted file mode 100644 index be452cd0..00000000 --- a/neversea/wallets/users/mallory.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd1z32fx8l6wk9tx4j555sxk28fm0clhr0cl88dpyam9zr7kw0hu7hsx2j524----- -OTVjMWMwMzRlM2NmNzczNzdmMTU3NTFhYWZjZmZiMmEyZWIyZDE2NDAxYzY3MTQ0 -NDIyZTFkMDNlYzIwYWM1MjE0NTQ5MzFmZmE3NThhYjM1NjU0YTUyMDZiMjhlOWRi -ZjFmYjhkZjhmOWNlZDA5M2JiMjg4N2ViMzlmN2U3YWY= ------END PRIVATE KEY for erd1z32fx8l6wk9tx4j555sxk28fm0clhr0cl88dpyam9zr7kw0hu7hsx2j524----- \ No newline at end of file diff --git a/neversea/wallets/users/mike.json b/neversea/wallets/users/mike.json deleted file mode 100644 index d46a9e7b..00000000 --- a/neversea/wallets/users/mike.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "3f6adbc3-1215-4c31-9a61-a049b430e6f7", - "address": "e32afedc904fe1939746ad973beb383563cf63642ba669b3040f9b9428a5ed60", - "bech32": "erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa", - "crypto": { - "ciphertext": "39a3ee873e39b30dd486dfe36fb9f8d8fb7bdaa83b30a35ac823acb0f69fb97413ff916afc6184888b60ffbe068a99c9109bb0291e21eae8c9556527263bbde7", - "cipherparams": { - "iv": "e99cc4008d995919b0da66aa94f95991" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "44f6da2344cf56a23c7ebdc22033caab21adf0ff18144ddf513b5a56f7f04eb5", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "eba324243f326ac2fd78c339962822147b8f01cd13c40fe019cf3b7834657d31" - } -} diff --git a/neversea/wallets/users/mike.pem b/neversea/wallets/users/mike.pem deleted file mode 100644 index b2c01e67..00000000 --- a/neversea/wallets/users/mike.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa----- -Y2JkMjYzNThmMDg0YWZlMDA0ZDUzZjQ2NzMwM2Q3MTMzZjU1MzBiYjhkODE2NzUx -MWI3YzEyNjg1YWJjNTY4N2UzMmFmZWRjOTA0ZmUxOTM5NzQ2YWQ5NzNiZWIzODM1 -NjNjZjYzNjQyYmE2NjliMzA0MGY5Yjk0MjhhNWVkNjA= ------END PRIVATE KEY for erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa----- \ No newline at end of file diff --git a/neversea/wallets/users/mnemonic.txt b/neversea/wallets/users/mnemonic.txt deleted file mode 100644 index a6c1bd69..00000000 --- a/neversea/wallets/users/mnemonic.txt +++ /dev/null @@ -1 +0,0 @@ -moral volcano peasant pass circle pen over picture flat shop clap goat never lyrics gather prepare woman film husband gravity behind test tiger improve \ No newline at end of file diff --git a/neversea/wallets/users/password.txt b/neversea/wallets/users/password.txt deleted file mode 100644 index 7aa311ad..00000000 --- a/neversea/wallets/users/password.txt +++ /dev/null @@ -1 +0,0 @@ -password \ No newline at end of file diff --git a/neversea/wallets/validators/mnemonic.txt b/neversea/wallets/validators/mnemonic.txt deleted file mode 100644 index e56700ae..00000000 --- a/neversea/wallets/validators/mnemonic.txt +++ /dev/null @@ -1 +0,0 @@ -teach wonder cheap pottery merge jeans ketchup spider federal agent armed tobacco myth priority hurt pony thing dune switch grace jewel chunk luggage genius \ No newline at end of file diff --git a/neversea/wallets/validators/password.txt b/neversea/wallets/validators/password.txt deleted file mode 100644 index 7aa311ad..00000000 --- a/neversea/wallets/validators/password.txt +++ /dev/null @@ -1 +0,0 @@ -password \ No newline at end of file diff --git a/neversea/wallets/validators/validatorKey00.pem b/neversea/wallets/validators/validatorKey00.pem deleted file mode 100644 index 50a57c40..00000000 --- a/neversea/wallets/validators/validatorKey00.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208----- -N2NmZjk5YmQ2NzE1MDJkYjdkMTViYzhhYmMwYzlhODA0ZmI5MjU0MDZmYmRkNTBm -MWU0YzE3YTRjZDc3NDI0Nw== ------END PRIVATE KEY for e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208----- diff --git a/neversea/wallets/validators/validatorKey01.pem b/neversea/wallets/validators/validatorKey01.pem deleted file mode 100644 index 4035dca5..00000000 --- a/neversea/wallets/validators/validatorKey01.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for 78689fd4b1e2e434d567fe01e61598a42717d83124308266bd09ccc15d2339dd318c019914b86ac29adbae5dd8a02d0307425e9bd85a296e94943708c72f8c670f0b7c50a890a5719088dbd9f1d062cad9acffa06df834106eebe1a4257ef00d----- -ODA4NWJhMWQ3ZjdjM2RiOTM4YWQ3MDU5NWEyYmRhYjA5NjQ0ZjFlYzM4MDNiZTE3 -MWMzM2YxNGJjODBkNGUzYg== ------END PRIVATE KEY for 78689fd4b1e2e434d567fe01e61598a42717d83124308266bd09ccc15d2339dd318c019914b86ac29adbae5dd8a02d0307425e9bd85a296e94943708c72f8c670f0b7c50a890a5719088dbd9f1d062cad9acffa06df834106eebe1a4257ef00d----- diff --git a/neversea/wallets/validators/validatorKey02.pem b/neversea/wallets/validators/validatorKey02.pem deleted file mode 100644 index e9d9abf9..00000000 --- a/neversea/wallets/validators/validatorKey02.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for 7188b234a8bf834f2e6258012aa09a2ab93178ffab9c789480275f61fe02cd1b9a58ddc63b79a73abea9e2b7ac5cac0b0d4324eff50aca2f0ec946b9ae6797511fa3ce461b57e77129cba8ab3b51147695d4ce889cbe67905f6586b4e4f22491----- -ZjFkYjBkMjE4NmJiZDEzOGQ4MTk5MzM3MDJlMmMxMmFkNDk5MTI0YzQ3N2Q0OGM3 -ZDM3MTM2N2E3MWZmMmM1Zg== ------END PRIVATE KEY for 7188b234a8bf834f2e6258012aa09a2ab93178ffab9c789480275f61fe02cd1b9a58ddc63b79a73abea9e2b7ac5cac0b0d4324eff50aca2f0ec946b9ae6797511fa3ce461b57e77129cba8ab3b51147695d4ce889cbe67905f6586b4e4f22491----- diff --git a/neversea/wallets/validators/validatorKey03.pem b/neversea/wallets/validators/validatorKey03.pem deleted file mode 100644 index 81df1345..00000000 --- a/neversea/wallets/validators/validatorKey03.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for 892e3ce2d8525ffdf6b01138eb8f568f6f2219d79f52c9ae611248defb5590b9d83065fb93f5ababdfb97c09111c4c1301e3aaacfc3ab17d9fa7c94327f525d80761420e9e0a8c61b3507a081a083b246a2d46d9c5b8d4331e813d7f26575b8b----- -Nzk1Mzg1NDcyNWMyYjhjMjBjMzU2MTlmOWU2ZDgzNjBkNmRlMjMyYzJiNGYyNTJh -NGExZGYxNDQ0NjJiNTYwZg== ------END PRIVATE KEY for 892e3ce2d8525ffdf6b01138eb8f568f6f2219d79f52c9ae611248defb5590b9d83065fb93f5ababdfb97c09111c4c1301e3aaacfc3ab17d9fa7c94327f525d80761420e9e0a8c61b3507a081a083b246a2d46d9c5b8d4331e813d7f26575b8b----- diff --git a/neversea/wallets/validators/validatorKey04.pem b/neversea/wallets/validators/validatorKey04.pem deleted file mode 100644 index 15d14008..00000000 --- a/neversea/wallets/validators/validatorKey04.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for bb2cad346f3610eb5caef2d8951794dacb43dec9804dc8f50c9a90aef3514b2d1f6e81a68334017e8ff4f64d8f66bb123649a7e80fc90d0bd24a7276c5bc180ee8b8e1b51c02ae23088cee245518fb161ed2e9f6e454d63b5043309603126d15----- -ZTA2ODUyYjZlYzM5MWE3Y2MzM2VjZWY3ODFiZmViZTRhYzZlM2JhNmJiMjBjOGNh -ZGE2NTgzNmEyODlkY2UzZQ== ------END PRIVATE KEY for bb2cad346f3610eb5caef2d8951794dacb43dec9804dc8f50c9a90aef3514b2d1f6e81a68334017e8ff4f64d8f66bb123649a7e80fc90d0bd24a7276c5bc180ee8b8e1b51c02ae23088cee245518fb161ed2e9f6e454d63b5043309603126d15----- diff --git a/neversea/wallets/validators/validatorKey05.pem b/neversea/wallets/validators/validatorKey05.pem deleted file mode 100644 index 5b09bd5d..00000000 --- a/neversea/wallets/validators/validatorKey05.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for 2507e11760bd939bdcfd5aca7ca8f34d94832ad57d174009bef829ab991746436767b1a4bd7b606bb302bb19e8fa7a02ee450d8c9c49f22dae89995ec9dc34fdd7d821e2774ef89841ed8a396a60701c0218c34201c4ed6aed6d8351db727402----- -NTAxYTE4ZTM1Y2FmNWE5NTFiMjdkZjc0YmQ2OTAxMGFhYzUxNmIwNWMzMTgxZGE5 -MzgzYzg0MzhkYjc0ZTkyMA== ------END PRIVATE KEY for 2507e11760bd939bdcfd5aca7ca8f34d94832ad57d174009bef829ab991746436767b1a4bd7b606bb302bb19e8fa7a02ee450d8c9c49f22dae89995ec9dc34fdd7d821e2774ef89841ed8a396a60701c0218c34201c4ed6aed6d8351db727402----- diff --git a/neversea/wallets/validators/validatorKey06.pem b/neversea/wallets/validators/validatorKey06.pem deleted file mode 100644 index a8b4159e..00000000 --- a/neversea/wallets/validators/validatorKey06.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for 6fe70a42fa820908fa3e32dc1d4ccc8b4b6bdd9c5bff7e8e96fa1801fe094be708d1462f86a43c738d8b1caa6cf81110b5db6a129c28a62df78934977327ecd79c5ec83e19c32e829295cc85ea3db7001adb9c65f7e802ca9031b7270b98aa8b----- -YWQ3YzZlZTI0N2U3NGY5ZjhhYWI5OWU1ZGJkMDVmOGFiZDZjNzE1ZjEwMTJiNzIz -MWQyNWUyNGRmMmFmOTM1Ng== ------END PRIVATE KEY for 6fe70a42fa820908fa3e32dc1d4ccc8b4b6bdd9c5bff7e8e96fa1801fe094be708d1462f86a43c738d8b1caa6cf81110b5db6a129c28a62df78934977327ecd79c5ec83e19c32e829295cc85ea3db7001adb9c65f7e802ca9031b7270b98aa8b----- diff --git a/neversea/wallets/validators/validatorKey07.pem b/neversea/wallets/validators/validatorKey07.pem deleted file mode 100644 index 032df40d..00000000 --- a/neversea/wallets/validators/validatorKey07.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for 4f134a3d2ab215321a21d0e53cf48b7be58015ace0ba0ae99cfdd333fc7fc2e38124b6cf6a2dec5eeee4058aebb3c009d60088de5efd63a4bddcdf1b6e07d189aec1007da95870af0895df8666a754493a8a9f81b406ba84695d838c87dae201----- -ZTIwNWM5ZTBlYzE5OTI0Zjk4YzJlODI4MDk0NzNmMTAxMjYwMWRkZWZiOTNlOGU1 -Njc4MDBhZmJkOGEyYTQ0Mw== ------END PRIVATE KEY for 4f134a3d2ab215321a21d0e53cf48b7be58015ace0ba0ae99cfdd333fc7fc2e38124b6cf6a2dec5eeee4058aebb3c009d60088de5efd63a4bddcdf1b6e07d189aec1007da95870af0895df8666a754493a8a9f81b406ba84695d838c87dae201----- diff --git a/neversea/wallets/validators/validatorKey08.pem b/neversea/wallets/validators/validatorKey08.pem deleted file mode 100644 index 38c2e2c3..00000000 --- a/neversea/wallets/validators/validatorKey08.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for be7489a2ab7eb220719e932387438ca3f25e7e480bb1cfd58c0cbdcfeadd64f01f353df0a7bbe67ddf3723f2fef1780b98ca639b726ece93bfedbb33b10db202062651ce1adc1d404b8978ddf72f2734779e4a8a43edec6f112b5b6748ee7704----- -MjBjMjNlNmFlNzlkMGJmMDAyNzRiMWFiYjgwODRlMjc4YTRhMDJkMjU0ODk2MWY0 -YmRjYzNmMmUxZmI4ZWYyMg== ------END PRIVATE KEY for be7489a2ab7eb220719e932387438ca3f25e7e480bb1cfd58c0cbdcfeadd64f01f353df0a7bbe67ddf3723f2fef1780b98ca639b726ece93bfedbb33b10db202062651ce1adc1d404b8978ddf72f2734779e4a8a43edec6f112b5b6748ee7704----- diff --git a/neversea/wallets/validators/validatorKey09.pem b/neversea/wallets/validators/validatorKey09.pem deleted file mode 100644 index 63cbe8c0..00000000 --- a/neversea/wallets/validators/validatorKey09.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for 058a40df443d83ed68f3c58a27e0d6e50e6a8fc57a318c587fefe44c13fe6ed916c89df8a52d845be337e1ec4ba01b0606418dd00c2a65021ba796671164c03f9fe63e08fb2b5c6571fe8bdebfee87f0110ded1a75bfed45921291a20c3db799----- -ODg0ZjlmNWJhNWU2OTYyNTVmM2QwZmI2NDdiNDZlNjhlM2IwZTU3N2Y0YmRkYzU0 -YTViMTkzNWNmN2U5YWUzNw== ------END PRIVATE KEY for 058a40df443d83ed68f3c58a27e0d6e50e6a8fc57a318c587fefe44c13fe6ed916c89df8a52d845be337e1ec4ba01b0606418dd00c2a65021ba796671164c03f9fe63e08fb2b5c6571fe8bdebfee87f0110ded1a75bfed45921291a20c3db799----- diff --git a/neversea/wallets/validators/validatorKey10.pem b/neversea/wallets/validators/validatorKey10.pem deleted file mode 100644 index 1e55f863..00000000 --- a/neversea/wallets/validators/validatorKey10.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for d11ccef07488d8babbdcffdafc8853d64838e31468866cdea2fae9507bdf6bb011454b1ff4872b402c3fefdaf2a1e0133b59abbf3f34f0e94cf7af5a2c80900280f4070e2779b6999ae6019da1e24c955693c508efe6b39d89db4b2585c8c987----- -MjdiYWZjZTlhZjg5ODlmOWUyYzczMjA1MWM4N2U3YzZiY2M3NWZhYjlmZmNkYWI4 -OWJmNTRkNDRjZGNkMGIzYw== ------END PRIVATE KEY for d11ccef07488d8babbdcffdafc8853d64838e31468866cdea2fae9507bdf6bb011454b1ff4872b402c3fefdaf2a1e0133b59abbf3f34f0e94cf7af5a2c80900280f4070e2779b6999ae6019da1e24c955693c508efe6b39d89db4b2585c8c987----- diff --git a/neversea/wallets/validators/validatorKey11.pem b/neversea/wallets/validators/validatorKey11.pem deleted file mode 100644 index 1eea5074..00000000 --- a/neversea/wallets/validators/validatorKey11.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY for d76b18da74232f15fa89f033ef1075af91ef5b5a6cb531300684cebb87e26493c53951f3af2d416544291472241221015af58f2d4b4567e0af2d93a5b294d432b786c7c0c5186e8581dfd65da3bc64ea1cede8d5c9f32ce7b27f6f31fc48d908----- -MDRkMWU5OGFjMjJlYmEyMDIyYzJlZjAyYWMzZTBmOGI2MWQ0Yzg1N2E5N2ZjOWE0 -YTI0Y2ZmM2NhNGUzNDAxZA== ------END PRIVATE KEY for d76b18da74232f15fa89f033ef1075af91ef5b5a6cb531300684cebb87e26493c53951f3af2d416544291472241221015af58f2d4b4567e0af2d93a5b294d432b786c7c0c5186e8581dfd65da3bc64ea1cede8d5c9f32ce7b27f6f31fc48d908----- diff --git a/neversea/wallets/validators/wallet00.json b/neversea/wallets/validators/wallet00.json deleted file mode 100644 index e9f51cce..00000000 --- a/neversea/wallets/validators/wallet00.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "86d56d54-3d31-437b-8231-db47920facb5", - "address": "bf719969e813719bd5f1a1e7eed5ba28439bc0c9a192f572bcf9bf33e4f7dfd8", - "bech32": "erd1hacej60gzdceh40358n7a4d69ppehsxf5xf02u4ulxln8e8hmlvqfta3ha", - "crypto": { - "ciphertext": "44f3ac74889bba883ab69e8ae35b10a28702804ffd311ca6344d0d4cb48ccc00d47b923fc35b48f978239d1ae538ab1bf865bb122b44352b94823d667cb8b058", - "cipherparams": { - "iv": "4905396375ee3cda1c9882257e36e603" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "87d24e7a2fefb4b68a3519117b676d53cbf6741300c501d3883484f910cac967", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "598091e456f60f4d0163036c7c474c27d45ac7bcb6d3dae2863172a4a1077a1b" - } -} diff --git a/neversea/wallets/validators/wallet00.pem b/neversea/wallets/validators/wallet00.pem deleted file mode 100644 index 905f5103..00000000 --- a/neversea/wallets/validators/wallet00.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd1hacej60gzdceh40358n7a4d69ppehsxf5xf02u4ulxln8e8hmlvqfta3ha----- -ZjAwOWY1NzcyOTM1ZWM4M2NlMGE1MWQzMzJlYzk0ZjhhYTg2MWY5YzVjN2Y2ZmFi -ZGViMDcyOTlmOTlhZTM4NmJmNzE5OTY5ZTgxMzcxOWJkNWYxYTFlN2VlZDViYTI4 -NDM5YmMwYzlhMTkyZjU3MmJjZjliZjMzZTRmN2RmZDg= ------END PRIVATE KEY for erd1hacej60gzdceh40358n7a4d69ppehsxf5xf02u4ulxln8e8hmlvqfta3ha----- \ No newline at end of file diff --git a/neversea/wallets/validators/wallet01.json b/neversea/wallets/validators/wallet01.json deleted file mode 100644 index a56d3939..00000000 --- a/neversea/wallets/validators/wallet01.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "6be51fa9-54d3-4f80-92a5-8e0383b2eb78", - "address": "fa19874971e3441e0bb2cb445df7e46f7a275dfb47ba16b631957ba02ee4489e", - "bech32": "erd1lgvcwjt3udzpuzajedz9malydaazwh0mg7apdd33j4a6qthyfz0q6nl2qv", - "crypto": { - "ciphertext": "74fdad76afac179d447a50d898825caa1344964842f8c8c6cab4cf7f47b4adac79bb909de5bf6bca7289da3874a3a41691240754695c3f25ca25d068712d0cc0", - "cipherparams": { - "iv": "845f327010f8db2638e4c7dee27674fd" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "e21ce22d4055711c5ac1bc9b0f38630787d1ae5223fbf4b9d64e4bd9204f61aa", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "8266cf7ae29827c881d80a9dcaef3a8defe675aa4b61e3a4b65b0c0daba162a1" - } -} diff --git a/neversea/wallets/validators/wallet01.pem b/neversea/wallets/validators/wallet01.pem deleted file mode 100644 index 50faa816..00000000 --- a/neversea/wallets/validators/wallet01.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd1lgvcwjt3udzpuzajedz9malydaazwh0mg7apdd33j4a6qthyfz0q6nl2qv----- -MjUyNWQ2ZDg4NGNlZjBmNWUwOTQ5MWI3MDRjMzhkNDVkOGZjMDllNGI3YzU4ODRh -MTI1NTQ5ODI3Njk4MGQ3NGZhMTk4NzQ5NzFlMzQ0MWUwYmIyY2I0NDVkZjdlNDZm -N2EyNzVkZmI0N2JhMTZiNjMxOTU3YmEwMmVlNDQ4OWU= ------END PRIVATE KEY for erd1lgvcwjt3udzpuzajedz9malydaazwh0mg7apdd33j4a6qthyfz0q6nl2qv----- \ No newline at end of file diff --git a/neversea/wallets/validators/wallet02.json b/neversea/wallets/validators/wallet02.json deleted file mode 100644 index b11faad9..00000000 --- a/neversea/wallets/validators/wallet02.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "669ce5e9-c12c-4d03-b439-d83abfbfa242", - "address": "4b4df3a2d2c75dbc786e8c3c9835ad32c1d24be7a7135627b14ac037cf688f58", - "bech32": "erd1fdxl8gkjcawmc7rw3s7fsdddxtqayjl85uf4vfa3ftqr0nmg3avqscfd7z", - "crypto": { - "ciphertext": "cc6ec722fe0a7efb45fd79636c7bfa7de41765d8aa39bef929461c73a6d31fed0049c798f5ff4e9459f6cea373149c700c4ad8d1e860d4e3df10c4d085c2af1a", - "cipherparams": { - "iv": "830999315a3b5998f709431d3c6a99e7" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "6a30a9d5535eb3d29ba5aede1d92cc0b23cf06fef5f1e4b7fa3177af675343d4", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "9056441a778fe0df2ad54b9dab2d7fdd53a00dbfac04698ef90833db4d0a5362" - } -} diff --git a/neversea/wallets/validators/wallet02.pem b/neversea/wallets/validators/wallet02.pem deleted file mode 100644 index 358dd652..00000000 --- a/neversea/wallets/validators/wallet02.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd1fdxl8gkjcawmc7rw3s7fsdddxtqayjl85uf4vfa3ftqr0nmg3avqscfd7z----- -ZTExYjE1NDNlYTZkNzEyMWQyOTU5ZWRiMDIxZjEwOTA4MjQ0ODZlMDliOGMyNTAy -OTQzMjAxZTIwNjZlNzcwNjRiNGRmM2EyZDJjNzVkYmM3ODZlOGMzYzk4MzVhZDMy -YzFkMjRiZTdhNzEzNTYyN2IxNGFjMDM3Y2Y2ODhmNTg= ------END PRIVATE KEY for erd1fdxl8gkjcawmc7rw3s7fsdddxtqayjl85uf4vfa3ftqr0nmg3avqscfd7z----- \ No newline at end of file diff --git a/neversea/wallets/validators/wallet03.json b/neversea/wallets/validators/wallet03.json deleted file mode 100644 index 1db2fa1d..00000000 --- a/neversea/wallets/validators/wallet03.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "35afdb6f-0b15-4984-bb60-a1a6236e70d1", - "address": "57c19e874a49c124bacb0196ca759c8735e29933de21f7ab8f77cd4cef530ac0", - "bech32": "erd12lqeap62f8qjfwktqxtv5avusu679xfnmcsl02u0wlx5em6nptqqfe8w49", - "crypto": { - "ciphertext": "6030d16fc6584f87aa2d3982e41c6a62f38d2ebc2c696164a924e8ed315dd946d38df858784b17e545e2cec3a089593c72bb7262032fecc8915905778cb0c010", - "cipherparams": { - "iv": "a6c7c136dd25fbe8cddcdd775b140a2e" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "e44e718931284ab12823468a51253c6db6bf2c492e1cb737b5d34fe67bad2f79", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "7d646fddcebd15e30490eda8ce19cfe5420c613f1389212ec3240e8ce3a38696" - } -} diff --git a/neversea/wallets/validators/wallet03.pem b/neversea/wallets/validators/wallet03.pem deleted file mode 100644 index e6ba3cab..00000000 --- a/neversea/wallets/validators/wallet03.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd12lqeap62f8qjfwktqxtv5avusu679xfnmcsl02u0wlx5em6nptqqfe8w49----- -NGE1YmY5YThkNTAwODlkMTY4ZmJjZGIzNTIxMmZkNmIwNmY0NmE1NTgwNTQ2MTE5 -MjYwODE2NTQxZDg2MjJhMzU3YzE5ZTg3NGE0OWMxMjRiYWNiMDE5NmNhNzU5Yzg3 -MzVlMjk5MzNkZTIxZjdhYjhmNzdjZDRjZWY1MzBhYzA= ------END PRIVATE KEY for erd12lqeap62f8qjfwktqxtv5avusu679xfnmcsl02u0wlx5em6nptqqfe8w49----- \ No newline at end of file diff --git a/neversea/wallets/validators/wallet04.json b/neversea/wallets/validators/wallet04.json deleted file mode 100644 index ccc4fdfd..00000000 --- a/neversea/wallets/validators/wallet04.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "b1c4296f-a33d-4792-96cd-075f92ed2cb8", - "address": "0746514aea9fe70676a30fe7e44cda921b5c4319d78bde2aef307550ffac8ed7", - "bech32": "erd1qar9zjh2nlnsva4rpln7gnx6jgd4csce679au2h0xp64plav3mtslq9lrf", - "crypto": { - "ciphertext": "b60eb9bb23fc7c18c74e28cd4b6b365cc3892226d2c8e94ecb3c7c1dd357943c924decf8974de0c8e9b49793bfe574d1d4c59d78e3764c10f897866f49b25df8", - "cipherparams": { - "iv": "0f2d2a811ba731d43516aa494b20b3d5" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "9bca39213bf4fb20ddaf10c1f16c34febd89f5b7cf4681c8642136ba38c2c9f3", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "d94d8642d4464ce513a02c12bbccb8df7b9b6926af04e72c461d515ad1566862" - } -} diff --git a/neversea/wallets/validators/wallet04.pem b/neversea/wallets/validators/wallet04.pem deleted file mode 100644 index 8eae2299..00000000 --- a/neversea/wallets/validators/wallet04.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd1qar9zjh2nlnsva4rpln7gnx6jgd4csce679au2h0xp64plav3mtslq9lrf----- -ZGM5ZjY2MDZkY2E3YzA1MDZkZmU1YTZlYmI4NjI2MzA1MTU5ZGNlODJhNTE4ZDRi -MWNmMzQ3NzMyNTZhYTc3YzA3NDY1MTRhZWE5ZmU3MDY3NmEzMGZlN2U0NGNkYTky -MWI1YzQzMTlkNzhiZGUyYWVmMzA3NTUwZmZhYzhlZDc= ------END PRIVATE KEY for erd1qar9zjh2nlnsva4rpln7gnx6jgd4csce679au2h0xp64plav3mtslq9lrf----- \ No newline at end of file diff --git a/neversea/wallets/validators/wallet05.json b/neversea/wallets/validators/wallet05.json deleted file mode 100644 index aa2c7246..00000000 --- a/neversea/wallets/validators/wallet05.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "e8912473-60cd-4c7c-9db5-e344aaa6f9b2", - "address": "43a648911410b57199e0468ebbb4e73310a48c1088f498df42688074372eba7c", - "bech32": "erd1gwny3yg5zz6hrx0qg68thd88xvg2frqs3r6f3h6zdzq8gdewhf7qkwxtad", - "crypto": { - "ciphertext": "a4e191dfafcd5fa76f877bfbcb6ad751d545012cd2467286c16f5d71498db8e38492d5b37f13fc2f90783fb2e0c7aa6be51598eb1beded664dad1e9be5004258", - "cipherparams": { - "iv": "f7f55581074fb4d2c50da4d572d7ae4c" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "ac8ff349032ed61b3fcbf13c0c6404a07861bfb49b9ea3a43843df27b33d33fe", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "bdadc0ecf65ebab95e78e926425b01d413dd17808f967d0c798eccabfe38233f" - } -} diff --git a/neversea/wallets/validators/wallet05.pem b/neversea/wallets/validators/wallet05.pem deleted file mode 100644 index 3802a1ab..00000000 --- a/neversea/wallets/validators/wallet05.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd1gwny3yg5zz6hrx0qg68thd88xvg2frqs3r6f3h6zdzq8gdewhf7qkwxtad----- -Yzc3YTYyOGNjM2ZmMWQ3MmNmMTk0YTdiYmI0NWU0ZDQ2MGFhYzE2ZDYzY2Y4OTA0 -NGQ0ZDY3Y2M5NjJkM2I5MzQzYTY0ODkxMTQxMGI1NzE5OWUwNDY4ZWJiYjRlNzMz -MTBhNDhjMTA4OGY0OThkZjQyNjg4MDc0MzcyZWJhN2M= ------END PRIVATE KEY for erd1gwny3yg5zz6hrx0qg68thd88xvg2frqs3r6f3h6zdzq8gdewhf7qkwxtad----- \ No newline at end of file diff --git a/neversea/wallets/validators/wallet06.json b/neversea/wallets/validators/wallet06.json deleted file mode 100644 index 1deb9fd6..00000000 --- a/neversea/wallets/validators/wallet06.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "91c8dd1d-30df-4f58-a014-e6611099a340", - "address": "5d137e290ee485cc40f25c68ccf4da5cf1b5d63ac407a73bf49aacc4153dedf7", - "bech32": "erd1t5fhu2gwujzucs8jt35veax6tncmt436csr6wwl5n2kvg9faahmsztzhpd", - "crypto": { - "ciphertext": "c637d76620f6786632b149665949b597fc0ef727bd065f6fc9d9022d01c35dd28063fc768af38b8363c6dc5ce36f71cf05d2e845b212abca26d539c0445c2036", - "cipherparams": { - "iv": "034c906f00ef6ff38fd1ac9511cf3432" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "7578836d2fb289d403a008eff889eb767fe1688b4832ec135c7f90be66d3be61", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "8f2927d35c25c56cccca8084aa83ec199e62b418858f8c7b7bd6d79960afdbdd" - } -} diff --git a/neversea/wallets/validators/wallet06.pem b/neversea/wallets/validators/wallet06.pem deleted file mode 100644 index b2081b87..00000000 --- a/neversea/wallets/validators/wallet06.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd1t5fhu2gwujzucs8jt35veax6tncmt436csr6wwl5n2kvg9faahmsztzhpd----- -ODgyNTE2MmUzM2VjZjc4MDFiYzY0ZGM5NjhkMmVjZWUzMDdlYTMwNWI0YjNmN2Y2 -OWJjZWQ2NmIzZDFjNTcwMTVkMTM3ZTI5MGVlNDg1Y2M0MGYyNWM2OGNjZjRkYTVj -ZjFiNWQ2M2FjNDA3YTczYmY0OWFhY2M0MTUzZGVkZjc= ------END PRIVATE KEY for erd1t5fhu2gwujzucs8jt35veax6tncmt436csr6wwl5n2kvg9faahmsztzhpd----- \ No newline at end of file diff --git a/neversea/wallets/validators/wallet07.json b/neversea/wallets/validators/wallet07.json deleted file mode 100644 index 71c4d16f..00000000 --- a/neversea/wallets/validators/wallet07.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "e69c399f-60dd-4b12-b535-62f956e2ff1a", - "address": "59ecfeb97546aa0a4f79d0e2d21f03f13a3df942f0406d087d20e5143d74cbb3", - "bech32": "erd1t8k0awt4g64q5nme6r3dy8cr7yarm72z7pqx6zrayrj3g0t5eweshjpp9j", - "crypto": { - "ciphertext": "e5f0d12c77d0fab8d6a505577efcee750c0dce775652d5d2db5b2b9e0091b5ae1a7804759140dc248198a5d1b62362f1cb3dcc96f2f5af6daf60ffc54ae16cb4", - "cipherparams": { - "iv": "0e115f24a73527db72f86da64e7d8fa6" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "a8662ed6965f92d851734ceb38d4e378ce5974fcc96afb00a17721d38f36c76e", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "eca10417fd1c224e55a3263e622d12c2429d79598785c236801b99c4498136f7" - } -} diff --git a/neversea/wallets/validators/wallet07.pem b/neversea/wallets/validators/wallet07.pem deleted file mode 100644 index c3a6bfea..00000000 --- a/neversea/wallets/validators/wallet07.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd1t8k0awt4g64q5nme6r3dy8cr7yarm72z7pqx6zrayrj3g0t5eweshjpp9j----- -MjNkNjY3Mzk0M2ZiMTcxNDhmMzFiZDE0YTdjYjVmYjBkNzA0MDcwMjgxOWU0NDBi -MmI4ODU0NmZhMDAxYWFiOTU5ZWNmZWI5NzU0NmFhMGE0Zjc5ZDBlMmQyMWYwM2Yx -M2EzZGY5NDJmMDQwNmQwODdkMjBlNTE0M2Q3NGNiYjM= ------END PRIVATE KEY for erd1t8k0awt4g64q5nme6r3dy8cr7yarm72z7pqx6zrayrj3g0t5eweshjpp9j----- \ No newline at end of file diff --git a/neversea/wallets/validators/wallet08.json b/neversea/wallets/validators/wallet08.json deleted file mode 100644 index 77b940ca..00000000 --- a/neversea/wallets/validators/wallet08.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "37980112-cef9-49c3-aa44-6119184bdefc", - "address": "69f538c27550f849bc71557a419e29766fa2eeeaf2c545aea3a61a496fa7732a", - "bech32": "erd1d86n3sn42ruyn0r324ayr83fweh69mh27tz5tt4r5cdyjma8wv4q0ncfel", - "crypto": { - "ciphertext": "bff24c6dfd545d4784a8a19728fb9f70028a135d3c59da010dd494d2cebd8ebfd5917b96f610bb7341b1554ffcb29f60b60cb60b613d17c30613878b5378601d", - "cipherparams": { - "iv": "1eb9687d6d2d99c9195774468972726c" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "f213d7283edd246a00f51fb40936ed5a3300a1d1d841b02fcbf744407ded9f56", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "4648878ea05a09827ca56978d65989411ffb42dd09e60727bcce0fc43f446b79" - } -} diff --git a/neversea/wallets/validators/wallet08.pem b/neversea/wallets/validators/wallet08.pem deleted file mode 100644 index 88f80f33..00000000 --- a/neversea/wallets/validators/wallet08.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd1d86n3sn42ruyn0r324ayr83fweh69mh27tz5tt4r5cdyjma8wv4q0ncfel----- -ZWM0NzUxZjMyNTY4M2M4M2UwN2UyMGJmMjVkYjk3OTc0Y2QwN2UzMWM2OTA5ZTFl -MWRmZDA1NGI0ODU5NzM2MDY5ZjUzOGMyNzU1MGY4NDliYzcxNTU3YTQxOWUyOTc2 -NmZhMmVlZWFmMmM1NDVhZWEzYTYxYTQ5NmZhNzczMmE= ------END PRIVATE KEY for erd1d86n3sn42ruyn0r324ayr83fweh69mh27tz5tt4r5cdyjma8wv4q0ncfel----- \ No newline at end of file diff --git a/neversea/wallets/validators/wallet09.json b/neversea/wallets/validators/wallet09.json deleted file mode 100644 index a23baca7..00000000 --- a/neversea/wallets/validators/wallet09.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "e14ca106-f544-4f5f-a478-4caf0b0db48d", - "address": "53173ccd3b89eb26ef32cae05b01b0da7a87318db842e0630ff0493fc476181c", - "bech32": "erd12vtnenfm384jdmejets9kqdsmfagwvvdhppwqcc07pynl3rkrqwqx6qtk9", - "crypto": { - "ciphertext": "9e462bc379520be3b1909be763f62b559a1d70e113b1624b1e24cfb8a8aa45b8ee73bc9f1626501181aacf74be7c9129040b3446d6aa518aa64a8095459d563d", - "cipherparams": { - "iv": "e0698b9ba096ab3a4b5bcee54d521536" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "f0ba048a41378e910f55bafd1f97187d370b591564d945bb8a2094b4868f5c4f", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "5980db19ec8c2fc641e320121c111eae77b19b6c1466176f0bb999c5317a5a45" - } -} diff --git a/neversea/wallets/validators/wallet09.pem b/neversea/wallets/validators/wallet09.pem deleted file mode 100644 index bf3e29b4..00000000 --- a/neversea/wallets/validators/wallet09.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd12vtnenfm384jdmejets9kqdsmfagwvvdhppwqcc07pynl3rkrqwqx6qtk9----- -YWU1NjQyY2YxZjYzODQ2OGJjMTYzYTQzYThhMGZmNzFlMGNlNjQ4Yjk3ODZkZDNl -YjE1MWIwMjM2ZGM1NWU1YTUzMTczY2NkM2I4OWViMjZlZjMyY2FlMDViMDFiMGRh -N2E4NzMxOGRiODQyZTA2MzBmZjA0OTNmYzQ3NjE4MWM= ------END PRIVATE KEY for erd12vtnenfm384jdmejets9kqdsmfagwvvdhppwqcc07pynl3rkrqwqx6qtk9----- \ No newline at end of file diff --git a/neversea/wallets/validators/wallet10.json b/neversea/wallets/validators/wallet10.json deleted file mode 100644 index 9a1da5f9..00000000 --- a/neversea/wallets/validators/wallet10.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "8da2d6ac-f086-4469-bae8-27a273ca7714", - "address": "2a1efd01153794f29f0925b005817814842ffff8d362ebaf43d7ac4f4f5c173b", - "bech32": "erd19g006qg4x72098cfykcqtqtczjzzlllc6d3wht6r67ky7n6uzuaszknrms", - "crypto": { - "ciphertext": "66ecbc671bb819f0625a214eb5a7432c2aef517c49201241f6fcb2cac17715f1db95dc0f501fb6412b0f2acf45ca7ba48737d89bcafa369ca09e0726da5964e3", - "cipherparams": { - "iv": "e29cdbf551d418eca15fd555ffc0f6e2" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "acf49765bb1d2045926b8de8cc5b55f5499afc9477ede97d5c261fe4f6e5462e", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "af4dcd9edcfd66e259dccc850d670f8f3b2333bb0dae27df6b45103ee83b97f7" - } -} diff --git a/neversea/wallets/validators/wallet10.pem b/neversea/wallets/validators/wallet10.pem deleted file mode 100644 index 78145fb9..00000000 --- a/neversea/wallets/validators/wallet10.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd19g006qg4x72098cfykcqtqtczjzzlllc6d3wht6r67ky7n6uzuaszknrms----- -NWE1ZTA5YjAwNjg0NGZjMTUwYTE2NzQ4NWM1YWRlZjZkYWFkZjUzMGYxYjYxZjBh -MzI5NmU0MjllMGIzYWRjZDJhMWVmZDAxMTUzNzk0ZjI5ZjA5MjViMDA1ODE3ODE0 -ODQyZmZmZjhkMzYyZWJhZjQzZDdhYzRmNGY1YzE3M2I= ------END PRIVATE KEY for erd19g006qg4x72098cfykcqtqtczjzzlllc6d3wht6r67ky7n6uzuaszknrms----- \ No newline at end of file diff --git a/neversea/wallets/validators/wallet11.json b/neversea/wallets/validators/wallet11.json deleted file mode 100644 index 764e1e0d..00000000 --- a/neversea/wallets/validators/wallet11.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 4, - "id": "d885a4b2-849e-4930-a1b8-011fb8a165a7", - "address": "20b60c66ded46407cb16957348c9cffd72d9996c5ade0310e7c11d45733b4812", - "bech32": "erd1yzmqcek763jq0jckj4e53jw0l4ednxtvtt0qxy88cyw52uemfqfqm8zcff", - "crypto": { - "ciphertext": "120f605398aa0816b7d90ef6296b57ccf29c9f1f7e4bc4208fbe0e2a7ef98f7dde86767c7f908239c9de1d91444504ba5996de19c5713592aa1e0d65cdd45a90", - "cipherparams": { - "iv": "33ed8a348b3a0ab14ea60e7f9f393f01" - }, - "cipher": "aes-128-ctr", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "salt": "b59899324f26f4968233ca672665170e0caf046bd3a4f094570d0a5f2df69bde", - "n": 4096, - "r": 8, - "p": 1 - }, - "mac": "68fb717bcabebb5346d51375086c60946ac43ba7dd8d4a1dd895300c09f102a8" - } -} diff --git a/neversea/wallets/validators/wallet11.pem b/neversea/wallets/validators/wallet11.pem deleted file mode 100644 index f0842046..00000000 --- a/neversea/wallets/validators/wallet11.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY for erd1yzmqcek763jq0jckj4e53jw0l4ednxtvtt0qxy88cyw52uemfqfqm8zcff----- -M2IzMjdmMmUyZjUzZGZhMDM3ZWMwNTEyYjFhNjA3OTU3MzBmY2MyMjc2ODI0Yjgw -ZjZjM2I3ZGRkNzM2YjA4ZTIwYjYwYzY2ZGVkNDY0MDdjYjE2OTU3MzQ4YzljZmZk -NzJkOTk5NmM1YWRlMDMxMGU3YzExZDQ1NzMzYjQ4MTI= ------END PRIVATE KEY for erd1yzmqcek763jq0jckj4e53jw0l4ednxtvtt0qxy88cyw52uemfqfqm8zcff----- \ No newline at end of file diff --git a/neversea/wasm/Cargo.toml b/neversea/wasm/Cargo.toml deleted file mode 100644 index 615f8aa7..00000000 --- a/neversea/wasm/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "adder-wasm" -version = "0.0.0" -authors = [ "you",] -edition = "2018" -publish = false - -[lib] -crate-type = [ "cdylib",] - -[workspace] -members = [ ".",] - -[dev-dependencies] - -[profile.release] -codegen-units = 1 -opt-level = "z" -lto = true -debug = false -panic = "abort" - -[dependencies.adder] -path = ".." - -[dependencies.elrond-wasm-node] -version = "0.30.0" - -[dependencies.elrond-wasm-output] -version = "0.30.0" -features = [ "wasm-output-mode",] diff --git a/neversea/wasm/src/lib.rs b/neversea/wasm/src/lib.rs deleted file mode 100644 index 2042066e..00000000 --- a/neversea/wasm/src/lib.rs +++ /dev/null @@ -1,15 +0,0 @@ -//////////////////////////////////////////////////// -////////////////// AUTO-GENERATED ////////////////// -//////////////////////////////////////////////////// - -#![no_std] - -elrond_wasm_node::wasm_endpoints! { - adder - ( - add - getSum - ) -} - -elrond_wasm_node::wasm_empty_callback! {}