From 39cc83134661debec35f80b92c0060d143e4737f Mon Sep 17 00:00:00 2001 From: Omar Date: Sun, 3 Mar 2024 00:05:52 +0300 Subject: [PATCH 01/17] [Publishing Tool]: Introduce the executors --- Cargo.lock | 771 +++++++++++++++++- Cargo.toml | 47 +- tests/example | 1 - tools/publishing-tool/Cargo.toml | 32 + tools/publishing-tool/src/database_overlay.rs | 656 +++++++++++++++ .../src/executor/gateway_executor.rs | 295 +++++++ .../executor/mainnet_simulator_executor.rs | 99 +++ tools/publishing-tool/src/executor/mod.rs | 7 + tools/publishing-tool/src/executor/traits.rs | 41 + tools/publishing-tool/src/main.rs | 6 + 10 files changed, 1910 insertions(+), 45 deletions(-) delete mode 100644 tests/example create mode 100644 tools/publishing-tool/Cargo.toml create mode 100644 tools/publishing-tool/src/database_overlay.rs create mode 100644 tools/publishing-tool/src/executor/gateway_executor.rs create mode 100644 tools/publishing-tool/src/executor/mainnet_simulator_executor.rs create mode 100644 tools/publishing-tool/src/executor/mod.rs create mode 100644 tools/publishing-tool/src/executor/traits.rs create mode 100644 tools/publishing-tool/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index e0461d07..b62361ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + [[package]] name = "aliasable" version = "0.1.3" @@ -92,6 +101,17 @@ version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" +[[package]] +name = "async-trait" +version = "0.1.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -125,6 +145,27 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.52", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -137,6 +178,15 @@ version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +[[package]] +name = "bitmaps" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" +dependencies = [ + "typenum", +] + [[package]] name = "blake2" version = "0.10.6" @@ -231,6 +281,17 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "camino" version = "1.1.6" @@ -295,6 +356,21 @@ dependencies = [ "libc", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -314,6 +390,17 @@ dependencies = [ "windows-targets 0.52.4", ] +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob 0.3.1", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.5.1" @@ -389,6 +476,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "common" version = "0.1.0" @@ -567,6 +664,19 @@ dependencies = [ "syn 2.0.52", ] +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.3", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "defiplaza-v2-adapter-v1" version = "0.1.0" @@ -660,6 +770,18 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enum_dispatch" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.52", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -748,6 +870,21 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.30" @@ -755,6 +892,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -763,12 +901,34 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + [[package]] name = "futures-io" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + [[package]] name = "futures-sink" version = "0.3.30" @@ -787,8 +947,11 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ + "futures-channel", "futures-core", "futures-io", + "futures-macro", + "futures-sink", "futures-task", "memchr", "pin-project-lite", @@ -1045,6 +1208,20 @@ dependencies = [ "scrypto", ] +[[package]] +name = "im" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" +dependencies = [ + "bitmaps", + "rand_core 0.6.4", + "rand_xoshiro", + "sized-chunks", + "typenum", + "version_check", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -1082,6 +1259,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" +[[package]] +name = "integer-encoding" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" + [[package]] name = "ipnet" version = "2.9.0" @@ -1097,12 +1280,41 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "js-sys" version = "0.3.68" @@ -1127,6 +1339,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "leb128" version = "0.2.5" @@ -1139,12 +1357,49 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libloading" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2caa5afb8bf9f3a2652760ce7d4f62d21c4d5a423e68466fca30df82f2330164" +dependencies = [ + "cfg-if", + "windows-targets 0.52.4", +] + [[package]] name = "libm" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "librocksdb-sys" +version = "0.11.0+8.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3386f101bcb4bd252d8e9d2fb41ec3b0862a15a62b478c355b2982efa469e3e" +dependencies = [ + "bindgen", + "bzip2-sys", + "cc", + "glob 0.3.1", + "libc", + "libz-sys", + "lz4-sys", + "zstd-sys", +] + +[[package]] +name = "libz-sys" +version = "1.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037731f5d3aaa87a5675e895b63ddff1a87624bc29f77004ea829809654e48f6" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "link-cplusplus" version = "1.0.9" @@ -1176,6 +1431,22 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "lru" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909" + +[[package]] +name = "lz4-sys" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "memchr" version = "2.7.1" @@ -1198,6 +1469,12 @@ dependencies = [ "unicase", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.2" @@ -1209,9 +1486,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -1243,7 +1520,7 @@ dependencies = [ [[package]] name = "native-sdk" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "radix-engine-common", "radix-engine-derive", @@ -1270,6 +1547,54 @@ dependencies = [ "tempfile", ] +[[package]] +name = "node-common" +version = "0.1.0" +source = "git+https://github.com/radixdlt/babylon-node?rev=63a8267196995fef0830e4fbf0271bea65c90ab1#63a8267196995fef0830e4fbf0271bea65c90ab1" +dependencies = [ + "bech32", + "blake2", + "jni", + "opentelemetry", + "opentelemetry-jaeger", + "parking_lot", + "prometheus", + "radix-engine", + "radix-engine-common", + "radix-engine-interface", + "radix-engine-queries", + "radix-engine-store-interface", + "radix-engine-stores", + "sbor", + "tokio", + "tokio-util", + "tracing", + "tracing-opentelemetry", + "tracing-subscriber", + "transaction", + "utils", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-bigint" version = "0.4.4" @@ -1407,6 +1732,89 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "opentelemetry" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d6c3d7288a106c0a363e4b0e8d308058d56902adefb16f4936f417ffef086e" +dependencies = [ + "opentelemetry_api", + "opentelemetry_sdk", +] + +[[package]] +name = "opentelemetry-jaeger" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e785d273968748578931e4dc3b4f5ec86b26e09d9e0d66b55adda7fce742f7a" +dependencies = [ + "async-trait", + "futures", + "futures-executor", + "once_cell", + "opentelemetry", + "opentelemetry-semantic-conventions", + "thiserror", + "thrift", + "tokio", +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b02e0230abb0ab6636d18e2ba8fa02903ea63772281340ccac18e0af3ec9eeb" +dependencies = [ + "opentelemetry", +] + +[[package]] +name = "opentelemetry_api" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c24f96e21e7acc813c7a8394ee94978929db2bcc46cf6b5014fc612bf7760c22" +dependencies = [ + "fnv", + "futures-channel", + "futures-util", + "indexmap 1.9.3", + "js-sys", + "once_cell", + "pin-project-lite", + "thiserror", +] + +[[package]] +name = "opentelemetry_sdk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca41c4933371b61c2a2f214bf16931499af4ec90543604ec828f7a625c09113" +dependencies = [ + "async-trait", + "crossbeam-channel", + "dashmap", + "fnv", + "futures-channel", + "futures-executor", + "futures-util", + "once_cell", + "opentelemetry_api", + "percent-encoding", + "rand 0.8.5", + "thiserror", + "tokio", + "tokio-stream", +] + +[[package]] +name = "ordered-float" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7" +dependencies = [ + "num-traits", +] + [[package]] name = "ouroboros" version = "0.17.2" @@ -1431,6 +1839,12 @@ dependencies = [ "syn 2.0.52", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "package-loader" version = "0.1.0" @@ -1476,6 +1890,12 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1536,6 +1956,16 @@ dependencies = [ "yansi", ] +[[package]] +name = "prettyplease" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +dependencies = [ + "proc-macro2", + "syn 2.0.52", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1569,6 +1999,41 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prometheus" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "memchr", + "parking_lot", + "thiserror", +] + +[[package]] +name = "publishing-tool" +version = "0.1.0" +dependencies = [ + "caviarnine-v1-adapter-v1", + "common", + "gateway-client", + "hex", + "ignition", + "ociswap-v1-adapter-v1", + "package-loader", + "radix-engine", + "radix-engine-common", + "radix-engine-interface", + "radix-engine-store-interface", + "sbor", + "scrypto-unit", + "state-manager", + "transaction", +] + [[package]] name = "pulldown-cmark" version = "0.9.6" @@ -1592,7 +2057,7 @@ dependencies = [ [[package]] name = "radix-engine" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "bitflags 1.3.2", "colored", @@ -1622,7 +2087,7 @@ dependencies = [ [[package]] name = "radix-engine-common" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "bech32", "blake2", @@ -1647,7 +2112,7 @@ dependencies = [ [[package]] name = "radix-engine-derive" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "proc-macro2", "quote", @@ -1658,7 +2123,7 @@ dependencies = [ [[package]] name = "radix-engine-interface" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "bitflags 1.3.2", "const-sha1", @@ -1680,7 +2145,7 @@ dependencies = [ [[package]] name = "radix-engine-macros" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "paste", "proc-macro2", @@ -1692,7 +2157,7 @@ dependencies = [ [[package]] name = "radix-engine-profiling" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "fixedstr", ] @@ -1700,10 +2165,10 @@ dependencies = [ [[package]] name = "radix-engine-queries" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "hex", - "itertools", + "itertools 0.10.5", "paste", "radix-engine", "radix-engine-interface", @@ -1716,10 +2181,10 @@ dependencies = [ [[package]] name = "radix-engine-store-interface" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "hex", - "itertools", + "itertools 0.10.5", "radix-engine-common", "radix-engine-derive", "radix-engine-interface", @@ -1730,10 +2195,10 @@ dependencies = [ [[package]] name = "radix-engine-stores" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "hex", - "itertools", + "itertools 0.10.5", "radix-engine-common", "radix-engine-derive", "radix-engine-store-interface", @@ -1812,6 +2277,15 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -1827,6 +2301,8 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" dependencies = [ + "aho-corasick", + "memchr", "regex-automata", "regex-syntax", ] @@ -1837,6 +2313,8 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" dependencies = [ + "aho-corasick", + "memchr", "regex-syntax", ] @@ -1890,7 +2368,7 @@ dependencies = [ [[package]] name = "resources-tracker-macro" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "proc-macro2", "quote", @@ -1898,12 +2376,28 @@ dependencies = [ "syn 1.0.93", ] +[[package]] +name = "rocksdb" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6f170a4041d50a0ce04b0d2e14916d6ca863ea2e422689a5b694395d299ffe" +dependencies = [ + "libc", + "librocksdb-sys", +] + [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc_version" version = "0.4.0" @@ -1959,7 +2453,7 @@ dependencies = [ [[package]] name = "sbor" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "const-sha1", "hex", @@ -1973,7 +2467,7 @@ dependencies = [ [[package]] name = "sbor-derive" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "proc-macro2", "sbor-derive-common", @@ -1982,10 +2476,10 @@ dependencies = [ [[package]] name = "sbor-derive-common" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "const-sha1", - "itertools", + "itertools 0.10.5", "proc-macro2", "quote", "syn 1.0.93", @@ -2024,7 +2518,7 @@ checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" [[package]] name = "scrypto" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "bech32", "const-sha1", @@ -2046,7 +2540,7 @@ dependencies = [ [[package]] name = "scrypto-derive" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "proc-macro2", "quote", @@ -2083,7 +2577,7 @@ dependencies = [ [[package]] name = "scrypto-schema" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "bitflags 1.3.2", "radix-engine-common", @@ -2094,7 +2588,7 @@ dependencies = [ [[package]] name = "scrypto-test" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "native-sdk", "ouroboros", @@ -2112,7 +2606,7 @@ dependencies = [ [[package]] name = "scrypto-unit" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "radix-engine", "radix-engine-interface", @@ -2283,6 +2777,30 @@ dependencies = [ "keccak", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "signature" version = "1.6.4" @@ -2302,6 +2820,16 @@ dependencies = [ "scrypto-interface", ] +[[package]] +name = "sized-chunks" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +dependencies = [ + "bitmaps", + "typenum", +] + [[package]] name = "skeptic" version = "0.13.7" @@ -2326,6 +2854,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + [[package]] name = "smallvec" version = "1.13.1" @@ -2348,6 +2885,37 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "state-manager" +version = "0.1.0" +source = "git+https://github.com/radixdlt/babylon-node?rev=63a8267196995fef0830e4fbf0271bea65c90ab1#63a8267196995fef0830e4fbf0271bea65c90ab1" +dependencies = [ + "blake2", + "enum_dispatch", + "hex", + "im", + "itertools 0.11.0", + "jni", + "lru", + "node-common", + "prometheus", + "radix-engine", + "radix-engine-common", + "radix-engine-interface", + "radix-engine-queries", + "radix-engine-store-interface", + "radix-engine-stores", + "rand 0.8.5", + "rocksdb", + "sbor", + "slotmap", + "tokio", + "tracing", + "transaction", + "transaction-scenarios", + "utils", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -2527,6 +3095,16 @@ dependencies = [ "syn 2.0.52", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "threadpool" version = "1.8.1" @@ -2536,6 +3114,19 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "thrift" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09678c4cdbb4eed72e18b7c2af1329c69825ed16fcbac62d083fc3e2b0590ff0" +dependencies = [ + "byteorder", + "integer-encoding", + "log", + "ordered-float", + "threadpool", +] + [[package]] name = "time" version = "0.3.34" @@ -2593,11 +3184,25 @@ dependencies = [ "libc", "mio", "num_cpus", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", + "tokio-macros", "windows-sys 0.48.0", ] +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + [[package]] name = "tokio-native-tls" version = "0.3.1" @@ -2608,6 +3213,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.10" @@ -2669,9 +3285,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + [[package]] name = "tracing-core" version = "0.1.32" @@ -2679,12 +3307,63 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-opentelemetry" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21ebb87a95ea13271332df069020513ab70bdb5637ca42d6e492dc3bbbad48de" +dependencies = [ + "once_cell", + "opentelemetry", + "tracing", + "tracing-core", + "tracing-log 0.1.4", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log 0.2.0", ] [[package]] name = "transaction" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "bech32", "hex", @@ -2696,6 +3375,24 @@ dependencies = [ "utils", ] +[[package]] +name = "transaction-scenarios" +version = "1.1.0-rc1" +source = "git+https://github.com/radixdlt/radixdlt-scrypto?tag=anemone-e212f2ea#e212f2ea33f05f7980ef6b4026edf60e162aaae3" +dependencies = [ + "hex", + "itertools 0.10.5", + "radix-engine", + "radix-engine-interface", + "radix-engine-store-interface", + "radix-engine-stores", + "sbor", + "scrypto", + "transaction", + "utils", + "walkdir", +] + [[package]] name = "triomphe" version = "0.1.11" @@ -2776,7 +3473,7 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "utils" version = "1.1.0-dev" -source = "git+https://github.com/radixdlt/radixdlt-scrypto?rev=ef169b1e1348b8dbad977ba81d086ee1e80d6ff8#ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" dependencies = [ "indexmap 2.0.0-pre", "serde", @@ -2792,6 +3489,12 @@ dependencies = [ "serde", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" @@ -2829,9 +3532,9 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -3278,3 +3981,13 @@ dependencies = [ "quote", "syn 2.0.52", ] + +[[package]] +name = "zstd-sys" +version = "2.0.9+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index c0f8ebff..8a52db6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ members = [ "libraries/scrypto-math", # Tools "tools/bootstrap", + "tools/publishing-tool", # Tests "tests" ] @@ -27,21 +28,21 @@ edition = "2021" description = "The implementation of project Ignition in Scrypto for the Radix Ledger" [workspace.dependencies] -sbor = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" } -utils = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" } -scrypto = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" } -native-sdk = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" } -transaction = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" } -radix-engine = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" } -radix-engine-common = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" } -radix-engine-stores = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" } -radix-engine-derive = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" } -radix-engine-queries = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" } -radix-engine-interface = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" } -radix-engine-store-interface = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" } +sbor = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +utils = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +scrypto = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +native-sdk = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +transaction = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +radix-engine = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +radix-engine-common = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +radix-engine-stores = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +radix-engine-derive = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +radix-engine-queries = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +radix-engine-interface = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +radix-engine-store-interface = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -scrypto-unit = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" } -scrypto-test = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" } +scrypto-unit = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +scrypto-test = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } [profile.release] opt-level = 'z' @@ -52,4 +53,20 @@ strip = true overflow-checks = true [workspace.lints.clippy] -arithmetic_side_effects = "warn" \ No newline at end of file +arithmetic_side_effects = "warn" + +[patch.'https://github.com/radixdlt/radixdlt-scrypto'] +sbor = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +utils = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +scrypto = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +native-sdk = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +transaction = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +radix-engine = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +radix-engine-common = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +radix-engine-stores = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +radix-engine-derive = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +radix-engine-queries = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +radix-engine-interface = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +radix-engine-store-interface = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +scrypto-unit = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +scrypto-test = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } \ No newline at end of file diff --git a/tests/example b/tests/example deleted file mode 100644 index 8b88f962..00000000 --- a/tests/example +++ /dev/null @@ -1 +0,0 @@ -Reading now! \ No newline at end of file diff --git a/tools/publishing-tool/Cargo.toml b/tools/publishing-tool/Cargo.toml new file mode 100644 index 00000000..b0e7fab4 --- /dev/null +++ b/tools/publishing-tool/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "publishing-tool" +description = "A configurable tool used to publish Ignition to various networks." +version.workspace = true +edition.workspace = true + +[dependencies] +sbor = { workspace = true } +transaction = { workspace = true } +scrypto-unit = { workspace = true } +radix-engine = { workspace = true } +radix-engine-common = { workspace = true } +radix-engine-interface = { workspace = true } +radix-engine-store-interface = { workspace = true } + +common = { path = "../../libraries/common" } +ignition = { path = "../../packages/ignition" } +package-loader = { path = "../../libraries/package-loader" } +gateway-client = { path = "../../libraries/gateway-client" } +ociswap-v1-adapter-v1 = { path = "../../packages/ociswap-v1-adapter-v1", features = [ + "manifest-builder-stubs", +] } +caviarnine-v1-adapter-v1 = { path = "../../packages/caviarnine-v1-adapter-v1", features = [ + "manifest-builder-stubs", +] } + +state-manager = { git = "https://github.com/radixdlt/babylon-node", rev = "63a8267196995fef0830e4fbf0271bea65c90ab1" } + +hex = { version = "0.4.3" } + +[lints] +workspace = true diff --git a/tools/publishing-tool/src/database_overlay.rs b/tools/publishing-tool/src/database_overlay.rs new file mode 100644 index 00000000..ce4e5856 --- /dev/null +++ b/tools/publishing-tool/src/database_overlay.rs @@ -0,0 +1,656 @@ +// TODO: This implementation is copied from radixdlt-scrypto into this repo so +// that we don't have to upgrade the scrypto dependency of Ignition and the node +// to a new one (especially since a bunch of things have been moved). The commit +// hash that this was copied from is `30b0956f7ec394ac981a2580d9c927aedd6ffc25`. +// This is an exact copy with no changes. Once we update to a Scrypto dependency +// that includes this, we should remove this copied code. + +#![allow(dead_code, clippy::mem_replace_with_default)] + +use core::borrow::*; +use core::cmp::*; +use core::iter::*; +use radix_engine_common::prelude::*; +use radix_engine_store_interface::interface::*; + +pub type UnmergeableSubstateDatabaseOverlay<'a, S> = + SubstateDatabaseOverlay<&'a S, S>; +pub type MergeableSubstateDatabaseOverlay<'a, S> = + SubstateDatabaseOverlay<&'a mut S, S>; +pub type OwnedSubstateDatabaseOverlay = SubstateDatabaseOverlay; + +pub struct SubstateDatabaseOverlay { + /// The database overlay. All commits made to the database are written to + /// the overlay. This covers new values and deletions too. + overlay: StagingDatabaseUpdates, + + /// A mutable or immutable reference to the root database that this type + /// overlays. It only needs to be mutable if you wish to commit to the root + /// store. To be useful, `S` should implement at least `Borrow`. + root: S, + + /// The concrete type of the underlying substate database. + substate_database_type: PhantomData, +} + +impl<'a, D> UnmergeableSubstateDatabaseOverlay<'a, D> { + pub fn new_unmergeable(root_database: &'a D) -> Self { + Self::new(root_database) + } +} + +impl<'a, D> MergeableSubstateDatabaseOverlay<'a, D> { + pub fn new_mergeable(root_database: &'a mut D) -> Self { + Self::new(root_database) + } +} + +impl OwnedSubstateDatabaseOverlay { + pub fn new_owned(root_database: D) -> Self { + Self::new(root_database) + } +} + +impl SubstateDatabaseOverlay { + pub fn new(root_database: S) -> Self { + Self { + overlay: Default::default(), + root: root_database, + substate_database_type: PhantomData, + } + } + + pub fn deconstruct(self) -> (S, DatabaseUpdates) { + (self.root, self.overlay.into()) + } + + pub fn database_updates(&self) -> DatabaseUpdates { + self.overlay.clone().into() + } + + pub fn into_database_updates(self) -> DatabaseUpdates { + self.overlay.into() + } +} + +impl, D> SubstateDatabaseOverlay { + fn get_readable_root(&self) -> &D { + self.root.borrow() + } +} + +impl, D> SubstateDatabaseOverlay { + fn get_writable_root(&mut self) -> &mut D { + self.root.borrow_mut() + } +} + +impl, D: CommittableSubstateDatabase> + SubstateDatabaseOverlay +{ + pub fn commit_overlay_into_root_store(&mut self) { + let overlay = core::mem::replace( + &mut self.overlay, + StagingDatabaseUpdates::default(), + ); + self.get_writable_root().commit(&overlay.into()); + } +} + +impl, D: SubstateDatabase> SubstateDatabase + for SubstateDatabaseOverlay +{ + fn get_substate( + &self, + partition_key @ DbPartitionKey { + node_key, + partition_num, + }: &DbPartitionKey, + sort_key: &DbSortKey, + ) -> Option { + let overlay_lookup_result = + match self.overlay.node_updates.get(node_key) { + // This particular node key exists in the overlay and probably has + // some partitions written to the overlay. + Some(StagingNodeDatabaseUpdates { partition_updates }) => { + match partition_updates.get(partition_num) { + // This partition has some data written to the overlay + Some(StagingPartitionDatabaseUpdates::Delta { + substate_updates, + }) => { + match substate_updates.get(sort_key) { + // The substate value is written to the overlay. It + // is a database set so we return the new value. + Some(DatabaseUpdate::Set(substate_value)) => { + OverlayLookupResult::Found(Some( + substate_value, + )) + } + // The substate value is written to the overlay. It + // is a database delete so we return a `Found(None)`. + Some(DatabaseUpdate::Delete) => { + OverlayLookupResult::Found(None) + } + // This particular substate was not written to the + // overlay and should be read from the underlying + // database. + None => OverlayLookupResult::NotFound, + } + } + Some(StagingPartitionDatabaseUpdates::Reset { + new_substate_values, + }) => match new_substate_values.get(sort_key) { + // The substate value is written to the overlay. + Some(substate_value) => { + OverlayLookupResult::Found(Some(substate_value)) + } + // In a partition reset we delete all substates in a + // partition and can also write new substates there. If + // the substate that we're looking for can't be found in + // the new substate values of a partition delete then it + // is one of the deleted substates. Therefore, the + // following will report that it has found the substate + // value in the overlay and that the substate does not + // exist. + None => OverlayLookupResult::Found(None), + }, + // This particular partition for the specified node key does + // not exist in the overlay and should be read from the + // underlying database. + None => OverlayLookupResult::NotFound, + } + } + // This particular node key does not exist in the overlay. The + // substate must be read from the underlying database. + None => OverlayLookupResult::NotFound, + }; + + match overlay_lookup_result { + OverlayLookupResult::Found(substate_value) => { + substate_value.cloned() + } + OverlayLookupResult::NotFound => self + .get_readable_root() + .get_substate(partition_key, sort_key), + } + } + + fn list_entries_from( + &self, + partition_key @ DbPartitionKey { + node_key, + partition_num, + }: &DbPartitionKey, + from_sort_key: Option<&DbSortKey>, + ) -> Box + '_> { + // This function iterates over entries of the specified partition. + // Therefore, we don't need to think about other partitions + // here. We first check if there are any partition updates + // for the specified partition. If there is not, no overlaying is needed + // and we can just return the iterator of the root store. + let from_sort_key = from_sort_key.cloned(); + match self.overlay.node_updates.get(node_key) { + // There is a partition update in the overlay. + Some(StagingNodeDatabaseUpdates { partition_updates }) => { + match partition_updates.get(partition_num) { + // The partition was reset. None of the substates of this + // partition that exist in the root + // store "exist" anymore. We just need an iterator over the + // new substates in the reset action. + Some(StagingPartitionDatabaseUpdates::Reset { + new_substate_values, + }) => { + match from_sort_key { + // A `from_sort_key` is specified. Only return sort + // keys that are larger than or equal to the from + // sort key. We do this through BTreeMap's range + // function instead of doing filtering. We're able + // to do this since a `BTreeMap`'s keys are always + // sorted. + Some(from_sort_key) => Box::new( + new_substate_values.range(from_sort_key..).map( + |(sort_key, substate_value)| { + ( + sort_key.clone(), + substate_value.clone(), + ) + }, + ), + ), + // No `from_sort_key` is specified. Start iterating + // from the beginning. + None => Box::new(new_substate_values.iter().map( + |(sort_key, substate_value)| { + (sort_key.clone(), substate_value.clone()) + }, + )), + } + } + // There are some changes that need to be overlayed. + Some(StagingPartitionDatabaseUpdates::Delta { + substate_updates, + }) => { + let underlying = + self.get_readable_root().list_entries_from( + partition_key, + from_sort_key.as_ref(), + ); + + match from_sort_key { + // A `from_sort_key` is specified. Only return sort + // keys that are larger than or equal to the from + // sort key. We do this through BTreeMap's range + // function instead of doing filtering. We're able + // to do this since a `BTreeMap`'s keys are always + // sorted. + Some(from_sort_key) => { + let overlaying = substate_updates + .range(from_sort_key..) + .map(|(sort_key, database_update)| { + match database_update { + DatabaseUpdate::Set( + substate_value, + ) => ( + sort_key.clone(), + Some(substate_value.clone()), + ), + DatabaseUpdate::Delete => { + (sort_key.clone(), None) + } + } + }); + Box::new(OverlayingIterator::new( + underlying, overlaying, + )) + } + // No `from_sort_key` is specified. Start iterating + // from the beginning. + None => { + let overlaying = substate_updates.iter().map( + |(sort_key, database_update)| { + match database_update { + DatabaseUpdate::Set( + substate_value, + ) => ( + sort_key.clone(), + Some(substate_value.clone()), + ), + DatabaseUpdate::Delete => { + (sort_key.clone(), None) + } + } + }, + ); + Box::new(OverlayingIterator::new( + underlying, overlaying, + )) + } + } + } + // Overlay doesn't contain anything for the provided + // partition number. Return an iterator over the data in the + // root store. + None => self.get_readable_root().list_entries_from( + partition_key, + from_sort_key.as_ref(), + ), + } + } + // Overlay doesn't contain anything for the provided node key. + // Return an iterator over the data in the root store. + None => self + .get_readable_root() + .list_entries_from(partition_key, from_sort_key.as_ref()), + } + } +} + +impl CommittableSubstateDatabase for SubstateDatabaseOverlay { + fn commit(&mut self, database_updates: &DatabaseUpdates) { + merge_database_updates(&mut self.overlay, database_updates.clone()) + } +} + +impl, D: ListableSubstateDatabase> ListableSubstateDatabase + for SubstateDatabaseOverlay +{ + fn list_partition_keys( + &self, + ) -> Box + '_> { + let overlying = self + .overlay + .node_updates + .iter() + .flat_map( + |( + node_key, + StagingNodeDatabaseUpdates { partition_updates }, + )| { + partition_updates.keys().map(|partition_num| { + DbPartitionKey { + node_key: node_key.clone(), + partition_num: *partition_num, + } + }) + }, + ) + .map(|partition_key| (partition_key, Some(()))); + let underlying = self + .get_readable_root() + .list_partition_keys() + .map(|partition_key| (partition_key, ())); + + Box::new( + OverlayingIterator::new(underlying, overlying) + .map(|(value, _)| value), + ) + } +} + +pub enum OverlayLookupResult { + Found(T), + NotFound, +} + +fn merge_database_updates( + this: &mut StagingDatabaseUpdates, + other: DatabaseUpdates, +) { + for ( + other_node_key, + NodeDatabaseUpdates { + partition_updates: other_partition_updates, + }, + ) in other.node_updates.into_iter() + { + // Check if the other node key exists in `this` database updates. + match this.node_updates.get_mut(&other_node_key) { + // The node key exists in `this` database updates. + Some(StagingNodeDatabaseUpdates { + partition_updates: this_partition_updates, + }) => { + for (other_partition_num, other_partition_database_updates) in + other_partition_updates.into_iter() + { + // Check if the partition num exists in `this` database + // updates + match this_partition_updates.get_mut(&other_partition_num) { + // The partition exists in both `this` and `other` and + // now we must combine both the partition database + // updates together + Some(this_partition_database_updates) => { + match ( + this_partition_database_updates, + other_partition_database_updates, + ) { + // This and other are both `Delta`. We insert + // all entries in the other state updates into + // this substate updates. This will also + // override anything in `this` with anything in + // `other`. + ( + StagingPartitionDatabaseUpdates::Delta { + substate_updates: this_substate_updates, + }, + PartitionDatabaseUpdates::Delta { + substate_updates: other_substate_updates, + }, + ) => this_substate_updates.extend(other_substate_updates), + // We need to apply the delta on the reset. + ( + StagingPartitionDatabaseUpdates::Reset { + new_substate_values: this_new_substate_values, + }, + PartitionDatabaseUpdates::Delta { + substate_updates: other_substate_updates, + }, + ) => { + for (other_sort_key, other_database_update) in + other_substate_updates.into_iter() + { + match other_database_update { + DatabaseUpdate::Set(other_substate_value) => { + this_new_substate_values + .insert(other_sort_key, other_substate_value); + } + DatabaseUpdate::Delete => { + this_new_substate_values.remove(&other_sort_key); + } + } + } + } + // Whatever the current state is, if the other + // database update is a partition reset then it + // takes precedence. + ( + this_partition_database_updates, + other_partition_database_updates @ PartitionDatabaseUpdates::Reset { .. }, + ) => { + *this_partition_database_updates = other_partition_database_updates.into(); + } + } + } + // The partition num does not exist in `this` database + // updates. This merge is simple, just insert it. + None => { + this_partition_updates.insert( + other_partition_num, + other_partition_database_updates.into(), + ); + } + } + } + } + // The node key does not exist in `this` database updates. This + // merge is simple, just insert it. + None => { + this.node_updates.insert( + other_node_key, + NodeDatabaseUpdates { + partition_updates: other_partition_updates, + } + .into(), + ); + } + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Sbor, Default)] +struct StagingDatabaseUpdates { + node_updates: BTreeMap, +} + +impl From for DatabaseUpdates { + fn from(value: StagingDatabaseUpdates) -> Self { + Self { + node_updates: value + .node_updates + .into_iter() + .map(|(key, value)| (key, NodeDatabaseUpdates::from(value))) + .collect(), + } + } +} + +impl From for StagingDatabaseUpdates { + fn from(value: DatabaseUpdates) -> Self { + Self { + node_updates: value + .node_updates + .into_iter() + .map(|(key, value)| { + (key, StagingNodeDatabaseUpdates::from(value)) + }) + .collect(), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Sbor, Default)] +struct StagingNodeDatabaseUpdates { + partition_updates: + BTreeMap, +} + +impl From for NodeDatabaseUpdates { + fn from(value: StagingNodeDatabaseUpdates) -> Self { + Self { + partition_updates: value + .partition_updates + .into_iter() + .map(|(key, value)| { + (key, PartitionDatabaseUpdates::from(value)) + }) + .collect(), + } + } +} + +impl From for StagingNodeDatabaseUpdates { + fn from(value: NodeDatabaseUpdates) -> Self { + Self { + partition_updates: value + .partition_updates + .into_iter() + .map(|(key, value)| { + (key, StagingPartitionDatabaseUpdates::from(value)) + }) + .collect(), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Sbor)] +enum StagingPartitionDatabaseUpdates { + Delta { + substate_updates: BTreeMap, + }, + + Reset { + new_substate_values: BTreeMap, + }, +} + +impl From for PartitionDatabaseUpdates { + fn from(value: StagingPartitionDatabaseUpdates) -> Self { + match value { + StagingPartitionDatabaseUpdates::Delta { substate_updates } => { + Self::Delta { + substate_updates: substate_updates.into_iter().collect(), + } + } + StagingPartitionDatabaseUpdates::Reset { + new_substate_values, + } => Self::Reset { + new_substate_values: new_substate_values.into_iter().collect(), + }, + } + } +} + +impl From for StagingPartitionDatabaseUpdates { + fn from(value: PartitionDatabaseUpdates) -> Self { + match value { + PartitionDatabaseUpdates::Delta { substate_updates } => { + Self::Delta { + substate_updates: substate_updates.into_iter().collect(), + } + } + PartitionDatabaseUpdates::Reset { + new_substate_values, + } => Self::Reset { + new_substate_values: new_substate_values.into_iter().collect(), + }, + } + } +} + +/// An iterator overlaying a "change on a value" (coming from the [`overlaying`] +/// iterator) over a "base value" (coming from the [`underlying`] iterator). +/// The one is matched to another by a `K` part (of the iterated tuple `(K, +/// V)`), which both iterators are assumed to be ordered by. +pub struct OverlayingIterator +where + U: Iterator, + O: Iterator, +{ + underlying: Peekable, + overlaying: Peekable, +} + +impl OverlayingIterator +where + K: Ord, + U: Iterator, + O: Iterator)>, +{ + /// Creates an overlaying iterator. + /// The [`underlying`] iterator provides the "base values". + /// The [`overlaying`] one provides the "changes" to those values, + /// represented as `Option`: + /// - A [`Some`] is an upsert, i.e. it may override an existing base value, + /// or "insert" a completely new one to the iterated results. + /// - A [`None`] is a delete, which causes the base value to be omitted in + /// the iterated results. + #[allow(clippy::new_ret_no_self)] + pub fn new(underlying: U, overlaying: O) -> impl Iterator { + Self { + underlying: underlying.peekable(), + overlaying: overlaying.peekable(), + } + } +} + +impl Iterator for OverlayingIterator +where + K: Ord, + U: Iterator, + O: Iterator)>, +{ + type Item = (K, V); + + fn next(&mut self) -> Option { + loop { + if let Some(overlaying_key) = self.overlaying.peek_key() { + if let Some(underlying_key) = self.underlying.peek_key() { + match underlying_key.cmp(overlaying_key) { + Ordering::Less => { + return self.underlying.next(); // return and move it + // forward + } + Ordering::Equal => { + self.underlying.next(); // only move it forward + } + Ordering::Greater => { + // leave it as-is + } + }; + } + let (overlaying_key, overlaying_change) = + self.overlaying.next().unwrap(); + match overlaying_change { + Some(value) => return Some((overlaying_key, value)), + None => continue, /* we may need to skip over an + * unbounded number of deletes */ + } + } else { + return self.underlying.next(); + } + } + } +} + +pub trait PeekableKeyExt<'a, K> { + /// Peeks at the next entry's key. + fn peek_key(&'a mut self) -> Option<&'a K>; +} + +impl<'a, K, V: 'a, I> PeekableKeyExt<'a, K> for Peekable +where + I: Iterator, +{ + fn peek_key(&'a mut self) -> Option<&'a K> { + self.peek().map(|(key, _value)| key) + } +} diff --git a/tools/publishing-tool/src/executor/gateway_executor.rs b/tools/publishing-tool/src/executor/gateway_executor.rs new file mode 100644 index 00000000..5c35a065 --- /dev/null +++ b/tools/publishing-tool/src/executor/gateway_executor.rs @@ -0,0 +1,295 @@ +use super::*; +use gateway_client::apis::configuration::*; +use gateway_client::apis::status_api::gateway_status; +use gateway_client::apis::status_api::GatewayStatusError; +use gateway_client::apis::transaction_api::*; +use gateway_client::apis::Error as GatewayClientError; +use gateway_client::models::*; +use radix_engine::transaction::*; +use transaction::manifest::*; +use transaction::prelude::*; + +pub struct GatewayExecutor { + /// The configuration to use when making gateway HTTP requests. + pub configuration: Configuration, + /// The network definition of the network that the gateway talks to. + pub network_definition: NetworkDefinition, + /// The configuration to use when polling for the transaction status. + pub polling_configuration: PollingConfiguration, +} + +impl GatewayExecutor { + pub fn new( + base_url: impl ToOwned, + network_definition: NetworkDefinition, + polling_configuration: PollingConfiguration, + ) -> Self { + Self { + configuration: Configuration { + base_path: base_url.to_owned(), + ..Default::default() + }, + network_definition, + polling_configuration, + } + } +} + +impl Executor for GatewayExecutor { + type Error = GatewayExecutorError; + + fn execute_transaction( + &mut self, + notarized_transaction: &NotarizedTransactionV1, + ) -> Result { + let notarized_transaction_payload_bytes = notarized_transaction + .to_payload_bytes() + .map_err(GatewayExecutorError::NotarizedTransactionEncodeError)?; + + transaction_submit( + &self.configuration, + TransactionSubmitRequest { + notarized_transaction: notarized_transaction_payload_bytes, + }, + ) + .map_err(GatewayExecutorError::TransactionSubmissionError)?; + + let intent_hash_string = { + let intent_hash = notarized_transaction + .prepare() + .map_err( + GatewayExecutorError::NotarizedTransactionPrepareError, + )? + .intent_hash(); + let transaction_hash_encoder = + TransactionHashBech32Encoder::new(&self.network_definition); + transaction_hash_encoder.encode(&intent_hash).map_err( + GatewayExecutorError::TransactionHashBech32mEncoderError, + )? + }; + + for _ in 0..self.polling_configuration.retries { + let transaction_status_response = transaction_status( + &self.configuration, + TransactionStatusRequest { + intent_hash: intent_hash_string.clone(), + }, + ) + .map_err(GatewayExecutorError::TransactionStatusError)?; + + match transaction_status_response.intent_status { + // Do nothing and keep on polling. + TransactionIntentStatus::Unknown + | TransactionIntentStatus::CommitPendingOutcomeUnknown + | TransactionIntentStatus::Pending => {} + TransactionIntentStatus::CommittedSuccess => { + let transaction_committed_result_response = transaction_committed_details( + &self.configuration, + TransactionCommittedDetailsRequest { + intent_hash: intent_hash_string.clone(), + opt_ins: Some(Box::new(TransactionDetailsOptIns { + raw_hex: Some(true), + receipt_state_changes: Some(true), + receipt_fee_summary: Some(true), + receipt_fee_source: Some(true), + receipt_fee_destination: Some(true), + receipt_costing_parameters: Some(true), + receipt_events: Some(true), + receipt_output: Some(true), + affected_global_entities: Some(true), + balance_changes: Some(true), + })), + at_ledger_state: None, + }, + ) + .map_err(GatewayExecutorError::TransactionCommittedDetailsError)?; + + let new_entities = { + let mut new_entities = NewEntities::default(); + + let bech32m_address_decoder = + AddressBech32Decoder::new(&self.network_definition); + let new_global_entities = transaction_committed_result_response + .transaction + .receipt + .expect("We have opted into this") + .state_updates + .expect("We have opted into this") + .new_global_entities + .into_iter() + .map(|Entity { entity_address, .. }| { + bech32m_address_decoder + .validate_and_decode(&entity_address) + .map_err(|_| GatewayExecutorError::AddressBech32mDecodeError) + .and_then(|(_, node_id)| { + node_id.try_into().map(NodeId).map_err(|_| { + GatewayExecutorError::AddressBech32mDecodeError + }) + }) + }); + + for node_id in new_global_entities { + let node_id = node_id?; + if let Ok(package_address) = + PackageAddress::try_from(node_id) + { + new_entities + .new_package_addresses + .insert(package_address); + } else if let Ok(resource_address) = + ResourceAddress::try_from(node_id) + { + new_entities + .new_resource_addresses + .insert(resource_address); + } else if let Ok(component_address) = + ComponentAddress::try_from(node_id) + { + new_entities + .new_component_addresses + .insert(component_address); + } + } + + new_entities + }; + + return Ok(ExecutionReceipt::CommitSuccess { + new_entities, + }); + } + TransactionIntentStatus::CommittedFailure => { + return Ok(ExecutionReceipt::CommitFailure { + reason: transaction_status_response + .intent_status_description, + }) + } + TransactionIntentStatus::PermanentlyRejected + | TransactionIntentStatus::LikelyButNotCertainRejection => { + return Ok(ExecutionReceipt::Rejection { + reason: transaction_status_response + .intent_status_description, + }) + } + } + + std::thread::sleep(std::time::Duration::from_secs( + self.polling_configuration.interval_in_seconds, + )) + } + + Err(GatewayExecutorError::Timeout) + } + + fn preview_transaction( + &mut self, + preview_intent: PreviewIntentV1, + ) -> Result { + let string_manifest = decompile( + &preview_intent.intent.instructions.0, + &self.network_definition, + ) + .map_err(GatewayExecutorError::ManifestDecompileError)?; + + let blob_hex = preview_intent + .intent + .blobs + .blobs + .iter() + .map(|blob| hex::encode(&blob.0)) + .collect::>(); + + let request = TransactionPreviewRequest { + manifest: string_manifest, + blobs_hex: Some(blob_hex), + start_epoch_inclusive: preview_intent + .intent + .header + .start_epoch_inclusive + .number() as i64, + end_epoch_exclusive: preview_intent + .intent + .header + .end_epoch_exclusive + .number() as i64, + notary_public_key: Some(Box::new( + native_public_key_to_gateway_public_key( + &preview_intent.intent.header.notary_public_key, + ), + )), + notary_is_signatory: Some( + preview_intent.intent.header.notary_is_signatory, + ), + tip_percentage: preview_intent.intent.header.tip_percentage as i32, + nonce: preview_intent.intent.header.nonce as i64, + signer_public_keys: preview_intent + .signer_public_keys + .iter() + .map(native_public_key_to_gateway_public_key) + .collect(), + flags: Box::new(TransactionPreviewRequestFlags { + assume_all_signature_proofs: preview_intent + .flags + .assume_all_signature_proofs, + use_free_credit: preview_intent.flags.use_free_credit, + skip_epoch_check: preview_intent.flags.skip_epoch_check, + }), + }; + let response = transaction_preview(&self.configuration, request) + .map_err(GatewayExecutorError::TransactionPreviewError)?; + + scrypto_decode::(&response.encoded_receipt) + .map_err(GatewayExecutorError::TransactionReceiptDecodeError) + .map(|receipt| receipt.into_latest()) + } + + fn get_current_epoch(&mut self) -> Result { + Ok(Epoch::of( + gateway_status(&self.configuration) + .map_err(GatewayExecutorError::GatewayStatusError)? + .ledger_state + .epoch as u64, + )) + } +} + +fn native_public_key_to_gateway_public_key( + native_public_key: &radix_engine_common::prelude::PublicKey, +) -> gateway_client::models::PublicKey { + match native_public_key { + radix_engine::types::PublicKey::Secp256k1(public_key) => { + gateway_client::models::PublicKey::EcdsaSecp256k1 { + key: public_key.0, + } + } + radix_engine::types::PublicKey::Ed25519(public_key) => { + gateway_client::models::PublicKey::EddsaEd25519 { + key: public_key.0, + } + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PollingConfiguration { + pub interval_in_seconds: u64, + pub retries: u64, +} + +#[derive(Debug)] +pub enum GatewayExecutorError { + ManifestDecompileError(DecompileError), + TransactionReceiptDecodeError(DecodeError), + NotarizedTransactionEncodeError(EncodeError), + NotarizedTransactionPrepareError(PrepareError), + TransactionHashBech32mEncoderError(TransactionHashBech32EncodeError), + GatewayStatusError(GatewayClientError), + TransactionStatusError(GatewayClientError), + TransactionPreviewError(GatewayClientError), + TransactionCommittedDetailsError( + GatewayClientError, + ), + TransactionSubmissionError(GatewayClientError), + AddressBech32mDecodeError, + Timeout, +} diff --git a/tools/publishing-tool/src/executor/mainnet_simulator_executor.rs b/tools/publishing-tool/src/executor/mainnet_simulator_executor.rs new file mode 100644 index 00000000..a21f15b6 --- /dev/null +++ b/tools/publishing-tool/src/executor/mainnet_simulator_executor.rs @@ -0,0 +1,99 @@ +use super::*; +use crate::database_overlay::*; +use radix_engine::transaction::*; +use radix_engine::vm::*; +use scrypto_unit::*; +use state_manager::store::*; +use transaction::prelude::*; + +/// An [`Executor`] that simulates the transaction execution on mainnet state. +/// This requires having a mainnet database that the executor can read from. All +/// of the database changes from the transaction execution is written to an +/// overlay which means that the mainnet database's state remains unchanged. +pub struct MainnetSimulatorExecutor<'s>( + TestRunner< + NoExtension, + UnmergeableSubstateDatabaseOverlay<'s, RocksDBStore>, + >, +); + +impl<'s> MainnetSimulatorExecutor<'s> { + pub fn new(database: &'s RocksDBStore) -> Self { + let database = UnmergeableSubstateDatabaseOverlay::new(database); + let test_runner = TestRunnerBuilder::new() + .with_custom_database(database) + .without_trace() + .build(); + Self(test_runner) + } +} + +impl<'s> Executor for MainnetSimulatorExecutor<'s> { + type Error = MainnetSimulatorError; + + fn execute_transaction( + &mut self, + notarized_transaction: &NotarizedTransactionV1, + ) -> Result { + let network_definition = NetworkDefinition::mainnet(); + let raw_transaction = notarized_transaction.to_raw().map_err( + MainnetSimulatorError::NotarizedTransactionRawFormatError, + )?; + + let transaction_receipt = self + .0 + .execute_raw_transaction(&network_definition, &raw_transaction); + + let execution_receipt = match transaction_receipt.result { + TransactionResult::Commit(CommitResult { + outcome: TransactionOutcome::Success(..), + state_update_summary, + .. + }) => ExecutionReceipt::CommitSuccess { + new_entities: NewEntities { + new_component_addresses: state_update_summary + .new_components, + new_resource_addresses: state_update_summary.new_resources, + new_package_addresses: state_update_summary.new_packages, + }, + }, + TransactionResult::Commit(CommitResult { + outcome: TransactionOutcome::Failure(reason), + .. + }) => ExecutionReceipt::CommitFailure { + reason: format!("{:?}", reason), + }, + TransactionResult::Reject(RejectResult { reason }) => { + ExecutionReceipt::Rejection { + reason: format!("{:?}", reason), + } + } + TransactionResult::Abort(AbortResult { reason }) => { + ExecutionReceipt::Abort { + reason: format!("{:?}", reason), + } + } + }; + Ok(execution_receipt) + } + + fn preview_transaction( + &mut self, + preview_intent: PreviewIntentV1, + ) -> Result { + let network_definition = NetworkDefinition::mainnet(); + self.0 + .preview(preview_intent, &network_definition) + .map_err(MainnetSimulatorError::PreviewError) + } + + fn get_current_epoch(&mut self) -> Result { + Ok(self.0.get_current_epoch()) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum MainnetSimulatorError { + NotarizedTransactionRawFormatError(EncodeError), + PreviewError(PreviewError), +} diff --git a/tools/publishing-tool/src/executor/mod.rs b/tools/publishing-tool/src/executor/mod.rs new file mode 100644 index 00000000..8a6921a6 --- /dev/null +++ b/tools/publishing-tool/src/executor/mod.rs @@ -0,0 +1,7 @@ +mod gateway_executor; +mod mainnet_simulator_executor; +mod traits; + +pub use gateway_executor::*; +pub use mainnet_simulator_executor::*; +pub use traits::*; diff --git a/tools/publishing-tool/src/executor/traits.rs b/tools/publishing-tool/src/executor/traits.rs new file mode 100644 index 00000000..a3c80717 --- /dev/null +++ b/tools/publishing-tool/src/executor/traits.rs @@ -0,0 +1,41 @@ +use radix_engine::transaction::TransactionReceiptV1; +use transaction::prelude::*; + +/// A trait that can be implemented by various structs to execute transactions +/// and produce execution receipts. The executor could be object doing the +/// execution itself in case of a node or could delegate the execution to +/// another object like in the case of the gateway. This detail does not matter +/// for the executor. +pub trait Executor { + type Error: Debug; + + fn execute_transaction( + &mut self, + notarized_transaction: &NotarizedTransactionV1, + ) -> Result; + + fn preview_transaction( + &mut self, + preview_intent: PreviewIntentV1, + ) -> Result; + + fn get_current_epoch(&mut self) -> Result; +} + +/// A simplified transaction receipt containing the key pieces of information +/// that must be included in an execution receipt. This is limited by the data +/// that the node can give us. +#[derive(Clone, Debug, PartialEq, Eq, ScryptoSbor)] +pub enum ExecutionReceipt { + CommitSuccess { new_entities: NewEntities }, + CommitFailure { reason: String }, + Rejection { reason: String }, + Abort { reason: String }, +} + +#[derive(Clone, Default, Debug, PartialEq, Eq, ScryptoSbor)] +pub struct NewEntities { + pub new_component_addresses: IndexSet, + pub new_resource_addresses: IndexSet, + pub new_package_addresses: IndexSet, +} diff --git a/tools/publishing-tool/src/main.rs b/tools/publishing-tool/src/main.rs new file mode 100644 index 00000000..3c495211 --- /dev/null +++ b/tools/publishing-tool/src/main.rs @@ -0,0 +1,6 @@ +mod database_overlay; +mod executor; + +fn main() { + println!("Hello, world!"); +} From 8ea84bc06115d45f22b390fb24bccc6627af0216 Mon Sep 17 00:00:00 2001 From: Omar Date: Sun, 3 Mar 2024 00:44:25 +0300 Subject: [PATCH 02/17] [Publishing Tool]: Create the execution service. --- Cargo.lock | 1 + tools/publishing-tool/Cargo.toml | 1 + .../src/executor/execution_service.rs | 154 ++++++++++++++++++ .../src/executor/gateway_executor.rs | 6 + .../executor/mainnet_simulator_executor.rs | 6 + tools/publishing-tool/src/executor/mod.rs | 2 + tools/publishing-tool/src/executor/traits.rs | 4 + 7 files changed, 174 insertions(+) create mode 100644 tools/publishing-tool/src/executor/execution_service.rs diff --git a/Cargo.lock b/Cargo.lock index b62361ea..e73840ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2028,6 +2028,7 @@ dependencies = [ "radix-engine-common", "radix-engine-interface", "radix-engine-store-interface", + "rand 0.8.5", "sbor", "scrypto-unit", "state-manager", diff --git a/tools/publishing-tool/Cargo.toml b/tools/publishing-tool/Cargo.toml index b0e7fab4..d48427a2 100644 --- a/tools/publishing-tool/Cargo.toml +++ b/tools/publishing-tool/Cargo.toml @@ -27,6 +27,7 @@ caviarnine-v1-adapter-v1 = { path = "../../packages/caviarnine-v1-adapter-v1", f state-manager = { git = "https://github.com/radixdlt/babylon-node", rev = "63a8267196995fef0830e4fbf0271bea65c90ab1" } hex = { version = "0.4.3" } +rand = "0.8.5" [lints] workspace = true diff --git a/tools/publishing-tool/src/executor/execution_service.rs b/tools/publishing-tool/src/executor/execution_service.rs new file mode 100644 index 00000000..862853a0 --- /dev/null +++ b/tools/publishing-tool/src/executor/execution_service.rs @@ -0,0 +1,154 @@ +use radix_engine::transaction::*; +use radix_engine_common::prelude::*; +use radix_engine_interface::blueprints::account::*; +use transaction::model::*; +use transaction::prelude::*; + +use super::*; + +/// A simple execution service whose main responsibilities is to construct, +/// submit, and return the result of transactions. +pub struct ExecutionService<'e, E: Executor> { + /// The executor that the service will use to execute transactions. + executor: &'e mut E, + /// The account to use for the payment of fees. + fee_payer_account_address: ComponentAddress, + /// The notary of the transaction + notary_private_key: &'e PrivateKey, + /// The set of private keys that should sign the transaction. + signers_private_keys: &'e [PrivateKey], +} + +impl<'e, E: Executor> ExecutionService<'e, E> { + pub fn new( + executor: &'e mut E, + fee_payer_account_address: ComponentAddress, + notary_private_key: &'e PrivateKey, + additional_signatures: &'e [PrivateKey], + ) -> Self { + Self { + executor, + fee_payer_account_address, + notary_private_key, + signers_private_keys: additional_signatures, + } + } + + pub fn execute_manifest( + &mut self, + mut manifest: TransactionManifestV1, + ) -> Result> { + // The signers for the transaction + let notary_is_signatory = + self.signers_private_keys.iter().any(|private_key| { + private_key.public_key() == self.notary_private_key.public_key() + }); + let signer_private_keys = + self.signers_private_keys.iter().filter(|private_key| { + private_key.public_key() != self.notary_private_key.public_key() + }); + + // Getting the current network definition + let network_definition = self + .executor + .get_network_definition() + .map_err(ExecutionServiceError::ExecutorError)?; + + // Constructing the header + let current_epoch = self + .executor + .get_current_epoch() + .map_err(ExecutionServiceError::ExecutorError)?; + let header = TransactionHeaderV1 { + network_id: network_definition.id, + start_epoch_inclusive: current_epoch, + end_epoch_exclusive: current_epoch + .after(10) + .expect("Not currently an issue"), + nonce: rand::random(), + notary_public_key: self.notary_private_key.public_key(), + notary_is_signatory, + tip_percentage: 0, + }; + + // Getting a preview of the transaction to determine the fees. + let preview_receipt = self + .executor + .preview_transaction(PreviewIntentV1 { + intent: IntentV1 { + header: header.clone(), + instructions: InstructionsV1(manifest.instructions.clone()), + blobs: BlobsV1 { + blobs: manifest + .blobs + .clone() + .into_values() + .map(BlobV1) + .collect(), + }, + message: MessageV1::None, + }, + signer_public_keys: signer_private_keys + .clone() + .map(|private_key| private_key.public_key()) + .collect(), + flags: PreviewFlags { + use_free_credit: false, + assume_all_signature_proofs: false, + skip_epoch_check: false, + }, + }) + .map_err(ExecutionServiceError::ExecutorError)?; + + if !preview_receipt.is_commit_success() { + return Err( + ExecutionServiceError::TransactionPreviewWasNotSuccessful( + manifest.clone(), + preview_receipt, + ), + ); + } + let total_fees = preview_receipt.fee_summary.total_cost(); + let total_fees_plus_padding = total_fees * dec!(1.20); + + // Adding a lock fee instruction to the manifest. + manifest.instructions.insert( + 0, + InstructionV1::CallMethod { + address: self.fee_payer_account_address.into(), + method_name: ACCOUNT_LOCK_FEE_IDENT.to_string(), + args: to_manifest_value(&AccountLockFeeInput { + amount: total_fees_plus_padding, + }) + .expect("Can't fail!"), + }, + ); + + // Constructing the transaction. + let mut transaction_builder = + TransactionBuilder::new().header(header).manifest(manifest); + for signer_private_key in signer_private_keys { + transaction_builder = transaction_builder.sign(signer_private_key) + } + let transaction = transaction_builder + .notarize(self.notary_private_key) + .build(); + + // Submitting the transaction + let receipt = self + .executor + .execute_transaction(&transaction) + .map_err(ExecutionServiceError::ExecutorError)?; + + Ok(receipt) + } +} + +#[derive(Debug)] +pub enum ExecutionServiceError { + ExecutorError(::Error), + TransactionPreviewWasNotSuccessful( + TransactionManifestV1, + TransactionReceipt, + ), +} diff --git a/tools/publishing-tool/src/executor/gateway_executor.rs b/tools/publishing-tool/src/executor/gateway_executor.rs index 5c35a065..f3555b26 100644 --- a/tools/publishing-tool/src/executor/gateway_executor.rs +++ b/tools/publishing-tool/src/executor/gateway_executor.rs @@ -251,6 +251,12 @@ impl Executor for GatewayExecutor { .epoch as u64, )) } + + fn get_network_definition( + &mut self, + ) -> Result { + Ok(self.network_definition.clone()) + } } fn native_public_key_to_gateway_public_key( diff --git a/tools/publishing-tool/src/executor/mainnet_simulator_executor.rs b/tools/publishing-tool/src/executor/mainnet_simulator_executor.rs index a21f15b6..337771d5 100644 --- a/tools/publishing-tool/src/executor/mainnet_simulator_executor.rs +++ b/tools/publishing-tool/src/executor/mainnet_simulator_executor.rs @@ -90,6 +90,12 @@ impl<'s> Executor for MainnetSimulatorExecutor<'s> { fn get_current_epoch(&mut self) -> Result { Ok(self.0.get_current_epoch()) } + + fn get_network_definition( + &mut self, + ) -> Result { + Ok(NetworkDefinition::mainnet()) + } } #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/tools/publishing-tool/src/executor/mod.rs b/tools/publishing-tool/src/executor/mod.rs index 8a6921a6..0f330d14 100644 --- a/tools/publishing-tool/src/executor/mod.rs +++ b/tools/publishing-tool/src/executor/mod.rs @@ -1,7 +1,9 @@ +mod execution_service; mod gateway_executor; mod mainnet_simulator_executor; mod traits; +pub use execution_service::*; pub use gateway_executor::*; pub use mainnet_simulator_executor::*; pub use traits::*; diff --git a/tools/publishing-tool/src/executor/traits.rs b/tools/publishing-tool/src/executor/traits.rs index a3c80717..6a674fa3 100644 --- a/tools/publishing-tool/src/executor/traits.rs +++ b/tools/publishing-tool/src/executor/traits.rs @@ -20,6 +20,10 @@ pub trait Executor { ) -> Result; fn get_current_epoch(&mut self) -> Result; + + fn get_network_definition( + &mut self, + ) -> Result; } /// A simplified transaction receipt containing the key pieces of information From a36666d3cc4fccce35ade09675814761d5455a89 Mon Sep 17 00:00:00 2001 From: Omar Date: Tue, 5 Mar 2024 00:47:48 +0300 Subject: [PATCH 03/17] [Publishing Tool]: Add a publishing tool --- Cargo.lock | 143 +- Cargo.toml | 57 +- ..._collection_item_vault_aggregated_vault.rs | 15 +- .../state_entity_details_response_item.rs | 3 +- ...entity_details_response_package_details.rs | 11 +- ..._fungible_resource_vaults_page_response.rs | 14 +- libraries/scrypto-interface/src/handlers.rs | 39 +- .../src/blueprint_interface.rs | 23 + packages/ignition/src/blueprint.rs | 10 +- tests/src/environment.rs | 3 +- tests/tests/caviarnine_v1.rs | 15 +- tests/tests/caviarnine_v1_simulation.rs | 6 +- tests/tests/defiplaza_v2.rs | 9 +- tests/tests/ociswap_v1.rs | 9 +- tests/tests/ociswap_v2.rs | 15 +- tools/bootstrap/Cargo.toml | 34 - tools/bootstrap/src/cli.rs | 20 - tools/bootstrap/src/error.rs | 65 - tools/bootstrap/src/main.rs | 16 - tools/bootstrap/src/mainnet_testing.rs | 666 -------- tools/bootstrap/src/stokenet_production.rs | 662 -------- tools/bootstrap/src/transaction_service.rs | 386 ----- tools/bootstrap/src/types/mod.rs | 5 - .../src/types/name_indexed_dex_information.rs | 30 - .../name_indexed_resource_information.rs | 45 - tools/publishing-tool/Cargo.toml | 17 +- .../default_configurations/mainnet_testing.rs | 304 ++++ .../src/cli/default_configurations/mod.rs | 33 + tools/publishing-tool/src/cli/mod.rs | 18 + tools/publishing-tool/src/cli/publish.rs | 70 + tools/publishing-tool/src/error.rs | 20 + .../executor/mainnet_simulator_executor.rs | 105 -- tools/publishing-tool/src/executor/mod.rs | 9 - .../src/macros.rs | 12 +- tools/publishing-tool/src/main.rs | 23 +- .../execution_service.rs | 91 +- .../gateway_connector.rs} | 69 +- .../mainnet_simulator_connector.rs | 140 ++ .../src/network_connection_provider/mod.rs | 9 + .../traits.rs | 26 +- .../src/publishing/configuration.rs | 254 +++ tools/publishing-tool/src/publishing/error.rs | 4 + .../publishing-tool/src/publishing/handler.rs | 1412 +++++++++++++++++ .../publishing-tool/src/publishing/macros.rs | 134 ++ tools/publishing-tool/src/publishing/mod.rs | 9 + tools/publishing-tool/src/utils.rs | 39 + 46 files changed, 2879 insertions(+), 2220 deletions(-) delete mode 100644 tools/bootstrap/Cargo.toml delete mode 100644 tools/bootstrap/src/cli.rs delete mode 100644 tools/bootstrap/src/error.rs delete mode 100644 tools/bootstrap/src/main.rs delete mode 100644 tools/bootstrap/src/mainnet_testing.rs delete mode 100644 tools/bootstrap/src/stokenet_production.rs delete mode 100644 tools/bootstrap/src/transaction_service.rs delete mode 100644 tools/bootstrap/src/types/mod.rs delete mode 100644 tools/bootstrap/src/types/name_indexed_dex_information.rs delete mode 100644 tools/bootstrap/src/types/name_indexed_resource_information.rs create mode 100644 tools/publishing-tool/src/cli/default_configurations/mainnet_testing.rs create mode 100644 tools/publishing-tool/src/cli/default_configurations/mod.rs create mode 100644 tools/publishing-tool/src/cli/mod.rs create mode 100644 tools/publishing-tool/src/cli/publish.rs create mode 100644 tools/publishing-tool/src/error.rs delete mode 100644 tools/publishing-tool/src/executor/mainnet_simulator_executor.rs delete mode 100644 tools/publishing-tool/src/executor/mod.rs rename tools/{bootstrap => publishing-tool}/src/macros.rs (87%) rename tools/publishing-tool/src/{executor => network_connection_provider}/execution_service.rs (62%) rename tools/publishing-tool/src/{executor/gateway_executor.rs => network_connection_provider/gateway_connector.rs} (82%) create mode 100644 tools/publishing-tool/src/network_connection_provider/mainnet_simulator_connector.rs create mode 100644 tools/publishing-tool/src/network_connection_provider/mod.rs rename tools/publishing-tool/src/{executor => network_connection_provider}/traits.rs (57%) create mode 100644 tools/publishing-tool/src/publishing/configuration.rs create mode 100644 tools/publishing-tool/src/publishing/error.rs create mode 100644 tools/publishing-tool/src/publishing/handler.rs create mode 100644 tools/publishing-tool/src/publishing/macros.rs create mode 100644 tools/publishing-tool/src/publishing/mod.rs create mode 100644 tools/publishing-tool/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index e73840ed..617daa46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -236,27 +236,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "bootstrap" -version = "0.1.0" -dependencies = [ - "caviarnine-v1-adapter-v1", - "clap", - "common", - "gateway-client", - "hex", - "ignition", - "ociswap-v1-adapter-v1", - "package-loader", - "radix-engine", - "radix-engine-interface", - "rand 0.8.5", - "sbor", - "serde", - "serde_json", - "transaction", -] - [[package]] name = "bumpalo" version = "3.15.3" @@ -782,6 +761,29 @@ dependencies = [ "syn 2.0.52", ] +[[package]] +name = "env_filter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c012a26a7f605efc424dd53697843a72be7dc86ad2d01f7814337794a12231d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -1081,6 +1083,12 @@ dependencies = [ "serde", ] +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + [[package]] name = "http" version = "0.2.11" @@ -1289,6 +1297,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.10" @@ -1447,6 +1464,22 @@ dependencies = [ "libc", ] +[[package]] +name = "macro_rules_attribute" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a82271f7bc033d84bbca59a3ce3e4159938cb08a9c3aebbe54d215131518a13" +dependencies = [ + "macro_rules_attribute-proc_macro", + "paste", +] + +[[package]] +name = "macro_rules_attribute-proc_macro" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dd856d451cc0da70e2ef2ce95a18e39a93b7558bedf10201ad28503f918568" + [[package]] name = "memchr" version = "2.7.1" @@ -1520,7 +1553,7 @@ dependencies = [ [[package]] name = "native-sdk" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "radix-engine-common", "radix-engine-derive", @@ -2018,11 +2051,19 @@ name = "publishing-tool" version = "0.1.0" dependencies = [ "caviarnine-v1-adapter-v1", + "clap", "common", + "defiplaza-v2-adapter-v1", + "env_logger", "gateway-client", "hex", + "hex-literal", "ignition", + "itertools 0.12.1", + "log", + "macro_rules_attribute", "ociswap-v1-adapter-v1", + "ociswap-v2-adapter-v1", "package-loader", "radix-engine", "radix-engine-common", @@ -2030,7 +2071,9 @@ dependencies = [ "radix-engine-store-interface", "rand 0.8.5", "sbor", + "sbor-json", "scrypto-unit", + "serde_json", "state-manager", "transaction", ] @@ -2058,7 +2101,7 @@ dependencies = [ [[package]] name = "radix-engine" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "bitflags 1.3.2", "colored", @@ -2088,7 +2131,7 @@ dependencies = [ [[package]] name = "radix-engine-common" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "bech32", "blake2", @@ -2113,7 +2156,7 @@ dependencies = [ [[package]] name = "radix-engine-derive" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "proc-macro2", "quote", @@ -2124,7 +2167,7 @@ dependencies = [ [[package]] name = "radix-engine-interface" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "bitflags 1.3.2", "const-sha1", @@ -2146,7 +2189,7 @@ dependencies = [ [[package]] name = "radix-engine-macros" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "paste", "proc-macro2", @@ -2158,7 +2201,7 @@ dependencies = [ [[package]] name = "radix-engine-profiling" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "fixedstr", ] @@ -2166,7 +2209,7 @@ dependencies = [ [[package]] name = "radix-engine-queries" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "hex", "itertools 0.10.5", @@ -2182,7 +2225,7 @@ dependencies = [ [[package]] name = "radix-engine-store-interface" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "hex", "itertools 0.10.5", @@ -2196,7 +2239,7 @@ dependencies = [ [[package]] name = "radix-engine-stores" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "hex", "itertools 0.10.5", @@ -2369,7 +2412,7 @@ dependencies = [ [[package]] name = "resources-tracker-macro" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "proc-macro2", "quote", @@ -2454,7 +2497,7 @@ dependencies = [ [[package]] name = "sbor" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "const-sha1", "hex", @@ -2468,7 +2511,7 @@ dependencies = [ [[package]] name = "sbor-derive" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "proc-macro2", "sbor-derive-common", @@ -2477,7 +2520,7 @@ dependencies = [ [[package]] name = "sbor-derive-common" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "const-sha1", "itertools 0.10.5", @@ -2486,6 +2529,21 @@ dependencies = [ "syn 1.0.93", ] +[[package]] +name = "sbor-json" +version = "1.0.10" +source = "git+https://github.com/radixdlt/radix-engine-toolkit?rev=1cfe879c7370cfa497857ada7a8973f8a3388abc#1cfe879c7370cfa497857ada7a8973f8a3388abc" +dependencies = [ + "bech32", + "radix-engine-common", + "radix-engine-interface", + "regex", + "sbor", + "serde", + "serde_json", + "serde_with", +] + [[package]] name = "schannel" version = "0.1.23" @@ -2519,7 +2577,7 @@ checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" [[package]] name = "scrypto" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "bech32", "const-sha1", @@ -2541,7 +2599,7 @@ dependencies = [ [[package]] name = "scrypto-derive" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "proc-macro2", "quote", @@ -2578,7 +2636,7 @@ dependencies = [ [[package]] name = "scrypto-schema" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "bitflags 1.3.2", "radix-engine-common", @@ -2589,7 +2647,7 @@ dependencies = [ [[package]] name = "scrypto-test" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "native-sdk", "ouroboros", @@ -2607,7 +2665,7 @@ dependencies = [ [[package]] name = "scrypto-unit" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "radix-engine", "radix-engine-interface", @@ -2699,6 +2757,7 @@ version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ + "indexmap 2.2.5", "itoa", "ryu", "serde", @@ -3364,7 +3423,7 @@ dependencies = [ [[package]] name = "transaction" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "bech32", "hex", @@ -3474,7 +3533,7 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "utils" version = "1.1.0-dev" -source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=feff02d54b6b7b348b6a9cc465971004a41a9b2f#feff02d54b6b7b348b6a9cc465971004a41a9b2f" +source = "git+https://www.github.com/radixdlt/radixdlt-scrypto.git?rev=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "indexmap 2.0.0-pre", "serde", diff --git a/Cargo.toml b/Cargo.toml index 8a52db6b..1b59d8db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,6 @@ members = [ "libraries/ports-interface", "libraries/scrypto-math", # Tools - "tools/bootstrap", "tools/publishing-tool", # Tests "tests" @@ -28,21 +27,21 @@ edition = "2021" description = "The implementation of project Ignition in Scrypto for the Radix Ledger" [workspace.dependencies] -sbor = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -utils = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -scrypto = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -native-sdk = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -transaction = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -radix-engine = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -radix-engine-common = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -radix-engine-stores = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -radix-engine-derive = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -radix-engine-queries = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -radix-engine-interface = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -radix-engine-store-interface = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +sbor = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +utils = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +scrypto = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +native-sdk = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +transaction = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +radix-engine = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +radix-engine-common = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +radix-engine-stores = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +radix-engine-derive = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +radix-engine-queries = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +radix-engine-interface = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +radix-engine-store-interface = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } -scrypto-unit = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -scrypto-test = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } +scrypto-unit = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +scrypto-test = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } [profile.release] opt-level = 'z' @@ -56,17 +55,17 @@ overflow-checks = true arithmetic_side_effects = "warn" [patch.'https://github.com/radixdlt/radixdlt-scrypto'] -sbor = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -utils = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -scrypto = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -native-sdk = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -transaction = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -radix-engine = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -radix-engine-common = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -radix-engine-stores = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -radix-engine-derive = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -radix-engine-queries = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -radix-engine-interface = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -radix-engine-store-interface = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -scrypto-unit = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } -scrypto-test = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "feff02d54b6b7b348b6a9cc465971004a41a9b2f" } \ No newline at end of file +sbor = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +utils = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +scrypto = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +native-sdk = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +transaction = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +radix-engine = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +radix-engine-common = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +radix-engine-stores = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +radix-engine-derive = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +radix-engine-queries = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +radix-engine-interface = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +radix-engine-store-interface = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +scrypto-unit = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } +scrypto-test = { git = "https://www.github.com/radixdlt/radixdlt-scrypto.git", rev = "4887c5e4be2603433592ed290b70b1a0c03cced3" } \ No newline at end of file diff --git a/libraries/gateway-client/src/models/non_fungible_resources_collection_item_vault_aggregated_vault.rs b/libraries/gateway-client/src/models/non_fungible_resources_collection_item_vault_aggregated_vault.rs index 913b84f1..eb72fb67 100644 --- a/libraries/gateway-client/src/models/non_fungible_resources_collection_item_vault_aggregated_vault.rs +++ b/libraries/gateway-client/src/models/non_fungible_resources_collection_item_vault_aggregated_vault.rs @@ -1,10 +1,19 @@ #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] pub struct NonFungibleResourcesCollectionItemVaultAggregatedVault { - - #[serde(rename = "total_count", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + #[serde( + rename = "total_count", + default, + with = "::serde_with::rust::double_option", + skip_serializing_if = "Option::is_none" + )] pub total_count: Option>, - #[serde(rename = "next_cursor", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + #[serde( + rename = "next_cursor", + default, + with = "::serde_with::rust::double_option", + skip_serializing_if = "Option::is_none" + )] pub next_cursor: Option>, #[serde(rename = "items")] pub items: Vec, diff --git a/libraries/gateway-client/src/models/state_entity_details_response_item.rs b/libraries/gateway-client/src/models/state_entity_details_response_item.rs index c74fb398..ab048f5b 100644 --- a/libraries/gateway-client/src/models/state_entity_details_response_item.rs +++ b/libraries/gateway-client/src/models/state_entity_details_response_item.rs @@ -29,8 +29,7 @@ pub struct StateEntityDetailsResponseItem { )] pub explicit_metadata: Option>, #[serde(rename = "details", skip_serializing_if = "Option::is_none")] - pub details: - Option>, + pub details: Option, } impl StateEntityDetailsResponseItem { diff --git a/libraries/gateway-client/src/models/state_entity_details_response_package_details.rs b/libraries/gateway-client/src/models/state_entity_details_response_package_details.rs index 28de3148..1d629e9a 100644 --- a/libraries/gateway-client/src/models/state_entity_details_response_package_details.rs +++ b/libraries/gateway-client/src/models/state_entity_details_response_package_details.rs @@ -11,12 +11,17 @@ pub struct StateEntityDetailsResponsePackageDetails { #[serde(rename = "code_hex")] pub code_hex: String, - #[serde(rename = "royalty_vault_balance", skip_serializing_if = "Option::is_none")] + #[serde( + rename = "royalty_vault_balance", + skip_serializing_if = "Option::is_none" + )] pub royalty_vault_balance: Option, #[serde(rename = "blueprints", skip_serializing_if = "Option::is_none")] - pub blueprints: Option>, + pub blueprints: + Option>, #[serde(rename = "schemas", skip_serializing_if = "Option::is_none")] - pub schemas: Option>, + pub schemas: + Option>, } impl StateEntityDetailsResponsePackageDetails { diff --git a/libraries/gateway-client/src/models/state_entity_non_fungible_resource_vaults_page_response.rs b/libraries/gateway-client/src/models/state_entity_non_fungible_resource_vaults_page_response.rs index 5587a241..529b86cf 100644 --- a/libraries/gateway-client/src/models/state_entity_non_fungible_resource_vaults_page_response.rs +++ b/libraries/gateway-client/src/models/state_entity_non_fungible_resource_vaults_page_response.rs @@ -3,10 +3,20 @@ pub struct StateEntityNonFungibleResourceVaultsPageResponse { #[serde(rename = "ledger_state")] pub ledger_state: Box, - #[serde(rename = "total_count", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + #[serde( + rename = "total_count", + default, + with = "::serde_with::rust::double_option", + skip_serializing_if = "Option::is_none" + )] pub total_count: Option>, - #[serde(rename = "next_cursor", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + #[serde( + rename = "next_cursor", + default, + with = "::serde_with::rust::double_option", + skip_serializing_if = "Option::is_none" + )] pub next_cursor: Option>, #[serde(rename = "items")] pub items: Vec, diff --git a/libraries/scrypto-interface/src/handlers.rs b/libraries/scrypto-interface/src/handlers.rs index 8b3455ce..d79da5e2 100644 --- a/libraries/scrypto-interface/src/handlers.rs +++ b/libraries/scrypto-interface/src/handlers.rs @@ -149,8 +149,8 @@ fn generate_scrypto_stub( let mut arguments = arguments.clone(); if arguments.is_function() { arguments.add_argument_to_end( - Ident::new("blueprint_package_address", ident.span()), - parse_quote!(::radix_engine_interface::prelude::PackageAddress) + Ident::new("blueprint_package_address", ident.span()), + parse_quote!(::radix_engine_interface::prelude::PackageAddress), ); } @@ -296,14 +296,12 @@ fn generate_scrypto_test_stub( let mut arguments = arguments.clone(); if arguments.is_function() { arguments.add_argument_to_end( - Ident::new("blueprint_package_address", ident.span()), - parse_quote!(::radix_engine_interface::prelude::PackageAddress) + Ident::new("blueprint_package_address", ident.span()), + parse_quote!(::radix_engine_interface::prelude::PackageAddress), ); } - arguments.add_argument_to_end( - Ident::new("env", ident.span()), - parse_quote!(&mut Y) - ); + arguments + .add_argument_to_end(Ident::new("env", ident.span()), parse_quote!(&mut Y)); let inner = if arguments.is_function() { quote! { @@ -421,29 +419,24 @@ fn generate_manifest_builder_stub( if arguments.is_function() { arguments.add_argument_to_beginning( Ident::new("blueprint_package_address", ident.span()), - parse_quote!( - ::radix_engine_interface::prelude::PackageAddress - ), + parse_quote!(::radix_engine_interface::prelude::PackageAddress), ); } else { arguments.add_argument_to_beginning( Ident::new("component_address", ident.span()), - parse_quote!( - impl ::transaction::builder::ResolvableGlobalAddress - ), + parse_quote!(impl ::transaction::builder::ResolvableGlobalAddress), ); } - let fn_ident = format_ident!( - "{}_{}", - struct_ident.to_string().to_snake_case(), - ident - ); + let fn_ident = + format_ident!("{}_{}", struct_ident.to_string().to_snake_case(), ident); - arguments.manifest_arguments().map(|arguments| quote! { - #(#attrs)* - #[allow(clippy::too_many_arguments)] - #token_fn #fn_ident ( self, #arguments ) -> Self #semi_colon + arguments.manifest_arguments().map(|arguments| { + quote! { + #(#attrs)* + #[allow(clippy::too_many_arguments)] + #token_fn #fn_ident ( self, #arguments ) -> Self #semi_colon + } }) }, ) diff --git a/packages/defiplaza-v2-adapter-v1/src/blueprint_interface.rs b/packages/defiplaza-v2-adapter-v1/src/blueprint_interface.rs index 37993a83..1e53a5fb 100644 --- a/packages/defiplaza-v2-adapter-v1/src/blueprint_interface.rs +++ b/packages/defiplaza-v2-adapter-v1/src/blueprint_interface.rs @@ -165,3 +165,26 @@ impl From for ShortageState { } } } + +#[derive( + ScryptoSbor, + ManifestSbor, + Copy, + Clone, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, +)] +pub struct PlazaPair { + pub config: PairConfig, + pub state: PairState, + pub base_address: ResourceAddress, + pub quote_address: ResourceAddress, + pub base_divisibility: u8, + pub quote_divisibility: u8, + pub base_pool: ComponentAddress, + pub quote_pool: ComponentAddress, +} diff --git a/packages/ignition/src/blueprint.rs b/packages/ignition/src/blueprint.rs index 6f1cb30f..47845e4a 100644 --- a/packages/ignition/src/blueprint.rs +++ b/packages/ignition/src/blueprint.rs @@ -579,7 +579,7 @@ mod ignition { value .checked_round(17, RoundingMode::ToPositiveInfinity) }) - .expect(OVERFLOW_ERROR); + .unwrap_or(Decimal::MAX); assert!( pool_reported_value_of_user_resource_in_protocol_resource <= maximum_amount, @@ -1483,10 +1483,12 @@ mod ignition { } } else { drop(entry); - self.pool_units - .insert(global_id, indexmap! { + self.pool_units.insert( + global_id, + indexmap! { pool_units_resource_address => Vault::with_bucket(pool_units) - }) + }, + ) } } diff --git a/tests/src/environment.rs b/tests/src/environment.rs index 066c8be1..9b77ab26 100644 --- a/tests/src/environment.rs +++ b/tests/src/environment.rs @@ -458,8 +458,7 @@ impl ScryptoTestEnv { let resource_y = ResourceManager(resource_y).mint_fungible(dec!(100_000_000), &mut env)?; - let (_, change1) = - defiplaza_pool.add_liquidity(resource_x, None, &mut env)?; + let (_, change1) = defiplaza_pool.add_liquidity(resource_x, None, &mut env)?; let (_, change2) = defiplaza_pool.add_liquidity(resource_y, None, &mut env)?; let change_amount1 = change1 .map(|bucket| bucket.amount(&mut env).unwrap()) diff --git a/tests/tests/caviarnine_v1.rs b/tests/tests/caviarnine_v1.rs index 5d9f2a4c..6a8ca582 100644 --- a/tests/tests/caviarnine_v1.rs +++ b/tests/tests/caviarnine_v1.rs @@ -248,7 +248,9 @@ fn can_close_a_liquidity_position_in_caviarnine_that_fits_into_fee_limits() { ModuleId::Main, ConsensusManagerField::ProposerMilliTimestamp.field_index(), ConsensusManagerProposerMilliTimestampFieldPayload::from_content_source( - ProposerMilliTimestampSubstate { epoch_milli: maturity_instant.seconds_since_unix_epoch * 1000 } + ProposerMilliTimestampSubstate { + epoch_milli: maturity_instant.seconds_since_unix_epoch * 1000, + }, ), ) .unwrap(); @@ -260,8 +262,9 @@ fn can_close_a_liquidity_position_in_caviarnine_that_fits_into_fee_limits() { ConsensusManagerField::ProposerMinuteTimestamp.field_index(), ConsensusManagerProposerMinuteTimestampFieldPayload::from_content_source( ProposerMinuteTimestampSubstate { - epoch_minute: i32::try_from(maturity_instant.seconds_since_unix_epoch / 60).unwrap(), - } + epoch_minute: i32::try_from(maturity_instant.seconds_since_unix_epoch / 60) + .unwrap(), + }, ), ) .unwrap(); @@ -1097,10 +1100,8 @@ fn test_effect_of_price_action_on_fees(multiplier: i32, bin_span: u32) { ConsensusManagerField::ProposerMinuteTimestamp.field_index(), ConsensusManagerProposerMinuteTimestampFieldPayload::from_content_source( ProposerMinuteTimestampSubstate { - epoch_minute: i32::try_from( - maturity_instant.seconds_since_unix_epoch / 60, - ) - .unwrap(), + epoch_minute: i32::try_from(maturity_instant.seconds_since_unix_epoch / 60) + .unwrap(), }, ), ) diff --git a/tests/tests/caviarnine_v1_simulation.rs b/tests/tests/caviarnine_v1_simulation.rs index d9bae7e7..394585d0 100644 --- a/tests/tests/caviarnine_v1_simulation.rs +++ b/tests/tests/caviarnine_v1_simulation.rs @@ -1042,10 +1042,8 @@ fn test_effect_of_price_action_on_fees( ConsensusManagerField::ProposerMinuteTimestamp.field_index(), ConsensusManagerProposerMinuteTimestampFieldPayload::from_content_source( ProposerMinuteTimestampSubstate { - epoch_minute: i32::try_from( - maturity_instant.seconds_since_unix_epoch / 60, - ) - .unwrap(), + epoch_minute: i32::try_from(maturity_instant.seconds_since_unix_epoch / 60) + .unwrap(), }, ), ) diff --git a/tests/tests/defiplaza_v2.rs b/tests/tests/defiplaza_v2.rs index 78711179..323bc054 100644 --- a/tests/tests/defiplaza_v2.rs +++ b/tests/tests/defiplaza_v2.rs @@ -200,7 +200,9 @@ fn can_close_a_liquidity_position_in_defiplaza_that_fits_into_fee_limits() { ModuleId::Main, ConsensusManagerField::ProposerMilliTimestamp.field_index(), ConsensusManagerProposerMilliTimestampFieldPayload::from_content_source( - ProposerMilliTimestampSubstate { epoch_milli: maturity_instant.seconds_since_unix_epoch * 1000 } + ProposerMilliTimestampSubstate { + epoch_milli: maturity_instant.seconds_since_unix_epoch * 1000, + }, ), ) .unwrap(); @@ -212,8 +214,9 @@ fn can_close_a_liquidity_position_in_defiplaza_that_fits_into_fee_limits() { ConsensusManagerField::ProposerMinuteTimestamp.field_index(), ConsensusManagerProposerMinuteTimestampFieldPayload::from_content_source( ProposerMinuteTimestampSubstate { - epoch_minute: i32::try_from(maturity_instant.seconds_since_unix_epoch / 60).unwrap(), - } + epoch_minute: i32::try_from(maturity_instant.seconds_since_unix_epoch / 60) + .unwrap(), + }, ), ) .unwrap(); diff --git a/tests/tests/ociswap_v1.rs b/tests/tests/ociswap_v1.rs index f03f9c45..cf2bc2f4 100644 --- a/tests/tests/ociswap_v1.rs +++ b/tests/tests/ociswap_v1.rs @@ -210,7 +210,9 @@ fn can_close_a_liquidity_position_in_ociswap_that_fits_into_fee_limits() { ModuleId::Main, ConsensusManagerField::ProposerMilliTimestamp.field_index(), ConsensusManagerProposerMilliTimestampFieldPayload::from_content_source( - ProposerMilliTimestampSubstate { epoch_milli: maturity_instant.seconds_since_unix_epoch * 1000 } + ProposerMilliTimestampSubstate { + epoch_milli: maturity_instant.seconds_since_unix_epoch * 1000, + }, ), ) .unwrap(); @@ -222,8 +224,9 @@ fn can_close_a_liquidity_position_in_ociswap_that_fits_into_fee_limits() { ConsensusManagerField::ProposerMinuteTimestamp.field_index(), ConsensusManagerProposerMinuteTimestampFieldPayload::from_content_source( ProposerMinuteTimestampSubstate { - epoch_minute: i32::try_from(maturity_instant.seconds_since_unix_epoch / 60).unwrap(), - } + epoch_minute: i32::try_from(maturity_instant.seconds_since_unix_epoch / 60) + .unwrap(), + }, ), ) .unwrap(); diff --git a/tests/tests/ociswap_v2.rs b/tests/tests/ociswap_v2.rs index 13aae66b..44224f4d 100644 --- a/tests/tests/ociswap_v2.rs +++ b/tests/tests/ociswap_v2.rs @@ -209,7 +209,9 @@ fn can_close_a_liquidity_position_in_ociswap_that_fits_into_fee_limits() { ModuleId::Main, ConsensusManagerField::ProposerMilliTimestamp.field_index(), ConsensusManagerProposerMilliTimestampFieldPayload::from_content_source( - ProposerMilliTimestampSubstate { epoch_milli: maturity_instant.seconds_since_unix_epoch * 1000 } + ProposerMilliTimestampSubstate { + epoch_milli: maturity_instant.seconds_since_unix_epoch * 1000, + }, ), ) .unwrap(); @@ -221,8 +223,9 @@ fn can_close_a_liquidity_position_in_ociswap_that_fits_into_fee_limits() { ConsensusManagerField::ProposerMinuteTimestamp.field_index(), ConsensusManagerProposerMinuteTimestampFieldPayload::from_content_source( ProposerMinuteTimestampSubstate { - epoch_minute: i32::try_from(maturity_instant.seconds_since_unix_epoch / 60).unwrap(), - } + epoch_minute: i32::try_from(maturity_instant.seconds_since_unix_epoch / 60) + .unwrap(), + }, ), ) .unwrap(); @@ -801,10 +804,8 @@ fn test_effect_of_price_action_on_fees(multiplier: i32) { ConsensusManagerField::ProposerMinuteTimestamp.field_index(), ConsensusManagerProposerMinuteTimestampFieldPayload::from_content_source( ProposerMinuteTimestampSubstate { - epoch_minute: i32::try_from( - maturity_instant.seconds_since_unix_epoch / 60, - ) - .unwrap(), + epoch_minute: i32::try_from(maturity_instant.seconds_since_unix_epoch / 60) + .unwrap(), }, ), ) diff --git a/tools/bootstrap/Cargo.toml b/tools/bootstrap/Cargo.toml deleted file mode 100644 index 084cc679..00000000 --- a/tools/bootstrap/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "bootstrap" -description = "A tool used to bootstrap project Ignition on various networks." -version.workspace = true -edition.workspace = true - -[dependencies] -sbor = { workspace = true } -transaction = { workspace = true } -radix-engine = { workspace = true } -radix-engine-interface = { workspace = true } - -common = { path = "../../libraries/common" } -ignition = { path = "../../packages/ignition" } -gateway-client = { path = "../../libraries/gateway-client" } -package-loader = { path = "../../libraries/package-loader", features = [ - "build-time-blueprints", -] } -ociswap-v1-adapter-v1 = { path = "../../packages/ociswap-v1-adapter-v1", features = [ - "manifest-builder-stubs", -] } -caviarnine-v1-adapter-v1 = { path = "../../packages/caviarnine-v1-adapter-v1", features = [ - "manifest-builder-stubs", -] } - -serde = { version = "1.0.196", features = ["derive"] } -serde_json = { version = "1.0.112" } - -hex = { version = "0.4.3", features = ["serde"] } -clap = { version = "4.4.18", features = ["derive"] } -rand = { version = "0.8.5" } - -[lints] -workspace = true \ No newline at end of file diff --git a/tools/bootstrap/src/cli.rs b/tools/bootstrap/src/cli.rs deleted file mode 100644 index 839ab556..00000000 --- a/tools/bootstrap/src/cli.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::error::*; -use crate::mainnet_testing; -use crate::stokenet_production; - -use clap::Parser; - -#[derive(Parser, Debug)] -pub enum Cli { - StokenetProduction(stokenet_production::StokenetProduction), - MainnetTesting(mainnet_testing::MainnetTesting), -} - -impl Cli { - pub fn run(self, out: &mut O) -> Result<(), Error> { - match self { - Self::StokenetProduction(cmd) => cmd.run(out), - Self::MainnetTesting(cmd) => cmd.run(out), - } - } -} diff --git a/tools/bootstrap/src/error.rs b/tools/bootstrap/src/error.rs deleted file mode 100644 index 4a80a1d1..00000000 --- a/tools/bootstrap/src/error.rs +++ /dev/null @@ -1,65 +0,0 @@ -use radix_engine::transaction::*; -use transaction::manifest::*; - -type TransactionPreviewError = gateway_client::apis::Error< - gateway_client::apis::transaction_api::TransactionPreviewError, ->; -type TransactionCommittedDetailsError = gateway_client::apis::Error< - gateway_client::apis::transaction_api::TransactionCommittedDetailsError, ->; -type TransactionSubmitError = gateway_client::apis::Error< - gateway_client::apis::transaction_api::TransactionSubmitError, ->; -type GatewayStatusError = gateway_client::apis::Error< - gateway_client::apis::status_api::GatewayStatusError, ->; - -#[derive(Debug)] -pub enum Error { - ManifestDecompilation(DecompileError), - TransactionPreviewError(TransactionPreviewError), - TransactionSubmitError(TransactionSubmitError), - TransactionCommittedDetailsError(TransactionCommittedDetailsError), - GatewayStatusError(GatewayStatusError), - PreviewFailed { - manifest: String, - receipt: TransactionReceiptV1, - }, - TransactionPollingYieldedNothing { - intent_hash: String, - }, - TransactionWasNotSuccessful { - intent_hash: String, - }, - FailedToLoadPrivateKey, -} - -impl From for Error { - fn from(value: DecompileError) -> Self { - Self::ManifestDecompilation(value) - } -} - -impl From for Error { - fn from(value: TransactionPreviewError) -> Self { - Self::TransactionPreviewError(value) - } -} - -impl From for Error { - fn from(value: TransactionSubmitError) -> Self { - Self::TransactionSubmitError(value) - } -} - -impl From for Error { - fn from(value: TransactionCommittedDetailsError) -> Self { - Self::TransactionCommittedDetailsError(value) - } -} - -impl From for Error { - fn from(value: GatewayStatusError) -> Self { - Self::GatewayStatusError(value) - } -} diff --git a/tools/bootstrap/src/main.rs b/tools/bootstrap/src/main.rs deleted file mode 100644 index f4da8342..00000000 --- a/tools/bootstrap/src/main.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![allow(dead_code, clippy::enum_variant_names)] - -#[macro_use] -mod macros; -mod cli; -mod error; -mod mainnet_testing; -mod stokenet_production; -mod transaction_service; -mod types; - -fn main() -> Result<(), error::Error> { - let mut out = std::io::stdout(); - let cli = ::parse(); - cli.run(&mut out) -} diff --git a/tools/bootstrap/src/mainnet_testing.rs b/tools/bootstrap/src/mainnet_testing.rs deleted file mode 100644 index 50d50445..00000000 --- a/tools/bootstrap/src/mainnet_testing.rs +++ /dev/null @@ -1,666 +0,0 @@ -use crate::error::*; -use crate::transaction_service::*; -use crate::types::*; -use crate::*; -use clap::Parser; -use common::prelude::*; -use ignition::{InitializationParametersManifest, PoolBlueprintInformation}; -use package_loader::PackageLoader; -use radix_engine_interface::api::node_modules::auth::*; -use radix_engine_interface::api::node_modules::*; -use radix_engine_interface::blueprints::account::*; -use radix_engine_interface::prelude::*; -use transaction::prelude::*; - -const PRIVATE_KEY_ENVIRONMENT_VARIABLE: &str = "PRIVATE_KEY"; - -#[derive(Parser, Debug)] -pub struct MainnetTesting {} - -impl MainnetTesting { - pub fn run(self, _: &mut O) -> Result<(), Error> { - // Loading the private key that will notarize and pay the fees of the - // transaction. - let notary_private_key = { - std::env::var(PRIVATE_KEY_ENVIRONMENT_VARIABLE) - .map_err(|_| Error::FailedToLoadPrivateKey) - .and_then(|hex| { - hex::decode(hex).map_err(|_| Error::FailedToLoadPrivateKey) - }) - .and_then(|bytes| { - Ed25519PrivateKey::from_bytes(&bytes) - .map_err(|_| Error::FailedToLoadPrivateKey) - }) - .map(PrivateKey::Ed25519) - }?; - let notary_account = ComponentAddress::virtual_account_from_public_key( - ¬ary_private_key.public_key(), - ); - let fee_handling = FeeHandling::EstimateAndLock { - fee_payer_account: notary_account, - fee_payer_private_key: ¬ary_private_key, - }; - - // Initializing all of the data that this command will use. These are - // pretty much constants but we can't make them constants because most - // of the functions are not `const`. There is also not really a point - // in making them a lazy static, let's keep things simple. - - /* cSpell:disable - Sorry for this, I dislike it too. */ - const GATEWAY_API_BASE_URL: &str = "https://mainnet.radixdlt.com/"; - let network_definition = NetworkDefinition::mainnet(); - let bech32m_coders = - Bech32mCoders::from_network_definition(&network_definition); - - // TODO: What do we want these values to be? - const MAXIMUM_ALLOWED_PRICE_STALENESS_IN_SECONDS: i64 = 60; // 60 seconds - const MAXIMUM_ALLOWED_PRICE_DIFFERENCE_PERCENTAGE: Decimal = - Decimal::MAX; // TODO: No oracle is deployed on mainnet for testing yet. - - let protocol_resource = resource_address!("resource_rdx1t4dekrf58h0r28s3c93z92w3jt5ngx87jzd63mgc597zmf3534rxfv"); - let resources = NameIndexedResourceInformation { - bitcoin: resource_address!("resource_rdx1t58dla7ykxzxe5es89wlhgzatqla0gceukg0eeduzvtj4cxd55etn8"), - ethereum: resource_address!("resource_rdx1tkscrlztcyn82ej5z3n232f0qqp0qur69arjf279ppmg5usa3xhnsm"), - usdc: resource_address!("resource_rdx1th7nx2hy0cf6aea6mz7zhkdmy4p45s488xutltnp7296zxj8hwchpf"), - usdt: resource_address!("resource_rdx1tkafx32lu72mcxr85gjx0rh3rx9q89zqffg4phmv5rxdqg5fnd0w7s"), - }; - let exchanges = NameIndexedDexInformation { - caviarnine_v1: DexInformation { - package: package_address!("package_rdx1p4r9rkp0cq67wmlve544zgy0l45mswn6h798qdqm47x4762h383wa3"), - pools: NameIndexedResourceInformation { - bitcoin: component_address!("component_rdx1crzl2c39m83lpe6fv62epgp3phqunxhc264ys23qz8xeemjcu8lln3"), - ethereum: component_address!("component_rdx1cqk2ufmdq6pkcu7ed7r6u9hmdsht9gyd8y8wwtd7w5znefz9k54a7d"), - usdc: component_address!("component_rdx1cq9q8umlpmngff6y4e534htz0n37te4m7vsj50u9zc58ys65zl6jv9"), - usdt: component_address!("component_rdx1cpl0v3lndt9d7g7uuepztxs9m7m24ly0yfhvcum2y7tm0vlzst0l5y") - } - }, - // TODO: Ths following is INCORRECT INFORMATION! There is no Ociswap - // package on mainnet. - ociswap_v1: DexInformation { - package: package_address!("package_rdx1p5l6dp3slnh9ycd7gk700czwlck9tujn0zpdnd0efw09n2zdnn0lzx"), - pools: NameIndexedResourceInformation { - bitcoin: component_address!("component_rdx1cr5uxxjq4a0r3gfn6yd62lk96fqca34tnmyqdxkwefhckcjea4t3am"), - ethereum: component_address!("component_rdx1cqylpcl8p45l2h5ew0qrkwyz23dky3e6ucs7kkhrtm90k9z3kzeztn"), - usdc: component_address!("component_rdx1cq96chge0q6kkk962heg0mgfl82gjw7x25dp9jv80gkx90mc3hk2ua"), - usdt: component_address!("component_rdx1cz3fa8qtfgfwjt3fzrtm544a89p5laerww7590g2tfcradqwdv3laq") - } - }, - }; - // TODO: Numbers here are not real and I have added from just to get - // things going. MUST modify before launch. - let reward_information = indexmap! { - LockupPeriod::from_minutes(0).unwrap() => dec!(0.125), // 12.5% - LockupPeriod::from_minutes(1).unwrap() => dec!(0.15), // 15.0% - }; - - // TODO: MUST determine what those accounts are prior to launch! - // For now, for the TEST deployments these are accounts that I CONTROL! - let protocol_manager_account = component_address!("account_rdx12xvk6x3usuzu7hdc5clc7lpu8e4czze6xa7vrw7vlek0h84j9299na"); - let protocol_owner_account = component_address!("account_rdx12xvk6x3usuzu7hdc5clc7lpu8e4czze6xa7vrw7vlek0h84j9299na"); - - /* cSpell:enable */ - - // An ephemeral private key that we will use the bootstrapping process. - // This key will initially control the dApp definition to allow us to - // easily update the metadata and will later on change the owner role - // of the dApp definition to the protocol owner. - - let ephemeral_private_key = - Ed25519PrivateKey::from_u64(rand::random()).unwrap(); - println!( - "Ephemeral Private Key: {:?}", - ephemeral_private_key.to_bytes() - ); - - let ephemeral_private_key = PrivateKey::Ed25519( - Ed25519PrivateKey::from_u64(rand::random()).unwrap(), - ); - let ephemeral_virtual_signature_badge = - NonFungibleGlobalId::from_public_key( - &ephemeral_private_key.public_key(), - ); - - // This is the transaction service that the submission will happen - // through. It does most of the heavy lifting associated with the - // transaction submission. - let transaction_service = - TransactionService::new(&bech32m_coders, GATEWAY_API_BASE_URL); - - // Creating the dApp definition account. When this account starts it - // its owner will be a virtual signature badge which will change once - // add all of the metadata fields that we want to add. The the manifest - // that involves the dApp definition will set the metadata on it and - // will also change its owner to be the protocol Owner badge. - let dapp_definition_account = { - let manifest = ManifestBuilder::new() - .call_function( - ACCOUNT_PACKAGE, - ACCOUNT_BLUEPRINT, - ACCOUNT_CREATE_ADVANCED_IDENT, - AccountCreateAdvancedManifestInput { - owner_role: OwnerRole::Updatable(rule!(require( - ephemeral_virtual_signature_badge - ))), - address_reservation: None, - }, - ) - .build(); - std::thread::sleep(std::time::Duration::from_secs(5)); - *transaction_service - .submit_manifest(manifest, ¬ary_private_key, &fee_handling)? - .new_component_addresses - .first() - .expect("Must succeed!") - }; - - // Creating the protocol owner and the protocol manager badges and - // sending them off to the accounts specified up above. - let (protocol_manager_badge, protocol_owner_badge) = { - let manifest = ManifestBuilder::new() - // The protocol manager badge - .create_fungible_resource( - OwnerRole::None, - true, - 0, - Default::default(), - // TODO: What do we want those to be? Any preference? - metadata! { - init { - "name" => "Ignition Protocol Manager", updatable; - "symbol" => "IGNPM", updatable; - "description" => "A badge that gives the authority to manage the Ignition protocol.", updatable; - "badge" => vec!["badge"], updatable; - "dapp_definitions" => vec![dapp_definition_account], updatable; - } - }, - Some(dec!(1)), - ) - .try_deposit_entire_worktop_or_abort(protocol_manager_account, None) - // The protocol owner badge - .create_fungible_resource( - OwnerRole::None, - true, - 0, - Default::default(), - metadata! { - init { - "name" => "Ignition Protocol Owner", updatable; - "symbol" => "IGNPO", updatable; - "description" => "A badge that of the owner of the ignition protocol.", updatable; - "badge" => vec!["badge"], updatable; - "dapp_definitions" => vec![dapp_definition_account], updatable; - } - }, - Some(dec!(1)), - ) - .try_deposit_entire_worktop_or_abort(protocol_owner_account, None) - .build(); - - std::thread::sleep(std::time::Duration::from_secs(5)); - let resource_addresses = transaction_service - .submit_manifest(manifest, ¬ary_private_key, &fee_handling)? - .new_resource_addresses; - ( - *resource_addresses.first().unwrap(), - *resource_addresses.get(1).unwrap(), - ) - }; - - let protocol_manager_rule = rule!(require(protocol_manager_badge)); - let protocol_owner_rule = rule!(require(protocol_owner_badge)); - let owner_role = OwnerRole::Fixed(protocol_owner_rule.clone()); - - // Publishing the packages. - let ( - ignition_package_address, - simple_oracle_package_address, - ociswap_v1_adapter_v1_package_address, - caviarnine_v1_adapter_v1_package_address, - ) = { - let (ignition_code, ignition_package_definition) = - PackageLoader::get("ignition"); - let (simple_oracle_code, simple_oracle_package_definition) = - PackageLoader::get("simple-oracle"); - let ( - ociswap_v1_adapter_v1_code, - ociswap_v1_adapter_v1_package_definition, - ) = PackageLoader::get("ociswap-v1-adapter-v1"); - let ( - caviarnine_v1_adapter_v1_code, - caviarnine_v1_adapter_v1_package_definition, - ) = PackageLoader::get("caviarnine-v1-adapter-v1"); - - // We can publish the simple oracle, ociswap adapter v1, and - // caviarnine adapter v1 all in a single transaction since they - // are below the size limit. - let manifest = ManifestBuilder::new() - .publish_package_advanced( - None, - simple_oracle_code, - simple_oracle_package_definition, - metadata_init! { - "name" => "Simple Oracle Package", updatable; - "description" => "The implementation of the Oracle used by the Ignition protocol.", updatable; - "tags" => vec!["oracle"], updatable; - "dapp_definition" => dapp_definition_account, updatable; - }, - owner_role.clone(), - ) - .publish_package_advanced( - None, - ociswap_v1_adapter_v1_code, - ociswap_v1_adapter_v1_package_definition, - metadata_init! { - "name" => "Ociswap Adapter v1 Package", updatable; - "description" => "The implementation of an adapter for Ociswap for the Ignition protocol.", updatable; - "tags" => vec!["adapter"], updatable; - "dapp_definition" => dapp_definition_account, updatable; - }, - owner_role.clone(), - ) - .publish_package_advanced( - None, - caviarnine_v1_adapter_v1_code, - caviarnine_v1_adapter_v1_package_definition, - metadata_init! { - "name" => "Caviarnine Adapter v1 Package", updatable; - "description" => "The implementation of an adapter for Caviarnine for the Ignition protocol.", updatable; - "tags" => vec!["adapter"], updatable; - "dapp_definition" => dapp_definition_account, updatable; - }, - owner_role.clone(), - ).build(); - - std::thread::sleep(std::time::Duration::from_secs(5)); - let package_addresses = transaction_service - .submit_manifest(manifest, ¬ary_private_key, &fee_handling)? - .new_package_addresses; - - let ( - simple_oracle_package_address, - ociswap_v1_adapter_v1_package_address, - caviarnine_v1_adapter_v1_package_address, - ) = ( - *package_addresses.first().unwrap(), - *package_addresses.get(1).unwrap(), - *package_addresses.get(2).unwrap(), - ); - - // Publishing the Ignition package - let manifest = ManifestBuilder::new() - .publish_package_advanced( - None, - ignition_code, - ignition_package_definition, - metadata_init! { - "name" => "Ignition Package", updatable; - "description" => "The implementation of the Ignition protocol.", updatable; - "tags" => Vec::::new(), updatable; - "dapp_definition" => dapp_definition_account, updatable; - }, - owner_role.clone(), - ) - .build(); - std::thread::sleep(std::time::Duration::from_secs(5)); - let ignition_package_address = *transaction_service - .submit_manifest(manifest, ¬ary_private_key, &fee_handling)? - .new_package_addresses - .first() - .unwrap(); - - ( - ignition_package_address, - simple_oracle_package_address, - ociswap_v1_adapter_v1_package_address, - caviarnine_v1_adapter_v1_package_address, - ) - }; - - // Creating the different liquidity receipt resources that the different - // exchanges will use. They will be mintable and burnable through the - // Ignition package caller badge. - let ignition_package_global_caller_rule = - rule!(require(package_of_direct_caller(ignition_package_address))); - let ( - ociswap_v1_liquidity_receipt_resource, - caviarnine_v1_liquidity_receipt_resource, - ) = { - let roles = NonFungibleResourceRoles { - // Mintable and burnable by the Ignition package and - // the protocol owner can update who can do that. - mint_roles: mint_roles! { - minter => ignition_package_global_caller_rule.clone(); - minter_updater => protocol_owner_rule.clone(); - }, - burn_roles: burn_roles! { - burner => ignition_package_global_caller_rule.clone(); - burner_updater => protocol_owner_rule.clone(); - }, - // We reserve the right to change the data of the - // liquidity receipts when we want. - non_fungible_data_update_roles: non_fungible_data_update_roles! { - non_fungible_data_updater => rule!(deny_all); - non_fungible_data_updater_updater => protocol_owner_rule.clone(); - }, - // Everything else is deny all and can't be changed. - recall_roles: recall_roles! { - recaller => rule!(deny_all); - recaller_updater => rule!(deny_all); - }, - freeze_roles: freeze_roles! { - freezer => rule!(deny_all); - freezer_updater => rule!(deny_all); - }, - deposit_roles: deposit_roles! { - depositor => rule!(allow_all); - depositor_updater => rule!(deny_all); - }, - withdraw_roles: withdraw_roles! { - withdrawer => rule!(allow_all); - withdrawer_updater => rule!(deny_all); - }, - }; - - let manifest = ManifestBuilder::new() - // Ociswap liquidity receipt - .call_function( - RESOURCE_PACKAGE, - NON_FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT, - NON_FUNGIBLE_RESOURCE_MANAGER_CREATE_RUID_WITH_INITIAL_SUPPLY_IDENT, - NonFungibleResourceManagerCreateRuidWithInitialSupplyManifestInput { - owner_role: owner_role.clone(), - track_total_supply: true, - non_fungible_schema: NonFungibleDataSchema::new_local_without_self_package_replacement::>(), - entries: Vec::new(), - resource_roles: roles.clone(), - metadata: metadata! { - roles { - metadata_setter => protocol_owner_rule.clone(); - metadata_setter_updater => protocol_owner_rule.clone(); - metadata_locker => protocol_owner_rule.clone(); - metadata_locker_updater => protocol_owner_rule.clone(); - }, - init { - // TODO: Confirm with the exchanges what they - // want their name to be. - "name" => "Ignition LP: Ociswap", updatable; - "description" => "Represents a particular contribution of liquidity to Ociswap through the Ignition liquidity incentives program. See the redeem_url metadata for where to redeem these NFTs.", updatable; - "tags" => vec!["lp token"], updatable; - "dapp_definitions" => vec![dapp_definition_account], updatable; - // TODO: Must get this from our design team - "icon_url" => UncheckedUrl::of("https://www.google.com"), updatable; - "DEX" => "Ociswap", updatable; - // TODO: Must get this from Ociswap! - "redeem_url" => UncheckedUrl::of("https://www.google.com"), updatable; - } - }, - address_reservation: None - } - ) - // Caviarnine liquidity receipt - .call_function( - RESOURCE_PACKAGE, - NON_FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT, - NON_FUNGIBLE_RESOURCE_MANAGER_CREATE_RUID_WITH_INITIAL_SUPPLY_IDENT, - NonFungibleResourceManagerCreateRuidWithInitialSupplyManifestInput { - owner_role: owner_role.clone(), - track_total_supply: true, - non_fungible_schema: NonFungibleDataSchema::new_local_without_self_package_replacement::>(), - entries: Vec::new(), - resource_roles: roles.clone(), - metadata: metadata! { - roles { - metadata_setter => protocol_owner_rule.clone(); - metadata_setter_updater => protocol_owner_rule.clone(); - metadata_locker => protocol_owner_rule.clone(); - metadata_locker_updater => protocol_owner_rule.clone(); - }, - init { - // TODO: Confirm with the exchanges what they want - // their name to be. - "name" => "Ignition LP: Caviarnine", updatable; - "description" => "Represents a particular contribution of liquidity to Caviarnine through the Ignition liquidity incentives program. See the redeem_url metadata for where to redeem these NFTs.", updatable; - "tags" => vec!["lp token"], updatable; - "dapp_definitions" => vec![dapp_definition_account], updatable; - // TODO: Must get this from our design team - "icon_url" => UncheckedUrl::of("https://www.google.com"), updatable; - "DEX" => "Caviarnine", updatable; - // TODO: Must get this from Caviarnine! - "redeem_url" => UncheckedUrl::of("https://www.google.com"), updatable; - } - }, - address_reservation: None - } - ) - .build(); - - std::thread::sleep(std::time::Duration::from_secs(5)); - let resource_addresses = transaction_service - .submit_manifest(manifest, ¬ary_private_key, &fee_handling)? - .new_resource_addresses; - ( - *resource_addresses.first().unwrap(), - *resource_addresses.get(1).unwrap(), - ) - }; - - // Creating the oracle and adapters. - let ( - ignition_component, - simple_oracle_component, - ociswap_v1_adapter_v1_component, - caviarnine_v1_adapter_v1_component, - ) = { - let manifest = ManifestBuilder::new() - // Creating the oracle component - .call_function( - simple_oracle_package_address, - "SimpleOracle", - "instantiate", - ( - protocol_manager_rule.clone(), - metadata_init! { - "name" => "Ignition Oracle", updatable; - "description" => "The oracle used by the Ignition protocol.", updatable; - "tags" => vec!["oracle"], updatable; - "dapp_definition" => dapp_definition_account, updatable; - }, - owner_role.clone(), - None::, - ), - ) - // Creating the ociswap adapter v1 component - .call_function( - ociswap_v1_adapter_v1_package_address, - "OciswapV1Adapter", - "instantiate", - ( - metadata_init! { - "name" => "Ignition Ociswap Adapter", updatable; - "description" => "The adapter used by the Ignition protocol to communicate with Ociswap pools.", updatable; - "dapp_definition" => dapp_definition_account, updatable; - }, - owner_role.clone(), - None::, - ), - ) - // Creating the ociswap adapter v1 component - .call_function( - caviarnine_v1_adapter_v1_package_address, - "CaviarnineV1Adapter", - "instantiate", - ( - metadata_init! { - "name" => "Ignition Caviarnine Adapter", updatable; - "description" => "The adapter used by the Ignition protocol to communicate with Caviarnine pools.", updatable; - "dapp_definition" => dapp_definition_account, updatable; - }, - owner_role.clone(), - None::, - ), - ) - .build(); - - std::thread::sleep(std::time::Duration::from_secs(5)); - let component_addresses = transaction_service - .submit_manifest(manifest, ¬ary_private_key, &fee_handling)? - .new_component_addresses; - - let ( - simple_oracle_component, - ociswap_v1_adapter_v1_component, - caviarnine_v1_adapter_v1_component, - ) = ( - *component_addresses.first().unwrap(), - *component_addresses.get(1).unwrap(), - *component_addresses.get(2).unwrap(), - ); - - // Instantiating the Ignition component - let manifest = ManifestBuilder::new() - // Instantiate Ignition. - .call_function( - ignition_package_address, - "Ignition", - "instantiate", - manifest_args!( - metadata_init! { - "name" => "Ignition", updatable; - "description" => "The Ignition protocol component", updatable; - "dapp_definition" => dapp_definition_account, updatable; - }, - owner_role.clone(), - protocol_owner_rule.clone(), - protocol_manager_rule.clone(), - protocol_resource, - simple_oracle_component, - MAXIMUM_ALLOWED_PRICE_STALENESS_IN_SECONDS, - MAXIMUM_ALLOWED_PRICE_DIFFERENCE_PERCENTAGE, - InitializationParametersManifest { - initial_pool_information: Some(indexmap! { - BlueprintId { - package_address: exchanges.caviarnine_v1.package, - blueprint_name: "QuantaSwap".to_owned() - } => PoolBlueprintInformation { - adapter: caviarnine_v1_adapter_v1_component, - allowed_pools: exchanges.caviarnine_v1.pools.into_iter().collect(), - liquidity_receipt: caviarnine_v1_liquidity_receipt_resource - }, - BlueprintId { - package_address: exchanges.ociswap_v1.package, - blueprint_name: "BasicPool".to_owned() - } => PoolBlueprintInformation { - adapter: ociswap_v1_adapter_v1_component, - allowed_pools: exchanges.ociswap_v1.pools.into_iter().collect(), - liquidity_receipt: ociswap_v1_liquidity_receipt_resource - } - }), - initial_user_resource_volatility: Some( - indexmap! { - resources.bitcoin => Volatility::Volatile, - resources.ethereum => Volatility::Volatile, - resources.usdc => Volatility::NonVolatile, - resources.usdt => Volatility::NonVolatile, - } - ), - initial_reward_rates: Some(reward_information), - initial_volatile_protocol_resources: None, - initial_non_volatile_protocol_resources: None, - initial_is_open_position_enabled: Some(true), - initial_is_close_position_enabled: Some(true), - }, - None:: - ) - ) - .build(); - - std::thread::sleep(std::time::Duration::from_secs(5)); - let component_addresses = transaction_service - .submit_manifest( - manifest, - &ephemeral_private_key, - &fee_handling, - )? - .new_component_addresses; - - let ignition_component_address = - *component_addresses.first().unwrap(); - - ( - ignition_component_address, - simple_oracle_component, - ociswap_v1_adapter_v1_component, - caviarnine_v1_adapter_v1_component, - ) - }; - - // Updating the dapp definition account with the metadata that it - // should have. - { - let manifest = ManifestBuilder::new() - .set_metadata( - dapp_definition_account, - "account_type", - "dapp definition", - ) - .set_metadata( - dapp_definition_account, - "claimed_websites", - Vec::::new(), - ) - .set_metadata( - dapp_definition_account, - "dapp_definitions", - Vec::::new(), - ) - .set_metadata( - dapp_definition_account, - "claimed_entities", - vec![ - GlobalAddress::from(protocol_manager_badge), - GlobalAddress::from(protocol_owner_badge), - GlobalAddress::from(ignition_package_address), - GlobalAddress::from(simple_oracle_package_address), - GlobalAddress::from( - ociswap_v1_adapter_v1_package_address, - ), - GlobalAddress::from( - caviarnine_v1_adapter_v1_package_address, - ), - GlobalAddress::from( - ociswap_v1_liquidity_receipt_resource, - ), - GlobalAddress::from( - caviarnine_v1_liquidity_receipt_resource, - ), - GlobalAddress::from(ignition_component), - GlobalAddress::from(simple_oracle_component), - GlobalAddress::from(ociswap_v1_adapter_v1_component), - GlobalAddress::from(caviarnine_v1_adapter_v1_component), - ], - ) - .call_role_assignment_method( - dapp_definition_account, - ROLE_ASSIGNMENT_SET_OWNER_IDENT, - RoleAssignmentSetOwnerInput { - rule: protocol_owner_rule, - }, - ) - .build(); - - std::thread::sleep(std::time::Duration::from_secs(5)); - transaction_service.submit_manifest( - manifest, - &ephemeral_private_key, - &fee_handling, - )?; - } - - Ok(()) - } -} - -pub struct DexInformation { - pub pools: NameIndexedResourceInformation, - pub package: PackageAddress, -} diff --git a/tools/bootstrap/src/stokenet_production.rs b/tools/bootstrap/src/stokenet_production.rs deleted file mode 100644 index 03765762..00000000 --- a/tools/bootstrap/src/stokenet_production.rs +++ /dev/null @@ -1,662 +0,0 @@ -use crate::error::*; -use crate::transaction_service::*; -use crate::types::*; -use crate::*; -use clap::Parser; -use common::prelude::*; -use ignition::{InitializationParametersManifest, PoolBlueprintInformation}; -use package_loader::PackageLoader; -use radix_engine_interface::api::node_modules::auth::*; -use radix_engine_interface::api::node_modules::*; -use radix_engine_interface::blueprints::account::*; -use radix_engine_interface::prelude::*; -use transaction::prelude::*; - -const PRIVATE_KEY_ENVIRONMENT_VARIABLE: &str = "PRIVATE_KEY"; - -#[derive(Parser, Debug)] -pub struct StokenetProduction {} - -impl StokenetProduction { - pub fn run(self, _: &mut O) -> Result<(), Error> { - // Loading the private key that will notarize and pay the fees of the - // transaction. - let notary_private_key = { - std::env::var(PRIVATE_KEY_ENVIRONMENT_VARIABLE) - .map_err(|_| Error::FailedToLoadPrivateKey) - .and_then(|hex| { - hex::decode(hex).map_err(|_| Error::FailedToLoadPrivateKey) - }) - .and_then(|bytes| { - Ed25519PrivateKey::from_bytes(&bytes) - .map_err(|_| Error::FailedToLoadPrivateKey) - }) - .map(PrivateKey::Ed25519) - }?; - let notary_account = ComponentAddress::virtual_account_from_public_key( - ¬ary_private_key.public_key(), - ); - let fee_handling = FeeHandling::EstimateAndLock { - fee_payer_account: notary_account, - fee_payer_private_key: ¬ary_private_key, - }; - - // Initializing all of the data that this command will use. These are - // pretty much constants but we can't make them constants because most - // of the functions are not `const`. There is also not really a point - // in making them a lazy static, let's keep things simple. - - /* cSpell:disable - Sorry for this, I dislike it too. */ - const GATEWAY_API_BASE_URL: &str = "https://stokenet.radixdlt.com/"; - let network_definition = NetworkDefinition::stokenet(); - let bech32m_coders = - Bech32mCoders::from_network_definition(&network_definition); - - // TODO: What do we want these values to be? - const MAXIMUM_ALLOWED_PRICE_STALENESS_IN_SECONDS: i64 = 60; // 60 seconds - const MAXIMUM_ALLOWED_PRICE_DIFFERENCE_PERCENTAGE: Decimal = dec!(0.05); // 5 % - - let protocol_resource = resource_address!("resource_tdx_2_1thwmtk9qet08y3wpujd8nddmvjuqyptg5nt0mw0zcdgcrahu5k36qx"); - let resources = NameIndexedResourceInformation { - bitcoin: resource_address!("resource_tdx_2_1thltk578jr4v7axqpu5ceznhlha6ca2qtzcflqdmytgtf37xncu7l9"), - ethereum: resource_address!("resource_tdx_2_1t59gx963vzd6u6fz63h5de2zh9nmgwxc8y832edmr6pxvz98wg6zu3"), - usdc: resource_address!("resource_tdx_2_1thfv477eqwlh8x4wt6xsc62myt4z0zxmdpr4ea74fa8jnxh243y60r"), - usdt: resource_address!("resource_tdx_2_1t4p3ytx933n576pdps4ua7jkjh36zrh36a543u0tfcsu2vthavlqg8"), - }; - let exchanges = NameIndexedDexInformation { - caviarnine_v1: DexInformation { - package: package_address!("package_tdx_2_1p57g523zj736u370z6g4ynrytn7t6r2hledvzkhl6tzpg3urn0707e"), - pools: NameIndexedResourceInformation { - bitcoin: component_address!("component_tdx_2_1czt59vxdqg7q4l0gzphmt5ev6lagl2cu6sm2hsaz9y8ypcf0aukf8r"), - ethereum: component_address!("component_tdx_2_1crqpgnpf3smh7kg8d4sz4h3502l65s4tslwhg46ru07ra6l30pcsj4"), - usdc: component_address!("component_tdx_2_1cpwkf9uhel3ut4ydm58g0uyaw7sxckmp2pz7sdv79vzt9y3p7ad4fu"), - usdt: component_address!("component_tdx_2_1czmdhtq0u8f40khky4c6j74msskuz60yq3y0zewu85phrdj0ryz2hl") - } - }, - // TODO: Ths following is INCORRECT INFORMATION! There is no Ociswap - // package on Stokenet. - ociswap_v1: DexInformation { - package: package_address!("package_tdx_2_1p40dekel26tp2a2srma4sc3lj2ukr6y8k4amr7x8yav86lyyeg7ta7"), - pools: NameIndexedResourceInformation { - bitcoin: component_address!("component_tdx_2_1czt59vxdqg7q4l0gzphmt5ev6lagl2cu6sm2hsaz9y8ypcf0aukf8r"), - ethereum: component_address!("component_tdx_2_1crqpgnpf3smh7kg8d4sz4h3502l65s4tslwhg46ru07ra6l30pcsj4"), - usdc: component_address!("component_tdx_2_1cpwkf9uhel3ut4ydm58g0uyaw7sxckmp2pz7sdv79vzt9y3p7ad4fu"), - usdt: component_address!("component_tdx_2_1czmdhtq0u8f40khky4c6j74msskuz60yq3y0zewu85phrdj0ryz2hl") - } - }, - }; - // TODO: Numbers here are not real and I have added from just to get - // things going. MUST modify before launch. - let reward_information = indexmap! { - LockupPeriod::from_months(9).unwrap() => dec!(0.125), // 12.5% - LockupPeriod::from_months(10).unwrap() => dec!(0.15), // 15.0% - LockupPeriod::from_months(11).unwrap() => dec!(0.175), // 17.5% - LockupPeriod::from_months(12).unwrap() => dec!(0.20), // 20.0% - }; - - // TODO: MUST determine what those accounts are prior to launch! - // For now they are MY stokenet accounts! - let protocol_manager_account = component_address!("account_tdx_2_12xxuglkrdgcphpqk34fv59ewq3gu5uwlzs42hpy0grsrefvgwgxrev"); - let protocol_owner_account = component_address!("account_tdx_2_12xxuglkrdgcphpqk34fv59ewq3gu5uwlzs42hpy0grsrefvgwgxrev"); - - /* cSpell:enable */ - - // An ephemeral private key that we will use the bootstrapping process. - // This key will initially control the dApp definition to allow us to - // easily update the metadata and will later on change the owner role - // of the dApp definition to the protocol owner. - let ephemeral_private_key = PrivateKey::Ed25519( - Ed25519PrivateKey::from_u64(rand::random()).unwrap(), - ); - let ephemeral_virtual_signature_badge = - NonFungibleGlobalId::from_public_key( - &ephemeral_private_key.public_key(), - ); - - // This is the transaction service that the submission will happen - // through. It does most of the heavy lifting associated with the - // transaction submission. - let transaction_service = - TransactionService::new(&bech32m_coders, GATEWAY_API_BASE_URL); - - // Creating the dApp definition account. When this account starts it - // its owner will be a virtual signature badge which will change once - // add all of the metadata fields that we want to add. The the manifest - // that involves the dApp definition will set the metadata on it and - // will also change its owner to be the protocol Owner badge. - let dapp_definition_account = { - let manifest = ManifestBuilder::new() - .call_function( - ACCOUNT_PACKAGE, - ACCOUNT_BLUEPRINT, - ACCOUNT_CREATE_ADVANCED_IDENT, - AccountCreateAdvancedManifestInput { - owner_role: OwnerRole::Updatable(rule!(require( - ephemeral_virtual_signature_badge - ))), - address_reservation: None, - }, - ) - .build(); - std::thread::sleep(std::time::Duration::from_secs(5)); - *transaction_service - .submit_manifest(manifest, ¬ary_private_key, &fee_handling)? - .new_component_addresses - .first() - .expect("Must succeed!") - }; - - // Creating the protocol owner and the protocol manager badges and - // sending them off to the accounts specified up above. - let (protocol_manager_badge, protocol_owner_badge) = { - let manifest = ManifestBuilder::new() - // The protocol manager badge - .create_fungible_resource( - OwnerRole::None, - true, - 0, - Default::default(), - // TODO: What do we want those to be? Any preference? - metadata! { - init { - "name" => "Ignition Protocol Manager", updatable; - "symbol" => "IGNPM", updatable; - "description" => "A badge that gives the authority to manage the Ignition protocol.", updatable; - "badge" => vec!["badge"], updatable; - "dapp_definitions" => vec![dapp_definition_account], updatable; - } - }, - Some(dec!(1)), - ) - .try_deposit_entire_worktop_or_abort(protocol_manager_account, None) - // The protocol owner badge - .create_fungible_resource( - OwnerRole::None, - true, - 0, - Default::default(), - metadata! { - init { - "name" => "Ignition Protocol Owner", updatable; - "symbol" => "IGNPO", updatable; - "description" => "A badge that of the owner of the ignition protocol.", updatable; - "badge" => vec!["badge"], updatable; - "dapp_definitions" => vec![dapp_definition_account], updatable; - } - }, - Some(dec!(1)), - ) - .try_deposit_entire_worktop_or_abort(protocol_owner_account, None) - .build(); - - std::thread::sleep(std::time::Duration::from_secs(5)); - let resource_addresses = transaction_service - .submit_manifest(manifest, ¬ary_private_key, &fee_handling)? - .new_resource_addresses; - ( - *resource_addresses.first().unwrap(), - *resource_addresses.get(1).unwrap(), - ) - }; - - let protocol_manager_rule = rule!(require(protocol_manager_badge)); - let protocol_owner_rule = rule!(require(protocol_owner_badge)); - let owner_role = OwnerRole::Fixed(protocol_owner_rule.clone()); - - // Publishing the packages. - let ( - ignition_package_address, - simple_oracle_package_address, - ociswap_v1_adapter_v1_package_address, - caviarnine_v1_adapter_v1_package_address, - ) = { - let (ignition_code, ignition_package_definition) = - PackageLoader::get("ignition"); - let (simple_oracle_code, simple_oracle_package_definition) = - PackageLoader::get("simple-oracle"); - let ( - ociswap_v1_adapter_v1_code, - ociswap_v1_adapter_v1_package_definition, - ) = PackageLoader::get("ociswap-v1-adapter-v1"); - let ( - caviarnine_v1_adapter_v1_code, - caviarnine_v1_adapter_v1_package_definition, - ) = PackageLoader::get("caviarnine-v1-adapter-v1"); - - // We can publish the simple oracle, ociswap adapter v1, and - // caviarnine adapter v1 all in a single transaction since they - // are below the size limit. - let manifest = ManifestBuilder::new() - .publish_package_advanced( - None, - simple_oracle_code, - simple_oracle_package_definition, - metadata_init! { - "name" => "Simple Oracle Package", updatable; - "description" => "The implementation of the Oracle used by the Ignition protocol.", updatable; - "tags" => vec!["oracle"], updatable; - "dapp_definition" => dapp_definition_account, updatable; - }, - owner_role.clone(), - ) - .publish_package_advanced( - None, - ociswap_v1_adapter_v1_code, - ociswap_v1_adapter_v1_package_definition, - metadata_init! { - "name" => "Ociswap Adapter v1 Package", updatable; - "description" => "The implementation of an adapter for Ociswap for the Ignition protocol.", updatable; - "tags" => vec!["adapter"], updatable; - "dapp_definition" => dapp_definition_account, updatable; - }, - owner_role.clone(), - ) - .publish_package_advanced( - None, - caviarnine_v1_adapter_v1_code, - caviarnine_v1_adapter_v1_package_definition, - metadata_init! { - "name" => "Caviarnine Adapter v1 Package", updatable; - "description" => "The implementation of an adapter for Caviarnine for the Ignition protocol.", updatable; - "tags" => vec!["adapter"], updatable; - "dapp_definition" => dapp_definition_account, updatable; - }, - owner_role.clone(), - ).build(); - - std::thread::sleep(std::time::Duration::from_secs(5)); - let package_addresses = transaction_service - .submit_manifest(manifest, ¬ary_private_key, &fee_handling)? - .new_package_addresses; - - let ( - simple_oracle_package_address, - ociswap_v1_adapter_v1_package_address, - caviarnine_v1_adapter_v1_package_address, - ) = ( - *package_addresses.first().unwrap(), - *package_addresses.get(1).unwrap(), - *package_addresses.get(2).unwrap(), - ); - - // Publishing the Ignition package - let manifest = ManifestBuilder::new() - .publish_package_advanced( - None, - ignition_code, - ignition_package_definition, - metadata_init! { - "name" => "Ignition Package", updatable; - "description" => "The implementation of the Ignition protocol.", updatable; - "tags" => Vec::::new(), updatable; - "dapp_definition" => dapp_definition_account, updatable; - }, - owner_role.clone(), - ) - .build(); - std::thread::sleep(std::time::Duration::from_secs(5)); - let ignition_package_address = *transaction_service - .submit_manifest(manifest, ¬ary_private_key, &fee_handling)? - .new_package_addresses - .first() - .unwrap(); - - ( - ignition_package_address, - simple_oracle_package_address, - ociswap_v1_adapter_v1_package_address, - caviarnine_v1_adapter_v1_package_address, - ) - }; - - // Creating the different liquidity receipt resources that the different - // exchanges will use. They will be mintable and burnable through the - // Ignition package caller badge. - let ignition_package_global_caller_rule = - rule!(require(package_of_direct_caller(ignition_package_address))); - let ( - ociswap_v1_liquidity_receipt_resource, - caviarnine_v1_liquidity_receipt_resource, - ) = { - let roles = NonFungibleResourceRoles { - // Mintable and burnable by the Ignition package and - // the protocol owner can update who can do that. - mint_roles: mint_roles! { - minter => ignition_package_global_caller_rule.clone(); - minter_updater => protocol_owner_rule.clone(); - }, - burn_roles: burn_roles! { - burner => ignition_package_global_caller_rule.clone(); - burner_updater => protocol_owner_rule.clone(); - }, - // We reserve the right to change the data of the - // liquidity receipts when we want. - non_fungible_data_update_roles: non_fungible_data_update_roles! { - non_fungible_data_updater => rule!(deny_all); - non_fungible_data_updater_updater => protocol_owner_rule.clone(); - }, - // Everything else is deny all and can't be changed. - recall_roles: recall_roles! { - recaller => rule!(deny_all); - recaller_updater => rule!(deny_all); - }, - freeze_roles: freeze_roles! { - freezer => rule!(deny_all); - freezer_updater => rule!(deny_all); - }, - deposit_roles: deposit_roles! { - depositor => rule!(allow_all); - depositor_updater => rule!(deny_all); - }, - withdraw_roles: withdraw_roles! { - withdrawer => rule!(allow_all); - withdrawer_updater => rule!(deny_all); - }, - }; - - let manifest = ManifestBuilder::new() - // Ociswap liquidity receipt - .call_function( - RESOURCE_PACKAGE, - NON_FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT, - NON_FUNGIBLE_RESOURCE_MANAGER_CREATE_RUID_WITH_INITIAL_SUPPLY_IDENT, - NonFungibleResourceManagerCreateRuidWithInitialSupplyManifestInput { - owner_role: owner_role.clone(), - track_total_supply: true, - non_fungible_schema: NonFungibleDataSchema::new_local_without_self_package_replacement::>(), - entries: Vec::new(), - resource_roles: roles.clone(), - metadata: metadata! { - roles { - metadata_setter => protocol_owner_rule.clone(); - metadata_setter_updater => protocol_owner_rule.clone(); - metadata_locker => protocol_owner_rule.clone(); - metadata_locker_updater => protocol_owner_rule.clone(); - }, - init { - // TODO: Confirm with the exchanges what they want - // their name to be. - "name" => "Ignition LP: Ociswap", updatable; - "description" => "Represents a particular contribution of liquidity to Ociswap through the Ignition liquidity incentives program. See the redeem_url metadata for where to redeem these NFTs.", updatable; - "tags" => vec!["lp token"], updatable; - "dapp_definitions" => vec![dapp_definition_account], updatable; - // TODO: Must get this from our design team - "icon_url" => UncheckedUrl::of("https://www.google.com"), updatable; - "DEX" => "Ociswap", updatable; - // TODO: Must get this from Ociswap! - "redeem_url" => UncheckedUrl::of("https://www.google.com"), updatable; - } - }, - address_reservation: None - } - ) - // Caviarnine liquidity receipt - .call_function( - RESOURCE_PACKAGE, - NON_FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT, - NON_FUNGIBLE_RESOURCE_MANAGER_CREATE_RUID_WITH_INITIAL_SUPPLY_IDENT, - NonFungibleResourceManagerCreateRuidWithInitialSupplyManifestInput { - owner_role: owner_role.clone(), - track_total_supply: true, - non_fungible_schema: NonFungibleDataSchema::new_local_without_self_package_replacement::>(), - entries: Vec::new(), - resource_roles: roles.clone(), - metadata: metadata! { - roles { - metadata_setter => protocol_owner_rule.clone(); - metadata_setter_updater => protocol_owner_rule.clone(); - metadata_locker => protocol_owner_rule.clone(); - metadata_locker_updater => protocol_owner_rule.clone(); - }, - init { - // TODO: Confirm with the exchanges what they want - // their name to be. - "name" => "Ignition LP: Caviarnine", updatable; - "description" => "Represents a particular contribution of liquidity to Caviarnine through the Ignition liquidity incentives program. See the redeem_url metadata for where to redeem these NFTs.", updatable; - "tags" => vec!["lp token"], updatable; - "dapp_definitions" => vec![dapp_definition_account], updatable; - // TODO: Must get this from our design team - "icon_url" => UncheckedUrl::of("https://www.google.com"), updatable; - "DEX" => "Caviarnine", updatable; - // TODO: Must get this from Caviarnine! - "redeem_url" => UncheckedUrl::of("https://www.google.com"), updatable; - } - }, - address_reservation: None - } - ) - .build(); - - std::thread::sleep(std::time::Duration::from_secs(5)); - let resource_addresses = transaction_service - .submit_manifest(manifest, ¬ary_private_key, &fee_handling)? - .new_resource_addresses; - ( - *resource_addresses.first().unwrap(), - *resource_addresses.get(1).unwrap(), - ) - }; - - // Creating the oracle and adapters. - let ( - ignition_component, - simple_oracle_component, - ociswap_v1_adapter_v1_component, - caviarnine_v1_adapter_v1_component, - ) = { - let manifest = ManifestBuilder::new() - // Creating the oracle component - .call_function( - simple_oracle_package_address, - "SimpleOracle", - "instantiate", - ( - protocol_manager_rule.clone(), - metadata_init! { - "name" => "Ignition Oracle", updatable; - "description" => "The oracle used by the Ignition protocol.", updatable; - "tags" => vec!["oracle"], updatable; - "dapp_definition" => dapp_definition_account, updatable; - }, - owner_role.clone(), - None::, - ), - ) - // Creating the ociswap adapter v1 component - .call_function( - ociswap_v1_adapter_v1_package_address, - "OciswapV1Adapter", - "instantiate", - ( - metadata_init! { - "name" => "Ignition Ociswap Adapter", updatable; - "description" => "The adapter used by the Ignition protocol to communicate with Ociswap pools.", updatable; - "dapp_definition" => dapp_definition_account, updatable; - }, - owner_role.clone(), - None::, - ), - ) - // Creating the ociswap adapter v1 component - .call_function( - caviarnine_v1_adapter_v1_package_address, - "CaviarnineV1Adapter", - "instantiate", - ( - metadata_init! { - "name" => "Ignition Caviarnine Adapter", updatable; - "description" => "The adapter used by the Ignition protocol to communicate with Caviarnine pools.", updatable; - "dapp_definition" => dapp_definition_account, updatable; - }, - owner_role.clone(), - None::, - ), - ) - .build(); - - std::thread::sleep(std::time::Duration::from_secs(5)); - let component_addresses = transaction_service - .submit_manifest(manifest, ¬ary_private_key, &fee_handling)? - .new_component_addresses; - - let ( - simple_oracle_component, - ociswap_v1_adapter_v1_component, - caviarnine_v1_adapter_v1_component, - ) = ( - *component_addresses.first().unwrap(), - *component_addresses.get(1).unwrap(), - *component_addresses.get(2).unwrap(), - ); - - // Instantiating the Ignition component - let manifest = ManifestBuilder::new() - // Instantiate Ignition. - .call_function( - ignition_package_address, - "Ignition", - "instantiate", - manifest_args!( - metadata_init! { - "name" => "Ignition", updatable; - "description" => "The Ignition protocol component", updatable; - "dapp_definition" => dapp_definition_account, updatable; - }, - owner_role.clone(), - protocol_owner_rule.clone(), - protocol_manager_rule.clone(), - protocol_resource, - simple_oracle_component, - MAXIMUM_ALLOWED_PRICE_STALENESS_IN_SECONDS, - MAXIMUM_ALLOWED_PRICE_DIFFERENCE_PERCENTAGE, - InitializationParametersManifest { - initial_pool_information: Some(indexmap! { - BlueprintId { - package_address: exchanges.caviarnine_v1.package, - blueprint_name: "QuantaSwap".to_owned() - } => PoolBlueprintInformation { - adapter: caviarnine_v1_adapter_v1_component, - allowed_pools: exchanges.caviarnine_v1.pools.into_iter().collect(), - liquidity_receipt: caviarnine_v1_liquidity_receipt_resource - }, - BlueprintId { - package_address: exchanges.ociswap_v1.package, - blueprint_name: "BasicPool".to_owned() - } => PoolBlueprintInformation { - adapter: ociswap_v1_adapter_v1_component, - // TODO: Fix this when we have actual - // ociswap pools. - allowed_pools: Default::default(), - // allowed_pools: exchanges.ociswap.pools.into_iter().collect(), - liquidity_receipt: ociswap_v1_liquidity_receipt_resource - } - }), - initial_user_resource_volatility: Some( - indexmap! { - resources.bitcoin => Volatility::Volatile, - resources.ethereum => Volatility::Volatile, - resources.usdc => Volatility::NonVolatile, - resources.usdt => Volatility::NonVolatile, - } - ), - initial_reward_rates: Some(reward_information), - initial_volatile_protocol_resources: None, - initial_non_volatile_protocol_resources: None, - initial_is_open_position_enabled: Some(true), - initial_is_close_position_enabled: Some(true), - }, - None:: - ) - ) - .build(); - - std::thread::sleep(std::time::Duration::from_secs(5)); - let component_addresses = transaction_service - .submit_manifest( - manifest, - &ephemeral_private_key, - &fee_handling, - )? - .new_component_addresses; - - let ignition_component_address = - *component_addresses.first().unwrap(); - - ( - ignition_component_address, - simple_oracle_component, - ociswap_v1_adapter_v1_component, - caviarnine_v1_adapter_v1_component, - ) - }; - - // Updating the dapp definition account with the metadata that it - // should have. - { - let manifest = ManifestBuilder::new() - .set_metadata( - dapp_definition_account, - "account_type", - "dapp definition", - ) - .set_metadata( - dapp_definition_account, - "claimed_websites", - Vec::::new(), - ) - .set_metadata( - dapp_definition_account, - "dapp_definitions", - Vec::::new(), - ) - .set_metadata( - dapp_definition_account, - "claimed_entities", - vec![ - GlobalAddress::from(protocol_manager_badge), - GlobalAddress::from(protocol_owner_badge), - GlobalAddress::from(ignition_package_address), - GlobalAddress::from(simple_oracle_package_address), - GlobalAddress::from( - ociswap_v1_adapter_v1_package_address, - ), - GlobalAddress::from( - caviarnine_v1_adapter_v1_package_address, - ), - GlobalAddress::from( - ociswap_v1_liquidity_receipt_resource, - ), - GlobalAddress::from( - caviarnine_v1_liquidity_receipt_resource, - ), - GlobalAddress::from(ignition_component), - GlobalAddress::from(simple_oracle_component), - GlobalAddress::from(ociswap_v1_adapter_v1_component), - GlobalAddress::from(caviarnine_v1_adapter_v1_component), - ], - ) - .call_role_assignment_method( - dapp_definition_account, - ROLE_ASSIGNMENT_SET_OWNER_IDENT, - RoleAssignmentSetOwnerInput { - rule: protocol_owner_rule, - }, - ) - .build(); - - std::thread::sleep(std::time::Duration::from_secs(5)); - transaction_service.submit_manifest( - manifest, - &ephemeral_private_key, - &fee_handling, - )?; - } - - Ok(()) - } -} - -pub struct DexInformation { - pub pools: NameIndexedResourceInformation, - pub package: PackageAddress, -} diff --git a/tools/bootstrap/src/transaction_service.rs b/tools/bootstrap/src/transaction_service.rs deleted file mode 100644 index eecae6ee..00000000 --- a/tools/bootstrap/src/transaction_service.rs +++ /dev/null @@ -1,386 +0,0 @@ -use crate::error::*; -use gateway_client::apis::configuration::*; -use gateway_client::apis::status_api::*; -use gateway_client::apis::transaction_api::*; -use gateway_client::models::*; -use radix_engine::transaction::*; -use radix_engine_interface::blueprints::account::*; -use radix_engine_interface::prelude::*; -use std::thread::*; -use std::time::*; -use transaction::manifest::*; -use transaction::prelude::*; - -type NativePublicKey = radix_engine_interface::crypto::PublicKey; -type GatewayPublicKey = gateway_client::models::PublicKey; - -/// A transaction service that provides a higher-level abstraction over the -/// gateway API. -pub struct TransactionService<'a> { - /// The Bech32m encoders and decoders that the transaction service uses. - bech32m_coders: &'a Bech32mCoders<'a>, - - /// The base url of the gateway API. - gateway_api_base_url: String, - - /// Controls how often the transaction service should poll for the - /// transaction status. This defaults to 5 seconds which is 5,000 - /// milliseconds. - polling_frequency_in_milliseconds: u64, - - /// Controls how many polling attempts the transaction service should make - /// before considering that to be an error. This defaults to 12 attempts. - maximum_number_of_polling_attempts: u64, -} - -impl<'a> TransactionService<'a> { - pub fn new( - bech32m_coders: &'a Bech32mCoders, - gateway_api_base_url: impl Into, - ) -> Self { - Self::new_configurable(bech32m_coders, gateway_api_base_url, 5_000, 12) - } - - pub fn new_configurable( - bech32m_coders: &'a Bech32mCoders, - gateway_api_base_url: impl Into, - polling_frequency_in_milliseconds: u64, - maximum_number_of_polling_attempts: u64, - ) -> Self { - Self { - bech32m_coders, - gateway_api_base_url: gateway_api_base_url.into(), - polling_frequency_in_milliseconds, - maximum_number_of_polling_attempts, - } - } - - pub fn submit_manifest( - &self, - mut manifest: TransactionManifestV1, - notary_private_key: &PrivateKey, - fee_handling: &FeeHandling<'_>, - ) -> std::result::Result { - // Generating the nonce that will be used in submitting the transaction. - let nonce = rand::random::(); - - // Getting the epoch bounds of this transaction - let current_epoch = self.current_epoch()?; - let max_epoch = current_epoch.after(10).unwrap(); - - let additional_signatures = if let FeeHandling::EstimateAndLock { - fee_payer_private_key, - .. - } = fee_handling - { - let is_additional_fee_payer_signature_required = - match (notary_private_key, fee_payer_private_key) { - ( - PrivateKey::Secp256k1(notary), - PrivateKey::Secp256k1(fee_payer), - ) => notary.to_bytes() != fee_payer.to_bytes(), - ( - PrivateKey::Ed25519(notary), - PrivateKey::Ed25519(fee_payer), - ) => notary.to_bytes() != fee_payer.to_bytes(), - (PrivateKey::Secp256k1(..), PrivateKey::Ed25519(..)) - | (PrivateKey::Ed25519(..), PrivateKey::Secp256k1(..)) => { - true - } - }; - - if is_additional_fee_payer_signature_required { - vec![fee_payer_private_key] - } else { - vec![] - } - } else { - vec![] - }; - - // If we need to estimate the fees then we must get a preview of the - // manifest to estimate how much the fees will be. - if let FeeHandling::EstimateAndLock { - fee_payer_account, - fee_payer_private_key, - } = fee_handling - { - let decompiled_manifest = decompile( - &manifest.instructions, - self.bech32m_coders.network_definition, - )?; - - let fees = { - // Getting a preview of the manifest. - let preview_response = transaction_preview( - &self.gateway_config(), - TransactionPreviewRequest { - manifest: decompiled_manifest.clone(), - blobs_hex: Some( - manifest.blobs.values().map(hex::encode).collect(), - ), - start_epoch_inclusive: current_epoch.number() as i64, - end_epoch_exclusive: max_epoch.number() as i64, - notary_public_key: match notary_private_key.public_key() - { - NativePublicKey::Secp256k1(pk) => Some(Box::new( - GatewayPublicKey::EcdsaSecp256k1 { key: pk.0 }, - )), - NativePublicKey::Ed25519(pk) => { - Some(Box::new(GatewayPublicKey::EddsaEd25519 { - key: pk.0, - })) - } - }, - notary_is_signatory: Some(true), - tip_percentage: 0, - nonce: nonce as i64, - signer_public_keys: vec![match fee_payer_private_key { - PrivateKey::Secp256k1(pk) => { - GatewayPublicKey::EcdsaSecp256k1 { - key: pk.public_key().0, - } - } - PrivateKey::Ed25519(pk) => { - GatewayPublicKey::EddsaEd25519 { - key: pk.public_key().0, - } - } - }], - flags: Box::new(TransactionPreviewRequestFlags { - use_free_credit: true, - assume_all_signature_proofs: true, - skip_epoch_check: false, - }), - }, - )?; - - // Ensure that the transaction succeeded in preview. Getting the - // fees of a transaction that failed or was rejected has no - // point. - let receipt = scrypto_decode::( - &preview_response.encoded_receipt, - ) - .unwrap() - .into_latest(); - - if !receipt.is_commit_success() { - return Err(Error::PreviewFailed { - manifest: decompiled_manifest, - receipt, - }); - } - - receipt.fee_summary.total_execution_cost_in_xrd - + receipt.fee_summary.total_finalization_cost_in_xrd - + receipt.fee_summary.total_tipping_cost_in_xrd - + receipt.fee_summary.total_storage_cost_in_xrd - + receipt.fee_summary.total_royalty_cost_in_xrd - }; - - // Adding a 50% padding over the fees that were calculated. - let fees_to_lock = fees * dec!(1.5); - - // Adding the instruction to lock fees. - manifest.instructions.insert( - 0, - InstructionV1::CallMethod { - address: (*fee_payer_account).into(), - method_name: ACCOUNT_LOCK_FEE_IDENT.to_owned(), - args: manifest_args!(fees_to_lock).into(), - }, - ); - }; - - // Constructing the transaction and submitting it. - let mut builder = TransactionBuilder::new().manifest(manifest).header( - TransactionHeaderV1 { - network_id: self.bech32m_coders.network_definition.id, - start_epoch_inclusive: current_epoch, - end_epoch_exclusive: max_epoch, - nonce, - notary_public_key: notary_private_key.public_key(), - notary_is_signatory: true, - tip_percentage: 0, - }, - ); - for key in additional_signatures { - builder = builder.sign(*key); - } - let notarized_transaction = - builder.notarize(notary_private_key).build(); - - // Compiling the notarized transaction and submitting it to the gateway. - let compiled_notarized_transaction = - notarized_transaction.to_payload_bytes().unwrap(); - transaction_submit( - &self.gateway_config(), - TransactionSubmitRequest { - notarized_transaction: compiled_notarized_transaction, - }, - )?; - - // Getting the intent hash and starting to poll for the transaction. - let intent_hash = - notarized_transaction.prepare().unwrap().intent_hash(); - let bech32m_intent_hash = self - .bech32m_coders - .transaction_hash_encoder - .encode(&intent_hash) - .unwrap(); - println!("{bech32m_intent_hash}"); - - for _ in 0..self.maximum_number_of_polling_attempts { - match transaction_status( - &self.gateway_config(), - TransactionStatusRequest { - intent_hash: bech32m_intent_hash.clone(), - }, - ) { - Ok(TransactionStatusResponse { - status: TransactionStatus::CommittedSuccess, - .. - }) => { - // The transaction has been committed successfully. We can - // now get the transaction committed details with no issues. - let committed_details = transaction_committed_details( - &self.gateway_config(), - TransactionCommittedDetailsRequest { - intent_hash: bech32m_intent_hash.clone(), - opt_ins: Some(Box::new(TransactionDetailsOptIns { - raw_hex: Some(true), - receipt_state_changes: Some(true), - receipt_fee_summary: Some(true), - receipt_fee_source: Some(true), - receipt_fee_destination: Some(true), - receipt_costing_parameters: Some(true), - receipt_events: Some(true), - receipt_output: Some(true), - affected_global_entities: Some(true), - balance_changes: Some(true), - })), - at_ledger_state: None, - }, - )?; - - let state_updates = committed_details - .transaction - .receipt - .unwrap() - .state_updates - .unwrap(); - - let mut simplified_receipt = SimplifiedTransactionReceipt { - new_component_addresses: Default::default(), - new_resource_addresses: Default::default(), - new_package_addresses: Default::default(), - }; - - for entity in state_updates.new_global_entities { - let address_string = entity.entity_address; - - if let Some(address) = PackageAddress::try_from_bech32( - &self.bech32m_coders.address_decoder, - &address_string, - ) { - simplified_receipt - .new_package_addresses - .push(address) - } else if let Some(address) = - ResourceAddress::try_from_bech32( - &self.bech32m_coders.address_decoder, - &address_string, - ) - { - simplified_receipt - .new_resource_addresses - .push(address) - } else if let Some(address) = - ComponentAddress::try_from_bech32( - &self.bech32m_coders.address_decoder, - &address_string, - ) - { - simplified_receipt - .new_component_addresses - .push(address) - } - } - - return Ok(simplified_receipt); - } - Ok(TransactionStatusResponse { - status: - TransactionStatus::CommittedFailure - | TransactionStatus::Rejected, - .. - }) => { - return Err(Error::TransactionWasNotSuccessful { - intent_hash: bech32m_intent_hash, - }) - } - _ => {} - } - sleep(Duration::from_millis( - self.polling_frequency_in_milliseconds, - )); - } - - Err(Error::TransactionPollingYieldedNothing { - intent_hash: bech32m_intent_hash, - }) - } - - fn gateway_config(&self) -> Configuration { - Configuration { - base_path: self.gateway_api_base_url.clone(), - ..Default::default() - } - } - - fn current_epoch(&self) -> std::result::Result { - Ok(Epoch::of( - gateway_status(&self.gateway_config())?.ledger_state.epoch as u64, - )) - } -} - -pub struct SimplifiedTransactionReceipt { - pub new_component_addresses: Vec, - pub new_resource_addresses: Vec, - pub new_package_addresses: Vec, -} - -pub enum FeeHandling<'a> { - AlreadyHandled, - EstimateAndLock { - fee_payer_account: ComponentAddress, - fee_payer_private_key: &'a PrivateKey, - }, -} - -pub struct Bech32mCoders<'a> { - pub network_definition: &'a NetworkDefinition, - pub address_encoder: AddressBech32Encoder, - pub address_decoder: AddressBech32Decoder, - pub transaction_hash_encoder: TransactionHashBech32Encoder, - pub transaction_hash_decoder: TransactionHashBech32Decoder, -} - -impl<'a> Bech32mCoders<'a> { - pub fn from_network_definition( - network_definition: &'a NetworkDefinition, - ) -> Self { - Self { - network_definition, - address_encoder: AddressBech32Encoder::new(network_definition), - address_decoder: AddressBech32Decoder::new(network_definition), - transaction_hash_encoder: TransactionHashBech32Encoder::new( - network_definition, - ), - transaction_hash_decoder: TransactionHashBech32Decoder::new( - network_definition, - ), - } - } -} diff --git a/tools/bootstrap/src/types/mod.rs b/tools/bootstrap/src/types/mod.rs deleted file mode 100644 index e36e386e..00000000 --- a/tools/bootstrap/src/types/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod name_indexed_dex_information; -mod name_indexed_resource_information; - -pub use name_indexed_dex_information::*; -pub use name_indexed_resource_information::*; diff --git a/tools/bootstrap/src/types/name_indexed_dex_information.rs b/tools/bootstrap/src/types/name_indexed_dex_information.rs deleted file mode 100644 index 91302a39..00000000 --- a/tools/bootstrap/src/types/name_indexed_dex_information.rs +++ /dev/null @@ -1,30 +0,0 @@ -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct NameIndexedDexInformation { - pub ociswap_v1: T, - pub caviarnine_v1: T, -} - -impl NameIndexedDexInformation { - pub fn map(&self, mut map: F) -> NameIndexedDexInformation - where - F: FnMut(&T) -> O, - { - NameIndexedDexInformation:: { - ociswap_v1: map(&self.ociswap_v1), - caviarnine_v1: map(&self.caviarnine_v1), - } - } - - pub fn try_map( - &self, - mut map: F, - ) -> Result, E> - where - F: FnMut(&T) -> Result, - { - Ok(NameIndexedDexInformation:: { - ociswap_v1: map(&self.ociswap_v1)?, - caviarnine_v1: map(&self.caviarnine_v1)?, - }) - } -} diff --git a/tools/bootstrap/src/types/name_indexed_resource_information.rs b/tools/bootstrap/src/types/name_indexed_resource_information.rs deleted file mode 100644 index fc5ad2f7..00000000 --- a/tools/bootstrap/src/types/name_indexed_resource_information.rs +++ /dev/null @@ -1,45 +0,0 @@ -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct NameIndexedResourceInformation { - pub bitcoin: T, - pub ethereum: T, - pub usdc: T, - pub usdt: T, -} - -impl NameIndexedResourceInformation { - pub fn map(&self, mut map: F) -> NameIndexedResourceInformation - where - F: FnMut(&T) -> O, - { - NameIndexedResourceInformation:: { - bitcoin: map(&self.bitcoin), - ethereum: map(&self.ethereum), - usdc: map(&self.usdc), - usdt: map(&self.usdt), - } - } - - pub fn try_map( - &self, - mut map: F, - ) -> Result, E> - where - F: FnMut(&T) -> Result, - { - Ok(NameIndexedResourceInformation:: { - bitcoin: map(&self.bitcoin)?, - ethereum: map(&self.ethereum)?, - usdc: map(&self.usdc)?, - usdt: map(&self.usdt)?, - }) - } -} - -impl std::iter::IntoIterator for NameIndexedResourceInformation { - type Item = T; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - vec![self.bitcoin, self.ethereum, self.usdc, self.usdt].into_iter() - } -} diff --git a/tools/publishing-tool/Cargo.toml b/tools/publishing-tool/Cargo.toml index d48427a2..a3d462e3 100644 --- a/tools/publishing-tool/Cargo.toml +++ b/tools/publishing-tool/Cargo.toml @@ -17,17 +17,32 @@ common = { path = "../../libraries/common" } ignition = { path = "../../packages/ignition" } package-loader = { path = "../../libraries/package-loader" } gateway-client = { path = "../../libraries/gateway-client" } + ociswap-v1-adapter-v1 = { path = "../../packages/ociswap-v1-adapter-v1", features = [ "manifest-builder-stubs", ] } +ociswap-v2-adapter-v1 = { path = "../../packages/ociswap-v2-adapter-v1", features = [ + "manifest-builder-stubs", +] } +defiplaza-v2-adapter-v1 = { path = "../../packages/defiplaza-v2-adapter-v1", features = [ + "manifest-builder-stubs", +] } caviarnine-v1-adapter-v1 = { path = "../../packages/caviarnine-v1-adapter-v1", features = [ "manifest-builder-stubs", ] } state-manager = { git = "https://github.com/radixdlt/babylon-node", rev = "63a8267196995fef0830e4fbf0271bea65c90ab1" } +sbor-json = { git = "https://github.com/radixdlt/radix-engine-toolkit", rev = "1cfe879c7370cfa497857ada7a8973f8a3388abc" } hex = { version = "0.4.3" } -rand = "0.8.5" +rand = { version = "0.8.5" } +macro_rules_attribute = { version = "0.2.0" } +log = "0.4.21" +env_logger = "0.11.2" +hex-literal = "0.4.1" +itertools = "0.12.1" +serde_json = "1.0.114" +clap = { version = "4.5.1", features = ["derive"] } [lints] workspace = true diff --git a/tools/publishing-tool/src/cli/default_configurations/mainnet_testing.rs b/tools/publishing-tool/src/cli/default_configurations/mainnet_testing.rs new file mode 100644 index 00000000..0d15cbdc --- /dev/null +++ b/tools/publishing-tool/src/cli/default_configurations/mainnet_testing.rs @@ -0,0 +1,304 @@ +use common::prelude::*; + +use self::utils::*; +use crate::*; + +pub fn mainnet_testing( + notary_private_key: &PrivateKey, +) -> PublishingConfiguration { + let notary_account_address = + ComponentAddress::virtual_account_from_public_key( + ¬ary_private_key.public_key(), + ); + + // cSpell:disable + PublishingConfiguration { + protocol_configuration: ProtocolConfiguration { + protocol_resource: resource_address!( + "resource_rdx1t4dekrf58h0r28s3c93z92w3jt5ngx87jzd63mgc597zmf3534rxfv" + ), + user_resource_volatility: UserResourceIndexedData { + bitcoin: Volatility::Volatile, + ethereum: Volatility::Volatile, + usdc: Volatility::NonVolatile, + usdt: Volatility::NonVolatile, + }, + reward_rates: indexmap! { + LockupPeriod::from_minutes(0).unwrap() => dec!(0.125), // 12.5% + LockupPeriod::from_minutes(1).unwrap() => dec!(0.15), // 15.0% + }, + allow_opening_liquidity_positions: true, + allow_closing_liquidity_positions: true, + maximum_allowed_price_staleness: i64::MAX, + maximum_allowed_price_difference_percentage: Decimal::MAX, + entities_metadata: Entities { + protocol_entities: ProtocolIndexedData { + ignition: metadata_init! { + "name" => "Ignition", updatable; + "description" => "The main entrypoint into the Ignition liquidity incentive program.", updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + simple_oracle: metadata_init! { + "name" => "Ignition Oracle", updatable; + "description" => "The oracle used by the Ignition protocol.", updatable; + "tags" => vec!["oracle"], updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + }, + exchange_adapter_entities: ExchangeIndexedData { + ociswap_v2: metadata_init! { + "name" => "Ignition Ociswap v2 Adapter", updatable; + "description" => "An adapter used by the Ignition protocol to communicate with Ociswap v2 pools.", updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + defiplaza_v2: metadata_init! { + "name" => "Ignition DefiPlaza v2 Adapter", updatable; + "description" => "An adapter used by the Ignition protocol to communicate with DefiPlaza v2 pools.", updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + caviarnine_v1: metadata_init! { + "name" => "Ignition Caviarnine v1 Adapter", updatable; + "description" => "An adapter used by the Ignition protocol to communicate with Caviarnine v1 pools.", updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + }, + }, + }, + dapp_definition_metadata: indexmap! { + "name".to_owned() => MetadataValue::String("Project Ignition".to_owned()), + "description".to_owned() => MetadataValue::String("A Radix liquidity incentives program, offered in partnership with select decentralized exchange dApps in the Radix ecosystem.".to_owned()), + "icon_url".to_owned() => MetadataValue::Url(UncheckedUrl::of("https://assets.radixdlt.com/icons/icon-Ignition-LP.png")) + }, + transaction_configuration: TransactionConfiguration { + notary: clone_private_key(¬ary_private_key), + fee_payer_information: AccountAndControllingKey::new_virtual_account( + clone_private_key(¬ary_private_key), + ), + }, + // TODO: Determine where they should be sent to. + badges: BadgeIndexedData { + oracle_manager_badge: BadgeHandling::CreateAndSend { + account_address: component_address!( + "account_rdx168nr5dwmll4k2x5apegw5dhrpejf3xac7khjhgjqyg4qddj9tg9v4d" + ), + metadata_init: metadata_init! { + "name" => "Ignition Oracle Manager", updatable; + "symbol" => "IGNOM", updatable; + "description" => "A badge with the authority to update the Oracle prices of the Ignition oracle.", updatable; + "tags" => vec!["badge"], updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + }, + protocol_manager_badge: BadgeHandling::CreateAndSend { + account_address: notary_account_address, + metadata_init: metadata_init! { + "name" => "Ignition Protocol Manager", updatable; + "symbol" => "IGNPM", updatable; + "description" => "A badge with the authority to manage the Ignition protocol.", updatable; + "tags" => vec!["badge"], updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + }, + protocol_owner_badge: BadgeHandling::CreateAndSend { + account_address: notary_account_address, + metadata_init: metadata_init! { + "name" => "Ignition Protocol Owner", updatable; + "symbol" => "IGNPO", updatable; + "description" => "A badge with owner authority over the Ignition protocol.", updatable; + "tags" => vec!["badge"], updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + }, + }, + // TODO: Not real resources, just the notXYZ resources. + user_resources: UserResourceIndexedData { + bitcoin: UserResourceHandling::UseExisting { + resource_address: resource_address!( + "resource_rdx1t58dla7ykxzxe5es89wlhgzatqla0gceukg0eeduzvtj4cxd55etn8" + ), + }, + ethereum: UserResourceHandling::UseExisting { + resource_address: resource_address!( + "resource_rdx1tkscrlztcyn82ej5z3n232f0qqp0qur69arjf279ppmg5usa3xhnsm" + ), + }, + usdc: UserResourceHandling::UseExisting { + resource_address: resource_address!( + "resource_rdx1th7nx2hy0cf6aea6mz7zhkdmy4p45s488xutltnp7296zxj8hwchpf" + ), + }, + usdt: UserResourceHandling::UseExisting { + resource_address: resource_address!( + "resource_rdx1tkafx32lu72mcxr85gjx0rh3rx9q89zqffg4phmv5rxdqg5fnd0w7s" + ), + }, + }, + packages: Entities { + protocol_entities: ProtocolIndexedData { + ignition: PackageHandling::LoadAndPublish { + crate_package_name: "ignition".to_owned(), + metadata: metadata_init! { + "name" => "Ignition Package", updatable; + "description" => "The implementation of the Ignition protocol.", updatable; + "tags" => Vec::::new(), updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + blueprint_name: "Ignition".to_owned(), + }, + simple_oracle: PackageHandling::LoadAndPublish { + crate_package_name: "simple-oracle".to_owned(), + metadata: metadata_init! { + "name" => "Ignition Simple Oracle Package", updatable; + "description" => "The implementation of the Oracle used by the Ignition protocol.", updatable; + "tags" => vec!["oracle"], updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + blueprint_name: "SimpleOracle".to_owned(), + }, + }, + exchange_adapter_entities: ExchangeIndexedData { + ociswap_v2: PackageHandling::LoadAndPublish { + crate_package_name: "ociswap-v2-adapter-v1".to_owned(), + metadata: metadata_init! { + "name" => "Ignition Ociswap v2 Adapter Package", updatable; + "description" => "The implementation of an adapter for Ociswap v2 for the Ignition protocol.", updatable; + "tags" => vec!["adapter"], updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + blueprint_name: "OciswapV2Adapter".to_owned(), + }, + defiplaza_v2: PackageHandling::LoadAndPublish { + crate_package_name: "defiplaza-v2-adapter-v1".to_owned(), + metadata: metadata_init! { + "name" => "Ignition DefiPlaza v2 Adapter Package", updatable; + "description" => "The implementation of an adapter for DefiPlaza v1 for the Ignition protocol.", updatable; + "tags" => vec!["adapter"], updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + blueprint_name: "DefiPlazaV2Adapter".to_owned(), + }, + caviarnine_v1: PackageHandling::LoadAndPublish { + crate_package_name: "caviarnine-v1-adapter-v1".to_owned(), + metadata: metadata_init! { + "name" => "Ignition Caviarnine v1 Adapter Package", updatable; + "description" => "The implementation of an adapter for Caviarnine v1 for the Ignition protocol.", updatable; + "tags" => vec!["adapter"], updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + blueprint_name: "CaviarnineV1Adapter".to_owned(), + }, + }, + }, + exchange_information: ExchangeIndexedData { + // No ociswap v2 currently on mainnet. + ociswap_v2: None, + defiplaza_v2: Some(ExchangeInformation { + blueprint_id: BlueprintId { + package_address: package_address!( + "package_rdx1p4dhfl7qwthqqu6p2267m5nedlqnzdvfxdl6q7h8g85dflx8n06p93" + ), + blueprint_name: "PlazaPair".to_owned(), + }, + pools: UserResourceIndexedData { + bitcoin: PoolHandling::UseExisting { + pool_address: component_address!( + "component_rdx1cpgyq8809z4mnc5rw2pvru3xdcjftjv45a5cgcwyqdqtg2xs35r58r" + ), + }, + ethereum: PoolHandling::UseExisting { + pool_address: component_address!( + "component_rdx1cpzf8pygechpgat29phu72nzn4gn6shu7x0fdjydjky6g683sl0azk" + ), + }, + usdc: PoolHandling::UseExisting { + pool_address: component_address!( + "component_rdx1cq32fjfp8gu3hh8jau9m6syfaargxagmvcakwwx966ejy6cwczghw4" + ), + }, + usdt: PoolHandling::UseExisting { + pool_address: component_address!( + "component_rdx1crx4h8dljzufy9m3g5ez49d5ge2q0vwysfc77vxrp8x480rqq3qpre" + ), + }, + }, + liquidity_receipt: LiquidityReceiptHandling::CreateNew { + non_fungible_schema: + NonFungibleDataSchema::new_local_without_self_package_replacement::< + LiquidityReceipt, + >(), + metadata: metadata_init! { + "name" => "Ignition LP: DefiPlaza", updatable; + "description" => "Represents a particular contribution of liquidity to DefiPlaza through the Ignition liquidity incentives program. See the redeem_url metadata for where to redeem these NFTs.", updatable; + "tags" => vec!["lp token"], updatable; + "icon_url" => UncheckedUrl::of("https://assets.radixdlt.com/icons/icon-Ignition-LP.png"), updatable; + "DEX" => "DefiPlaza", updatable; + // TODO: Must get this from the DEX + "redeem_url" => UncheckedUrl::of("https://www.google.com"), updatable; + }, + }, + }), + caviarnine_v1: Some(ExchangeInformation { + blueprint_id: BlueprintId { + package_address: package_address!( + "package_rdx1p4r9rkp0cq67wmlve544zgy0l45mswn6h798qdqm47x4762h383wa3" + ), + blueprint_name: "QuantaSwap".to_owned(), + }, + pools: UserResourceIndexedData { + bitcoin: PoolHandling::UseExisting { + pool_address: component_address!( + "component_rdx1crzl2c39m83lpe6fv62epgp3phqunxhc264ys23qz8xeemjcu8lln3" + ), + }, + ethereum: PoolHandling::UseExisting { + pool_address: component_address!( + "component_rdx1cqk2ufmdq6pkcu7ed7r6u9hmdsht9gyd8y8wwtd7w5znefz9k54a7d" + ), + }, + usdc: PoolHandling::UseExisting { + pool_address: component_address!( + "component_rdx1cq9q8umlpmngff6y4e534htz0n37te4m7vsj50u9zc58ys65zl6jv9" + ), + }, + usdt: PoolHandling::UseExisting { + pool_address: component_address!( + "component_rdx1cpl0v3lndt9d7g7uuepztxs9m7m24ly0yfhvcum2y7tm0vlzst0l5y" + ), + }, + }, + liquidity_receipt: LiquidityReceiptHandling::CreateNew { + non_fungible_schema: + NonFungibleDataSchema::new_local_without_self_package_replacement::< + LiquidityReceipt, + >(), + metadata: metadata_init! { + "name" => "Ignition LP: Caviarnine", updatable; + "description" => "Represents a particular contribution of liquidity to Caviarnine through the Ignition liquidity incentives program. See the redeem_url metadata for where to redeem these NFTs.", updatable; + "tags" => vec!["lp token"], updatable; + "icon_url" => UncheckedUrl::of("https://assets.radixdlt.com/icons/icon-Ignition-LP.png"), updatable; + "DEX" => "Caviarnine", updatable; + // TODO: Must get this from the DEX + "redeem_url" => UncheckedUrl::of("https://www.google.com"), updatable; + }, + }, + }), + }, + additional_information: AdditionalInformation { + ociswap_v2_registry_component: None, + }, + // cSpell:enable + } +} diff --git a/tools/publishing-tool/src/cli/default_configurations/mod.rs b/tools/publishing-tool/src/cli/default_configurations/mod.rs new file mode 100644 index 00000000..49a6f636 --- /dev/null +++ b/tools/publishing-tool/src/cli/default_configurations/mod.rs @@ -0,0 +1,33 @@ +use crate::*; +use clap::*; +mod mainnet_testing; + +#[derive(ValueEnum, Clone, Copy, Debug)] +pub enum ConfigurationSelector { + MainnetTesting, +} + +impl ConfigurationSelector { + pub fn configuration( + self, + notary_private_key: &PrivateKey, + ) -> PublishingConfiguration { + match self { + Self::MainnetTesting => { + mainnet_testing::mainnet_testing(notary_private_key) + } + } + } + + pub fn gateway_base_url(self) -> String { + match self { + Self::MainnetTesting => "https://mainnet.radixdlt.com".to_owned(), + } + } + + pub fn network_definition(self) -> NetworkDefinition { + match self { + Self::MainnetTesting => NetworkDefinition::mainnet(), + } + } +} diff --git a/tools/publishing-tool/src/cli/mod.rs b/tools/publishing-tool/src/cli/mod.rs new file mode 100644 index 00000000..e9eb4a83 --- /dev/null +++ b/tools/publishing-tool/src/cli/mod.rs @@ -0,0 +1,18 @@ +mod default_configurations; +mod publish; + +use crate::Error; +use clap::Parser; + +#[derive(Parser, Debug)] +pub enum Cli { + Publish(publish::Publish), +} + +impl Cli { + pub fn run(self, out: &mut O) -> Result<(), Error> { + match self { + Self::Publish(cmd) => cmd.run(out), + } + } +} diff --git a/tools/publishing-tool/src/cli/publish.rs b/tools/publishing-tool/src/cli/publish.rs new file mode 100644 index 00000000..b5664862 --- /dev/null +++ b/tools/publishing-tool/src/cli/publish.rs @@ -0,0 +1,70 @@ +use super::default_configurations::*; +use crate::utils::*; +use crate::*; +use clap::Parser; +use common::prelude::LockupPeriod; +use defiplaza_v2_adapter_v1::DefiPlazaV2PoolInterfaceManifestBuilderExtensionTrait; +use radix_engine_common::prelude::*; +use state_manager::RocksDBStore; +use std::path::*; +use transaction::prelude::*; + +#[derive(Parser, Debug)] +pub struct Publish { + /// The configuration that the user wants to use when publishing. + configuration_selector: ConfigurationSelector, + + /// The path to the state manager database. + state_manager_database_path: PathBuf, + + /// The hex-encoded private key of the notary. + notary_ed25519_private_key_hex: String, +} + +impl Publish { + pub fn run(self, f: &mut O) -> Result<(), Error> { + // Loading the private key from the passed argument. + let notary_private_key = + hex::decode(self.notary_ed25519_private_key_hex) + .ok() + .and_then(|bytes| Ed25519PrivateKey::from_bytes(&bytes).ok()) + .map(PrivateKey::Ed25519) + .ok_or(Error::PrivateKeyError)?; + + // Loading the configuration to use for the deployment + let configuration = self + .configuration_selector + .configuration(¬ary_private_key); + + // Creating the network connection providers to use for the deployments + let network_definition = + self.configuration_selector.network_definition(); + let gateway_base_url = self.configuration_selector.gateway_base_url(); + let database = + RocksDBStore::new_read_only(self.state_manager_database_path) + .unwrap(); + let mut simulator_network_provider = SimulatorNetworkConnector::new( + &database, + network_definition.clone(), + ); + let mut gateway_network_provider = GatewayNetworkConnector::new( + gateway_base_url, + network_definition.clone(), + PollingConfiguration { + interval_in_seconds: 10, + retries: 10, + }, + ); + + // Running a dry run of the publishing process against the simulator + // network provider. + log::info!("Publishing against the simulator"); + publish(&configuration, &mut simulator_network_provider)?; + + // Running the transactions against the network. + log::info!("Publishing against the gateway"); + let receipt = publish(&configuration, &mut gateway_network_provider)?; + writeln!(f, "{}", to_json(&receipt, &network_definition)).unwrap(); + Ok(()) + } +} diff --git a/tools/publishing-tool/src/error.rs b/tools/publishing-tool/src/error.rs new file mode 100644 index 00000000..42cdceaa --- /dev/null +++ b/tools/publishing-tool/src/error.rs @@ -0,0 +1,20 @@ +use crate::*; + +#[derive(Debug)] +pub enum Error { + PrivateKeyError, + GatewayExecutorError(PublishingError), + SimulatorExecutorError(PublishingError), +} + +impl From> for Error { + fn from(value: PublishingError) -> Self { + Self::GatewayExecutorError(value) + } +} + +impl From> for Error { + fn from(value: PublishingError) -> Self { + Self::SimulatorExecutorError(value) + } +} diff --git a/tools/publishing-tool/src/executor/mainnet_simulator_executor.rs b/tools/publishing-tool/src/executor/mainnet_simulator_executor.rs deleted file mode 100644 index 337771d5..00000000 --- a/tools/publishing-tool/src/executor/mainnet_simulator_executor.rs +++ /dev/null @@ -1,105 +0,0 @@ -use super::*; -use crate::database_overlay::*; -use radix_engine::transaction::*; -use radix_engine::vm::*; -use scrypto_unit::*; -use state_manager::store::*; -use transaction::prelude::*; - -/// An [`Executor`] that simulates the transaction execution on mainnet state. -/// This requires having a mainnet database that the executor can read from. All -/// of the database changes from the transaction execution is written to an -/// overlay which means that the mainnet database's state remains unchanged. -pub struct MainnetSimulatorExecutor<'s>( - TestRunner< - NoExtension, - UnmergeableSubstateDatabaseOverlay<'s, RocksDBStore>, - >, -); - -impl<'s> MainnetSimulatorExecutor<'s> { - pub fn new(database: &'s RocksDBStore) -> Self { - let database = UnmergeableSubstateDatabaseOverlay::new(database); - let test_runner = TestRunnerBuilder::new() - .with_custom_database(database) - .without_trace() - .build(); - Self(test_runner) - } -} - -impl<'s> Executor for MainnetSimulatorExecutor<'s> { - type Error = MainnetSimulatorError; - - fn execute_transaction( - &mut self, - notarized_transaction: &NotarizedTransactionV1, - ) -> Result { - let network_definition = NetworkDefinition::mainnet(); - let raw_transaction = notarized_transaction.to_raw().map_err( - MainnetSimulatorError::NotarizedTransactionRawFormatError, - )?; - - let transaction_receipt = self - .0 - .execute_raw_transaction(&network_definition, &raw_transaction); - - let execution_receipt = match transaction_receipt.result { - TransactionResult::Commit(CommitResult { - outcome: TransactionOutcome::Success(..), - state_update_summary, - .. - }) => ExecutionReceipt::CommitSuccess { - new_entities: NewEntities { - new_component_addresses: state_update_summary - .new_components, - new_resource_addresses: state_update_summary.new_resources, - new_package_addresses: state_update_summary.new_packages, - }, - }, - TransactionResult::Commit(CommitResult { - outcome: TransactionOutcome::Failure(reason), - .. - }) => ExecutionReceipt::CommitFailure { - reason: format!("{:?}", reason), - }, - TransactionResult::Reject(RejectResult { reason }) => { - ExecutionReceipt::Rejection { - reason: format!("{:?}", reason), - } - } - TransactionResult::Abort(AbortResult { reason }) => { - ExecutionReceipt::Abort { - reason: format!("{:?}", reason), - } - } - }; - Ok(execution_receipt) - } - - fn preview_transaction( - &mut self, - preview_intent: PreviewIntentV1, - ) -> Result { - let network_definition = NetworkDefinition::mainnet(); - self.0 - .preview(preview_intent, &network_definition) - .map_err(MainnetSimulatorError::PreviewError) - } - - fn get_current_epoch(&mut self) -> Result { - Ok(self.0.get_current_epoch()) - } - - fn get_network_definition( - &mut self, - ) -> Result { - Ok(NetworkDefinition::mainnet()) - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum MainnetSimulatorError { - NotarizedTransactionRawFormatError(EncodeError), - PreviewError(PreviewError), -} diff --git a/tools/publishing-tool/src/executor/mod.rs b/tools/publishing-tool/src/executor/mod.rs deleted file mode 100644 index 0f330d14..00000000 --- a/tools/publishing-tool/src/executor/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod execution_service; -mod gateway_executor; -mod mainnet_simulator_executor; -mod traits; - -pub use execution_service::*; -pub use gateway_executor::*; -pub use mainnet_simulator_executor::*; -pub use traits::*; diff --git a/tools/bootstrap/src/macros.rs b/tools/publishing-tool/src/macros.rs similarity index 87% rename from tools/bootstrap/src/macros.rs rename to tools/publishing-tool/src/macros.rs index d7fd9452..32b2c508 100644 --- a/tools/bootstrap/src/macros.rs +++ b/tools/publishing-tool/src/macros.rs @@ -53,11 +53,11 @@ macro_rules! global_address { #[macro_export] macro_rules! decode_to_node_id { ($address: expr) => { - ::radix_engine_interface::prelude::AddressBech32Decoder::validate_and_decode_ignore_hrp($address) - .ok() - .and_then(|(_, _, value)| - value.try_into().map(NodeId).ok() - ) - .unwrap() + ::radix_engine_interface::prelude::AddressBech32Decoder::validate_and_decode_ignore_hrp( + $address, + ) + .ok() + .and_then(|(_, _, value)| value.try_into().map(NodeId).ok()) + .unwrap() }; } diff --git a/tools/publishing-tool/src/main.rs b/tools/publishing-tool/src/main.rs index 3c495211..341c3d0c 100644 --- a/tools/publishing-tool/src/main.rs +++ b/tools/publishing-tool/src/main.rs @@ -1,6 +1,23 @@ +#![allow(dead_code, clippy::enum_variant_names)] + +mod cli; mod database_overlay; -mod executor; +mod error; +mod network_connection_provider; +mod publishing; +mod utils; +#[macro_use] +mod macros; + +use error::*; +use network_connection_provider::*; +use publishing::*; +use radix_engine_common::prelude::*; +use transaction::prelude::*; -fn main() { - println!("Hello, world!"); +fn main() -> Result<(), Error> { + env_logger::init(); + let mut out = std::io::stdout(); + let cli = ::parse(); + cli.run(&mut out) } diff --git a/tools/publishing-tool/src/executor/execution_service.rs b/tools/publishing-tool/src/network_connection_provider/execution_service.rs similarity index 62% rename from tools/publishing-tool/src/executor/execution_service.rs rename to tools/publishing-tool/src/network_connection_provider/execution_service.rs index 862853a0..d3108a1b 100644 --- a/tools/publishing-tool/src/executor/execution_service.rs +++ b/tools/publishing-tool/src/network_connection_provider/execution_service.rs @@ -1,6 +1,8 @@ +use itertools::*; use radix_engine::transaction::*; use radix_engine_common::prelude::*; use radix_engine_interface::blueprints::account::*; +use transaction::manifest::*; use transaction::model::*; use transaction::prelude::*; @@ -8,7 +10,7 @@ use super::*; /// A simple execution service whose main responsibilities is to construct, /// submit, and return the result of transactions. -pub struct ExecutionService<'e, E: Executor> { +pub struct ExecutionService<'e, E: NetworkConnectionProvider> { /// The executor that the service will use to execute transactions. executor: &'e mut E, /// The account to use for the payment of fees. @@ -16,37 +18,50 @@ pub struct ExecutionService<'e, E: Executor> { /// The notary of the transaction notary_private_key: &'e PrivateKey, /// The set of private keys that should sign the transaction. - signers_private_keys: &'e [PrivateKey], + signers_private_keys: &'e [&'e PrivateKey], } -impl<'e, E: Executor> ExecutionService<'e, E> { +impl<'e, E: NetworkConnectionProvider> ExecutionService<'e, E> { pub fn new( executor: &'e mut E, fee_payer_account_address: ComponentAddress, notary_private_key: &'e PrivateKey, - additional_signatures: &'e [PrivateKey], + signers_private_keys: &'e [&'e PrivateKey], ) -> Self { Self { executor, fee_payer_account_address, notary_private_key, - signers_private_keys: additional_signatures, + signers_private_keys, } } pub fn execute_manifest( &mut self, mut manifest: TransactionManifestV1, - ) -> Result> { + ) -> Result< + ExecutionReceiptSuccessContents, + ExecutionServiceError<::Error>, + > { + // If the manifest is empty (has no instructions) do no work + if manifest.instructions.is_empty() { + return Ok(ExecutionReceiptSuccessContents { + new_entities: Default::default(), + }); + } + // The signers for the transaction let notary_is_signatory = self.signers_private_keys.iter().any(|private_key| { private_key.public_key() == self.notary_private_key.public_key() }); - let signer_private_keys = - self.signers_private_keys.iter().filter(|private_key| { + let signer_private_keys = self + .signers_private_keys + .iter() + .filter(|private_key| { private_key.public_key() != self.notary_private_key.public_key() - }); + }) + .unique_by(|private_key| private_key.public_key()); // Getting the current network definition let network_definition = self @@ -88,12 +103,14 @@ impl<'e, E: Executor> ExecutionService<'e, E> { }, message: MessageV1::None, }, - signer_public_keys: signer_private_keys - .clone() + signer_public_keys: self + .signers_private_keys + .iter() .map(|private_key| private_key.public_key()) + .unique() .collect(), flags: PreviewFlags { - use_free_credit: false, + use_free_credit: true, assume_all_signature_proofs: false, skip_epoch_check: false, }, @@ -109,7 +126,9 @@ impl<'e, E: Executor> ExecutionService<'e, E> { ); } let total_fees = preview_receipt.fee_summary.total_cost(); - let total_fees_plus_padding = total_fees * dec!(1.20); + let total_fees_plus_padding = + total_fees + self.signers_private_keys.len() * dec!(0.5); + let total_fees_plus_padding = total_fees_plus_padding * dec!(1.10); // Adding a lock fee instruction to the manifest. manifest.instructions.insert( @@ -125,10 +144,11 @@ impl<'e, E: Executor> ExecutionService<'e, E> { ); // Constructing the transaction. - let mut transaction_builder = - TransactionBuilder::new().header(header).manifest(manifest); + let mut transaction_builder = TransactionBuilder::new() + .header(header) + .manifest(manifest.clone()); for signer_private_key in signer_private_keys { - transaction_builder = transaction_builder.sign(signer_private_key) + transaction_builder = transaction_builder.sign(*signer_private_key) } let transaction = transaction_builder .notarize(self.notary_private_key) @@ -140,13 +160,46 @@ impl<'e, E: Executor> ExecutionService<'e, E> { .execute_transaction(&transaction) .map_err(ExecutionServiceError::ExecutorError)?; - Ok(receipt) + // Do a match on the receipt and error out if execution failed. If it + // did not, then return the success contents. + match receipt { + ExecutionReceipt::CommitSuccess(success_contents) => { + Ok(success_contents) + } + ExecutionReceipt::CommitFailure { reason } + | ExecutionReceipt::Rejection { reason } + | ExecutionReceipt::Abort { reason } => { + let decompiled_manifest = + decompile(&manifest.instructions, &network_definition) + .map_err( + ExecutionServiceError::ManifestDecompilationFailed, + )?; + Err( + ExecutionServiceError::TransactionExecutionWasNotSuccessful { + manifest: decompiled_manifest, + reason, + }, + ) + } + } + } + + pub fn with_network_connection_provider(&mut self, callback: F) -> O + where + F: Fn(&mut E) -> O, + { + callback(self.executor) } } #[derive(Debug)] -pub enum ExecutionServiceError { - ExecutorError(::Error), +pub enum ExecutionServiceError { + ExecutorError(E), + ManifestDecompilationFailed(DecompileError), + TransactionExecutionWasNotSuccessful { + manifest: String, + reason: String, + }, TransactionPreviewWasNotSuccessful( TransactionManifestV1, TransactionReceipt, diff --git a/tools/publishing-tool/src/executor/gateway_executor.rs b/tools/publishing-tool/src/network_connection_provider/gateway_connector.rs similarity index 82% rename from tools/publishing-tool/src/executor/gateway_executor.rs rename to tools/publishing-tool/src/network_connection_provider/gateway_connector.rs index f3555b26..cfb8e810 100644 --- a/tools/publishing-tool/src/executor/gateway_executor.rs +++ b/tools/publishing-tool/src/network_connection_provider/gateway_connector.rs @@ -1,7 +1,8 @@ use super::*; use gateway_client::apis::configuration::*; -use gateway_client::apis::status_api::gateway_status; +use gateway_client::apis::state_api::*; use gateway_client::apis::status_api::GatewayStatusError; +use gateway_client::apis::status_api::*; use gateway_client::apis::transaction_api::*; use gateway_client::apis::Error as GatewayClientError; use gateway_client::models::*; @@ -9,7 +10,7 @@ use radix_engine::transaction::*; use transaction::manifest::*; use transaction::prelude::*; -pub struct GatewayExecutor { +pub struct GatewayNetworkConnector { /// The configuration to use when making gateway HTTP requests. pub configuration: Configuration, /// The network definition of the network that the gateway talks to. @@ -18,7 +19,7 @@ pub struct GatewayExecutor { pub polling_configuration: PollingConfiguration, } -impl GatewayExecutor { +impl GatewayNetworkConnector { pub fn new( base_url: impl ToOwned, network_definition: NetworkDefinition, @@ -35,7 +36,7 @@ impl GatewayExecutor { } } -impl Executor for GatewayExecutor { +impl NetworkConnectionProvider for GatewayNetworkConnector { type Error = GatewayExecutorError; fn execute_transaction( @@ -83,6 +84,11 @@ impl Executor for GatewayExecutor { | TransactionIntentStatus::CommitPendingOutcomeUnknown | TransactionIntentStatus::Pending => {} TransactionIntentStatus::CommittedSuccess => { + // We must wait for some time before requesting the commit + // details as I've observed that doing this too quickly can + // result in us not getting commit results back. + std::thread::sleep(std::time::Duration::from_secs(5)); + let transaction_committed_result_response = transaction_committed_details( &self.configuration, TransactionCommittedDetailsRequest { @@ -154,9 +160,9 @@ impl Executor for GatewayExecutor { new_entities }; - return Ok(ExecutionReceipt::CommitSuccess { - new_entities, - }); + return Ok(ExecutionReceipt::CommitSuccess( + ExecutionReceiptSuccessContents { new_entities }, + )); } TransactionIntentStatus::CommittedFailure => { return Ok(ExecutionReceipt::CommitFailure { @@ -257,6 +263,53 @@ impl Executor for GatewayExecutor { ) -> Result { Ok(self.network_definition.clone()) } + + fn read_component_state( + &mut self, + component_address: ComponentAddress, + ) -> Result { + let encoder = AddressBech32Encoder::new(&self.network_definition); + let encoded_component_address = encoder + .encode(&component_address.as_node_id().0) + .expect("Can't fail!"); + + let request = StateEntityDetailsRequest { + at_ledger_state: None, + opt_ins: Some(Box::new(StateEntityDetailsOptIns { + ancestor_identities: Some(true), + component_royalty_vault_balance: Some(true), + package_royalty_vault_balance: Some(true), + non_fungible_include_nfids: Some(true), + explicit_metadata: None, + })), + addresses: vec![encoded_component_address], + aggregation_level: None, + }; + + let response = state_entity_details(&self.configuration, request) + .map_err(GatewayExecutorError::StateEntityDetailsError)?; + + let details = serde_json::from_value::< + sbor_json::scrypto::programmatic::value::ProgrammaticScryptoValue, + >( + response + .items + .first() + .unwrap() + .clone() + .details + .unwrap() + .get("state") + .unwrap() + .clone(), + ) + .unwrap(); + let encoded_details = + scrypto_encode(&details.to_scrypto_value()).unwrap(); + + scrypto_decode(&encoded_details) + .map_err(GatewayExecutorError::StateScryptoDecodeError) + } } fn native_public_key_to_gateway_public_key( @@ -292,10 +345,12 @@ pub enum GatewayExecutorError { GatewayStatusError(GatewayClientError), TransactionStatusError(GatewayClientError), TransactionPreviewError(GatewayClientError), + StateEntityDetailsError(GatewayClientError), TransactionCommittedDetailsError( GatewayClientError, ), TransactionSubmissionError(GatewayClientError), + StateScryptoDecodeError(DecodeError), AddressBech32mDecodeError, Timeout, } diff --git a/tools/publishing-tool/src/network_connection_provider/mainnet_simulator_connector.rs b/tools/publishing-tool/src/network_connection_provider/mainnet_simulator_connector.rs new file mode 100644 index 00000000..4cf6f3d5 --- /dev/null +++ b/tools/publishing-tool/src/network_connection_provider/mainnet_simulator_connector.rs @@ -0,0 +1,140 @@ +use super::*; +use crate::database_overlay::*; +use radix_engine::system::system_substates::*; +use radix_engine::transaction::*; +use radix_engine::vm::*; +use radix_engine_store_interface::db_key_mapper::*; +use scrypto_unit::*; +use state_manager::store::*; +use transaction::prelude::*; + +/// A [`NetworkConnectionProvider`] that simulates the transaction execution on +/// any network so long as it can access the state manager's database. The most +/// common use of this is to simulate the transactions on mainnet prior to their +/// submission to ensure that they're all valid. The underlying database remains +/// unchanged since an overlay is used. +pub struct SimulatorNetworkConnector<'s> { + /// The id of the network + network_definition: NetworkDefinition, + + /// The simulator that transactions will be running against. + ledger_simulator: TestRunner< + NoExtension, + UnmergeableSubstateDatabaseOverlay<'s, RocksDBStore>, + >, +} + +impl<'s> SimulatorNetworkConnector<'s> { + pub fn new( + database: &'s RocksDBStore, + network_definition: NetworkDefinition, + ) -> Self { + let database = UnmergeableSubstateDatabaseOverlay::new(database); + let test_runner = TestRunnerBuilder::new() + .with_custom_database(database) + .without_trace() + .build_without_bootstrapping(); + Self { + ledger_simulator: test_runner, + network_definition, + } + } +} + +impl<'s> NetworkConnectionProvider for SimulatorNetworkConnector<'s> { + type Error = MainnetSimulatorError; + + fn execute_transaction( + &mut self, + notarized_transaction: &NotarizedTransactionV1, + ) -> Result { + let raw_transaction = notarized_transaction.to_raw().map_err( + MainnetSimulatorError::NotarizedTransactionRawFormatError, + )?; + + let transaction_receipt = + self.ledger_simulator.execute_raw_transaction( + &self.network_definition, + &raw_transaction, + ); + + let execution_receipt = match transaction_receipt.result { + TransactionResult::Commit(CommitResult { + outcome: TransactionOutcome::Success(..), + state_update_summary, + .. + }) => ExecutionReceipt::CommitSuccess( + ExecutionReceiptSuccessContents { + new_entities: NewEntities { + new_component_addresses: state_update_summary + .new_components, + new_resource_addresses: state_update_summary + .new_resources, + new_package_addresses: state_update_summary + .new_packages, + }, + }, + ), + TransactionResult::Commit(CommitResult { + outcome: TransactionOutcome::Failure(reason), + .. + }) => ExecutionReceipt::CommitFailure { + reason: format!("{:?}", reason), + }, + TransactionResult::Reject(RejectResult { reason }) => { + ExecutionReceipt::Rejection { + reason: format!("{:?}", reason), + } + } + TransactionResult::Abort(AbortResult { reason }) => { + ExecutionReceipt::Abort { + reason: format!("{:?}", reason), + } + } + }; + Ok(execution_receipt) + } + + fn preview_transaction( + &mut self, + preview_intent: PreviewIntentV1, + ) -> Result { + self.ledger_simulator + .preview(preview_intent, &self.network_definition) + .map_err(MainnetSimulatorError::PreviewError) + } + + fn get_current_epoch(&mut self) -> Result { + Ok(self.ledger_simulator.get_current_epoch()) + } + + fn get_network_definition( + &mut self, + ) -> Result { + Ok(self.network_definition.clone()) + } + + fn read_component_state( + &mut self, + component_address: ComponentAddress, + ) -> Result { + self.ledger_simulator + .substate_db() + .get_mapped::>( + component_address.as_node_id(), + MAIN_BASE_PARTITION, + &SubstateKey::Field(ComponentField::State0.into()), + ) + .ok_or(MainnetSimulatorError::CantReadComponentState( + component_address, + )) + .map(|value| value.into_payload()) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum MainnetSimulatorError { + NotarizedTransactionRawFormatError(EncodeError), + PreviewError(PreviewError), + CantReadComponentState(ComponentAddress), +} diff --git a/tools/publishing-tool/src/network_connection_provider/mod.rs b/tools/publishing-tool/src/network_connection_provider/mod.rs new file mode 100644 index 00000000..57f372aa --- /dev/null +++ b/tools/publishing-tool/src/network_connection_provider/mod.rs @@ -0,0 +1,9 @@ +mod execution_service; +mod gateway_connector; +mod mainnet_simulator_connector; +mod traits; + +pub use execution_service::*; +pub use gateway_connector::*; +pub use mainnet_simulator_connector::*; +pub use traits::*; diff --git a/tools/publishing-tool/src/executor/traits.rs b/tools/publishing-tool/src/network_connection_provider/traits.rs similarity index 57% rename from tools/publishing-tool/src/executor/traits.rs rename to tools/publishing-tool/src/network_connection_provider/traits.rs index 6a674fa3..7a1cc9a8 100644 --- a/tools/publishing-tool/src/executor/traits.rs +++ b/tools/publishing-tool/src/network_connection_provider/traits.rs @@ -1,12 +1,14 @@ use radix_engine::transaction::TransactionReceiptV1; use transaction::prelude::*; -/// A trait that can be implemented by various structs to execute transactions -/// and produce execution receipts. The executor could be object doing the -/// execution itself in case of a node or could delegate the execution to -/// another object like in the case of the gateway. This detail does not matter -/// for the executor. -pub trait Executor { +/// A standardized interface for objects that provide connection to the network +/// regardless of how these objects are implemented and how they provide such +/// connection. One implementation could choose to provide network connection +/// through the core-api, another might do it over the gateway-api, and another +/// might talk directly to a node. The implementation details are abstracted +/// away in the interface. The interface has a number of getter functions and +/// functions for executing transactions. +pub trait NetworkConnectionProvider { type Error: Debug; fn execute_transaction( @@ -24,6 +26,11 @@ pub trait Executor { fn get_network_definition( &mut self, ) -> Result; + + fn read_component_state( + &mut self, + component_address: ComponentAddress, + ) -> Result; } /// A simplified transaction receipt containing the key pieces of information @@ -31,7 +38,7 @@ pub trait Executor { /// that the node can give us. #[derive(Clone, Debug, PartialEq, Eq, ScryptoSbor)] pub enum ExecutionReceipt { - CommitSuccess { new_entities: NewEntities }, + CommitSuccess(ExecutionReceiptSuccessContents), CommitFailure { reason: String }, Rejection { reason: String }, Abort { reason: String }, @@ -43,3 +50,8 @@ pub struct NewEntities { pub new_resource_addresses: IndexSet, pub new_package_addresses: IndexSet, } + +#[derive(Clone, Debug, PartialEq, Eq, ScryptoSbor)] +pub struct ExecutionReceiptSuccessContents { + pub new_entities: NewEntities, +} diff --git a/tools/publishing-tool/src/publishing/configuration.rs b/tools/publishing-tool/src/publishing/configuration.rs new file mode 100644 index 00000000..9125fe9a --- /dev/null +++ b/tools/publishing-tool/src/publishing/configuration.rs @@ -0,0 +1,254 @@ +use super::macros::*; +use common::prelude::*; +use macro_rules_attribute::apply; +use radix_engine::prelude::*; +use transaction::prelude::*; + +pub struct PublishingConfiguration { + /// The configuration of the Ignition protocol. + pub protocol_configuration: ProtocolConfiguration, + + /// The metadata to use for the dapp definition that is created. + pub dapp_definition_metadata: IndexMap, + + /// Contains configurations for the transactions that will be submitted + /// such as the notary and the account to get the fees from. Information + /// that mostly pertains to signing. + pub transaction_configuration: TransactionConfiguration, + + /// Contains information on the various badges to use for publishing and + /// whether these badges already exist or should be created. + pub badges: BadgeIndexedData, + + /// Contains information on the user resources that this deployment will use + /// such as their addresses or information about their properties if they're + /// to be created during the publishing process. + pub user_resources: UserResourceIndexedData, + + /// Contains information on how each of the packages should be handled and + /// whether they should be compiled and published or if pre-existing ones + /// should be used. + pub packages: Entities, + + /// Information about the exchange will be supported in Ignition. This + /// contains information necessary for the publishing and bootstrapping + /// process of Ignition. As an example, the address of the exchange's + /// package, the name of the blueprint, and the pools that we wish to + /// support. This uses an [`Option`] to allow for cases when there are + /// some networks where these exchanges are not live and therefore their + /// information can't be provided as part of publishing. + pub exchange_information: ExchangeIndexedData< + Option>, + >, + + /// Additional information that doesn't quite fit into any of the above + /// categories nicely. + pub additional_information: AdditionalInformation, +} + +#[derive(Debug, Clone, ScryptoSbor)] +pub struct PublishingReceipt { + pub packages: Entities, + pub components: Entities, + pub exchange_information: ExchangeIndexedData< + Option>, + >, + pub protocol_configuration: ProtocolConfigurationReceipt, +} + +#[derive(Debug, Clone, ScryptoSbor)] +pub struct ProtocolConfigurationReceipt { + pub protocol_resource: ResourceAddress, + pub user_resource_volatility: UserResourceIndexedData, + pub reward_rates: IndexMap, + pub allow_opening_liquidity_positions: bool, + pub allow_closing_liquidity_positions: bool, + pub maximum_allowed_price_staleness: i64, + pub maximum_allowed_price_difference_percentage: Decimal, + pub user_resources: UserResourceIndexedData, + pub registered_pools: + ExchangeIndexedData>>, +} + +pub struct AdditionalInformation { + pub ociswap_v2_registry_component: Option, +} + +pub struct ProtocolConfiguration { + pub protocol_resource: ResourceAddress, + pub user_resource_volatility: UserResourceIndexedData, + pub reward_rates: IndexMap, + pub allow_opening_liquidity_positions: bool, + pub allow_closing_liquidity_positions: bool, + pub maximum_allowed_price_staleness: i64, + pub maximum_allowed_price_difference_percentage: Decimal, + pub entities_metadata: Entities, +} + +pub struct TransactionConfiguration { + pub notary: PrivateKey, + pub fee_payer_information: AccountAndControllingKey, +} + +pub struct AccountAndControllingKey { + pub account_address: ComponentAddress, + pub controlling_key: PrivateKey, +} + +impl AccountAndControllingKey { + pub fn new_virtual_account(controlling_key: PrivateKey) -> Self { + let account_address = ComponentAddress::virtual_account_from_public_key( + &controlling_key.public_key(), + ); + Self { + account_address, + controlling_key, + } + } +} + +#[derive( + Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, ScryptoSbor, +)] +pub struct Entities { + pub protocol_entities: ProtocolIndexedData, + pub exchange_adapter_entities: ExchangeIndexedData, +} + +#[apply(name_indexed_struct)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, ScryptoSbor, +)] +pub struct ProtocolIndexedData { + pub ignition: T, + pub simple_oracle: T, +} + +#[apply(name_indexed_struct)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, ScryptoSbor, +)] +pub struct ExchangeIndexedData { + pub ociswap_v2: T, + pub defiplaza_v2: T, + pub caviarnine_v1: T, +} + +#[apply(name_indexed_struct)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, ScryptoSbor, +)] +pub struct UserResourceIndexedData { + pub bitcoin: T, + pub ethereum: T, + pub usdc: T, + pub usdt: T, +} + +#[apply(name_indexed_struct)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, ScryptoSbor, +)] +pub struct BadgeIndexedData { + pub oracle_manager_badge: T, + pub protocol_owner_badge: T, + pub protocol_manager_badge: T, +} + +pub enum BadgeHandling { + /// Creates a new badge and deposits it into the specified account. + CreateAndSend { + /// The account that the badges should be sent to. + account_address: ComponentAddress, + /// The metadata of the created badges. + metadata_init: MetadataInit, + }, + /// Use an existing badge that exists in some account. If the badge is + /// required in one of the operations then a proof of it will be created. + /// A signature of this account must be provided. + UseExisting { + /// The private key of the account that controlling the badge. This is + /// required for any proofs that need to be created. + controlling_private_key: PrivateKey, + /// The address of the holder + holder_account_address: ComponentAddress, + /// The address of the badge + badge_resource_address: ResourceAddress, + }, +} + +#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)] +pub struct ExchangeInformation { + /// The id of the pool blueprint of the exchange. + pub blueprint_id: BlueprintId, + /// The pools that we wish to support for the exchange. + pub pools: UserResourceIndexedData

, + /// The liquidity receipt to use for the exchange. + pub liquidity_receipt: R, +} + +#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)] +pub enum PackageHandling { + /// The package should be compiled and published in the process. + LoadAndPublish { + /// The name of the crate that contains the package. This is the name + /// that will be used when instructing the package loader to get the + /// package. + crate_package_name: String, + /// The initial metadata to set on the package when it's being published + metadata: MetadataInit, + /// The name of the blueprint to use from this package. This is under + /// the assumption that each package is just a single blueprint. + blueprint_name: String, + }, + /// The package already exists on the desired network. + UseExisting { + /// The address of the package on the network and + package_address: BlueprintId, + }, +} + +#[derive( + Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, ScryptoSbor, +)] +pub enum PoolHandling { + /// A pool does not exist and should be created. + Create, + /// A pool already exists and should be used. + UseExisting { + /// The address of the pool to use + pool_address: ComponentAddress, + }, +} + +#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)] +pub enum UserResourceHandling { + /// Resources do not exist on the network and should be created + CreateFreelyMintableAndBurnable { + /// The divisibility to create the resource with + divisibility: u8, + /// The initial metadata to use for the resource + metadata: MetadataInit, + }, + /// Resources exist on the network and should be used. + UseExisting { + /// The address of the resource + resource_address: ResourceAddress, + }, +} + +#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)] +pub enum LiquidityReceiptHandling { + /// Create a new resource to use as the liquidity receipt + CreateNew { + /// The non-fungible data schema of the resource. + non_fungible_schema: NonFungibleDataSchema, + /// The initial metadata to use for the resource. + metadata: MetadataInit, + }, + /// Use an existing resource as the liquidity receipt of the exchange + UseExisting { + /// The address of the liquidity receipt resource + resource_address: ResourceAddress, + }, +} diff --git a/tools/publishing-tool/src/publishing/error.rs b/tools/publishing-tool/src/publishing/error.rs new file mode 100644 index 00000000..3d75c1eb --- /dev/null +++ b/tools/publishing-tool/src/publishing/error.rs @@ -0,0 +1,4 @@ +#[derive(Clone, Debug)] +pub struct KeyNotFound { + pub key: String, +} diff --git a/tools/publishing-tool/src/publishing/handler.rs b/tools/publishing-tool/src/publishing/handler.rs new file mode 100644 index 00000000..769af58e --- /dev/null +++ b/tools/publishing-tool/src/publishing/handler.rs @@ -0,0 +1,1412 @@ +#![allow(clippy::arithmetic_side_effects, clippy::too_many_arguments)] + +use defiplaza_v2_adapter_v1::*; +use ignition::{InitializationParametersManifest, PoolBlueprintInformation}; +use itertools::*; +use package_loader::*; +use radix_engine::blueprints::package::*; +use radix_engine::types::node_modules::*; +use radix_engine_interface::blueprints::account::*; +use rand::prelude::*; +use transaction::prelude::*; + +use super::*; +use crate::network_connection_provider::*; + +pub fn publish( + configuration: &PublishingConfiguration, + network_provider: &mut N, +) -> Result< + PublishingReceipt, + PublishingError<::Error>, +> { + // A cryptographically secure random number generator. + let mut std_rng = rand::rngs::StdRng::from_entropy(); + + // Creating an ephemeral private key to use for the publishing process. This + // key will be mapped to an account that may store things during the process + // but will ultimately be discarded in the end. + let ephemeral_key_u64 = std_rng.next_u64(); + let ephemeral_private_key = PrivateKey::Ed25519( + Ed25519PrivateKey::from_u64(ephemeral_key_u64).unwrap(), + ); + let ephemeral_account = ComponentAddress::virtual_account_from_public_key( + &ephemeral_private_key.public_key(), + ); + log::info!("Ephemeral private key selected: {}", ephemeral_key_u64); + + // Finding the set of private keys to use for the signatures. This will be + // the notary, the fee payer, and all of the private keys that control the + // accounts with the badges. + let mut signer_private_keys = vec![ + &configuration.transaction_configuration.notary, + &ephemeral_private_key, + &configuration + .transaction_configuration + .fee_payer_information + .controlling_key, + ]; + + for badge_handling in configuration.badges.iter() { + if let BadgeHandling::UseExisting { + controlling_private_key, + .. + } = badge_handling + { + signer_private_keys.push(controlling_private_key) + } + } + + // Creating an execution service from the passed executor + let mut execution_service = ExecutionService::new( + network_provider, + configuration + .transaction_configuration + .fee_payer_information + .account_address, + &configuration.transaction_configuration.notary, + &signer_private_keys, + ); + + // Creating the dApp definition account. The owner role will be set to the + // ephemeral private key and then switched to the protocol owner and manager + // at the end + let dapp_definition_account = { + let manifest = ManifestBuilder::new() + .allocate_global_address( + ACCOUNT_PACKAGE, + ACCOUNT_BLUEPRINT, + "reservation", + "named_address", + ) + .then(|builder| { + let reservation = builder.address_reservation("reservation"); + let named_address = builder.named_address("named_address"); + + let mut builder = builder + .call_function( + ACCOUNT_PACKAGE, + ACCOUNT_BLUEPRINT, + ACCOUNT_CREATE_ADVANCED_IDENT, + AccountCreateAdvancedManifestInput { + address_reservation: Some(reservation), + owner_role: OwnerRole::Updatable(rule!(require( + NonFungibleGlobalId::from_public_key( + &ephemeral_private_key.public_key() + ) + ))), + }, + ) + .call_metadata_method( + named_address, + METADATA_SET_IDENT, + MetadataSetInput { + key: "account_type".to_owned(), + value: MetadataValue::String( + "dapp definition".to_owned(), + ), + }, + ) + .call_metadata_method( + named_address, + METADATA_SET_IDENT, + MetadataSetInput { + key: "claimed_websites".to_owned(), + value: MetadataValue::OriginArray(vec![]), + }, + ) + .call_metadata_method( + named_address, + METADATA_SET_IDENT, + MetadataSetInput { + key: "dapp_definitions".to_owned(), + value: MetadataValue::GlobalAddressArray(vec![]), + }, + ); + + for (key, value) in + configuration.dapp_definition_metadata.iter() + { + builder = builder.call_metadata_method( + named_address, + METADATA_SET_IDENT, + MetadataSetInput { + key: key.to_owned(), + value: value.clone(), + }, + ) + } + + builder + }) + .build(); + + execution_service + .execute_manifest(manifest.clone())? + .new_entities + .new_component_addresses + .first() + .copied() + .expect("Must succeed!") + }; + + // Handling the creation of the user resources if they need to be created. + let resolved_user_resources = { + let user_resources_map = configuration.user_resources.into_map(); + + let user_resources_already_created = + user_resources_map.iter().flat_map(|(key, handling)| { + if let UserResourceHandling::UseExisting { resource_address } = + handling + { + Some((*key, resource_address)) + } else { + None + } + }); + let user_resources_requiring_creation = user_resources_map + .iter() + .flat_map(|(key, handling)| { + if let UserResourceHandling::CreateFreelyMintableAndBurnable { + divisibility, + metadata, + } = handling + { + Some((*key, (divisibility, metadata))) + } else { + None + } + }) + .collect::>(); + + // Construct a manifest that creates the user resources. + let manifest = TransactionManifestV1 { + instructions: user_resources_requiring_creation + .values() + .map(|(divisibility, metadata)| InstructionV1::CallFunction { + package_address: RESOURCE_PACKAGE.into(), + blueprint_name: FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT + .to_string(), + function_name: FUNGIBLE_RESOURCE_MANAGER_CREATE_IDENT + .to_owned(), + args: to_manifest_value( + &FungibleResourceManagerCreateManifestInput { + owner_role: OwnerRole::None, + track_total_supply: true, + divisibility: **divisibility, + resource_roles: FungibleResourceRoles { + mint_roles: mint_roles! { + minter => rule!(allow_all); + minter_updater => rule!(deny_all); + }, + burn_roles: burn_roles! { + burner => rule!(allow_all); + burner_updater => rule!(deny_all); + }, + ..Default::default() + }, + metadata: ModuleConfig { + init: (*metadata).clone(), + roles: Default::default(), + }, + address_reservation: None, + }, + ) + .expect("Can't fail!"), + }) + .collect::>(), + blobs: Default::default(), + }; + let resource_addresses = execution_service + .execute_manifest(manifest)? + .new_entities + .new_resource_addresses; + + UserResourceIndexedData::from_map( + user_resources_already_created + .map(|(key, address)| (key, *address)) + .chain( + user_resources_requiring_creation + .iter() + .map(|value| *value.0) + .zip(resource_addresses), + ), + ) + .expect("Can't fail!") + }; + + // Handling the badge creation that is needed. + let resolved_badges = { + let already_existing_badges = + configuration.badges.into_map().into_iter().filter_map( + |(key, value)| { + if let BadgeHandling::UseExisting { + holder_account_address, + badge_resource_address, + .. + } = value + { + Some(( + key, + (*holder_account_address, *badge_resource_address), + )) + } else { + None + } + }, + ); + + let badges_requiring_creation = configuration + .badges + .into_map() + .into_iter() + .filter_map(|(key, value)| { + if let BadgeHandling::CreateAndSend { metadata_init, .. } = + value + { + Some((key, metadata_init)) + } else { + None + } + }); + + let mut manifest_builder = ManifestBuilder::new(); + let mut keys = vec![]; + for (key, metadata_init) in badges_requiring_creation { + let mut metadata_init = metadata_init.clone(); + metadata_init.data.insert( + "dapp_definitions".to_owned(), + KeyValueStoreInitEntry { + value: Some(MetadataValue::GlobalAddressArray(vec![ + dapp_definition_account.into(), + ])), + lock: false, + }, + ); + + keys.push(key); + manifest_builder = manifest_builder.create_fungible_resource( + OwnerRole::Updatable(rule!(require( + NonFungibleGlobalId::from_public_key( + &ephemeral_private_key.public_key() + ) + ))), + true, + 0, + FungibleResourceRoles { + mint_roles: mint_roles! { + minter => rule!(deny_all); + minter_updater => rule!(deny_all); + }, + burn_roles: burn_roles! { + burner => rule!(deny_all); + burner_updater => rule!(deny_all); + }, + freeze_roles: freeze_roles! { + freezer => rule!(deny_all); + freezer_updater => rule!(deny_all); + }, + recall_roles: recall_roles! { + recaller => rule!(deny_all); + recaller_updater => rule!(deny_all); + }, + withdraw_roles: withdraw_roles! { + withdrawer => rule!(allow_all); + withdrawer_updater => rule!(deny_all); + }, + deposit_roles: deposit_roles! { + depositor => rule!(allow_all); + depositor_updater => rule!(deny_all); + }, + }, + ModuleConfig { + roles: Default::default(), + init: metadata_init, + }, + Some(dec!(1)), + ) + } + let manifest = manifest_builder + .try_deposit_entire_worktop_or_abort(ephemeral_account, None) + .build(); + let badges = keys + .into_iter() + .zip( + execution_service + .execute_manifest(manifest.clone())? + .new_entities + .new_resource_addresses, + ) + .map(|(key, resource_address)| { + (key, (ephemeral_account, resource_address)) + }); + + BadgeIndexedData::from_map(already_existing_badges.chain(badges)) + .expect("Can't fail") + }; + + let resolved_rules = resolved_badges + .map(|(_, resource_address)| rule!(require(*resource_address))); + + // The resources created in the previous transaction have an updatable owner + // role that is set to the ephemeral private key. In this transaction the + // owner role is modified to be the protocol owner. + { + let mut manifest_builder = ManifestBuilder::new(); + for ((_, address), handling) in + resolved_badges.zip_borrowed(&configuration.badges).iter() + { + if let BadgeHandling::CreateAndSend { .. } = handling { + manifest_builder = manifest_builder + .create_proof_from_account_of_amount( + resolved_badges.protocol_owner_badge.0, + resolved_badges.protocol_owner_badge.1, + dec!(1), + ) + .set_owner_role( + *address, + resolved_rules.protocol_owner_badge.clone(), + ) + .lock_owner_role(*address); + } + } + let manifest = manifest_builder.build(); + + execution_service.execute_manifest(manifest.clone())?; + } + + // Publishing the packages that need to be published + let resolved_blueprint_ids = { + let mut map = configuration.packages.protocol_entities.into_map(); + map.extend(configuration.packages.exchange_adapter_entities.into_map()); + + let iterator = map + .into_iter() + .filter_map(|(key, package_handling)| { + if let PackageHandling::LoadAndPublish { + crate_package_name, + metadata, + blueprint_name, + } = package_handling + { + Some((key, (crate_package_name, metadata, blueprint_name))) + } else { + None + } + }) + .map(|(key, (crate_package_name, metadata, blueprint_name))| { + let (code, definition) = PackageLoader::get(crate_package_name); + + let mut metadata = metadata.clone(); + metadata.data.insert( + "dapp_definition".to_owned(), + KeyValueStoreInitEntry { + value: Some(MetadataValue::GlobalAddress( + dapp_definition_account.into(), + )), + lock: false, + }, + ); + + (key, (code, definition, metadata, blueprint_name.clone())) + }) + .sorted_by(|x, y| x.1 .0.len().cmp(&y.1 .0.len())); + + // We want to get as many packages into one transaction. Goal is to + // have each transaction be 980 kbs or less in size. If the addition + // of a package increases the size beyond that then it goes in the + // next batch. + let mut batches = vec![Vec::<( + String, + (Vec, PackageDefinition, MetadataInit, String), + )>::new()]; + for (key, (code, definition, metadata_init, blueprint_name)) in iterator + { + let latest_batch = batches.last_mut().expect("Impossible!"); + let total_code_size = latest_batch + .iter() + .map(|entry| entry.1 .0.len()) + .sum::(); + + let size_if_code_is_added_to_batch = total_code_size + code.len(); + // Add to next batch + if size_if_code_is_added_to_batch > 980 * 1024 { + batches.push(vec![( + key.to_owned(), + (code, definition, metadata_init, blueprint_name), + )]) + } + // Add to this batch + else { + latest_batch.push(( + key.to_owned(), + (code, definition, metadata_init, blueprint_name), + )); + } + } + + // Creating transactions of the batches + let mut addresses_map = IndexMap::::new(); + for batch in batches { + let mut manifest_builder = ManifestBuilder::new(); + for (_, (code, definition, metadata, _)) in batch.iter() { + manifest_builder = manifest_builder.publish_package_advanced( + None, + code.clone(), + definition.clone(), + metadata.clone(), + OwnerRole::Fixed( + resolved_rules.protocol_owner_badge.clone(), + ), + ); + } + let manifest = manifest_builder.build(); + + addresses_map.extend( + execution_service + .execute_manifest(manifest.clone())? + .new_entities + .new_package_addresses + .into_iter() + .zip(batch.into_iter()) + .map( + |( + package_address, + (key, (_, _, _, blueprint_name)), + )| { + ( + key, + BlueprintId { + package_address, + blueprint_name, + }, + ) + }, + ), + ); + } + + let addresses_map = configuration + .packages + .protocol_entities + .into_map() + .into_iter() + .filter_map(|(key, value)| { + if let PackageHandling::UseExisting { package_address } = value + { + Some((key.to_owned(), package_address.clone())) + } else { + None + } + }) + .chain(addresses_map) + .collect::>(); + + Entities { + protocol_entities: ProtocolIndexedData::from_map( + addresses_map.clone(), + ) + .expect("Can't fail!"), + exchange_adapter_entities: ExchangeIndexedData::from_map( + addresses_map, + ) + .expect("Can't fail!"), + } + }; + + // Computing the package global caller + let resolved_package_global_caller_rules = Entities { + protocol_entities: resolved_blueprint_ids.protocol_entities.map( + |blueprint_id| { + rule!(require(package_of_direct_caller( + blueprint_id.package_address + ))) + }, + ), + exchange_adapter_entities: resolved_blueprint_ids + .exchange_adapter_entities + .map(|blueprint_id| { + rule!(require(package_of_direct_caller( + blueprint_id.package_address + ))) + }), + }; + + let resolved_exchange_data = ExchangeIndexedData { + ociswap_v2: handle_ociswap_v2_exchange_information( + &mut execution_service, + configuration.exchange_information.ociswap_v2.as_ref(), + dapp_definition_account, + &resolved_rules, + &resolved_package_global_caller_rules, + &resolved_user_resources, + configuration.protocol_configuration.protocol_resource, + &configuration.additional_information, + )?, + defiplaza_v2: handle_defiplaza_v2_exchange_information( + &mut execution_service, + configuration.exchange_information.defiplaza_v2.as_ref(), + dapp_definition_account, + &resolved_rules, + &resolved_package_global_caller_rules, + &resolved_user_resources, + configuration.protocol_configuration.protocol_resource, + )?, + caviarnine_v1: handle_caviarnine_v1_exchange_information( + &mut execution_service, + configuration.exchange_information.caviarnine_v1.as_ref(), + dapp_definition_account, + &resolved_rules, + &resolved_package_global_caller_rules, + &resolved_user_resources, + configuration.protocol_configuration.protocol_resource, + )?, + }; + + // Creating the adapter components of the various exchange packages that we + // published. + let resolved_adapter_component_addresses = { + let adapter_instantiation_instructions = resolved_blueprint_ids + .exchange_adapter_entities + .clone() + .zip( + configuration + .protocol_configuration + .entities_metadata + .exchange_adapter_entities + .clone(), + ) + .map(|(adapter_package, metadata_init)| { + let mut metadata_init = metadata_init.clone(); + metadata_init.data.insert( + "dapp_definition".to_owned(), + KeyValueStoreInitEntry { + value: Some(MetadataValue::GlobalAddress( + dapp_definition_account.into(), + )), + lock: false, + }, + ); + + InstructionV1::CallFunction { + package_address: adapter_package.package_address.into(), + blueprint_name: adapter_package.blueprint_name.clone(), + function_name: "instantiate".to_owned(), + args: to_manifest_value(&( + resolved_rules.protocol_manager_badge.clone(), + resolved_rules.protocol_owner_badge.clone(), + metadata_init, + OwnerRole::Fixed( + resolved_rules.protocol_owner_badge.clone(), + ), + None::, + )) + .expect("Impossible!"), + } + }); + + let manifest = TransactionManifestV1 { + instructions: adapter_instantiation_instructions + .iter() + .cloned() + .collect(), + blobs: Default::default(), + }; + + ExchangeIndexedData::from_map( + adapter_instantiation_instructions + .into_map() + .into_iter() + .zip( + execution_service + .execute_manifest(manifest)? + .new_entities + .new_component_addresses, + ) + .map(|((key, _), component_address)| (key, component_address)), + ) + .expect("Cant fail!") + }; + + // Instantiating the oracle component + let oracle_component_address = { + let mut metadata_init = configuration + .protocol_configuration + .entities_metadata + .protocol_entities + .simple_oracle + .clone(); + + metadata_init.data.insert( + "dapp_definition".to_owned(), + KeyValueStoreInitEntry { + value: Some(MetadataValue::GlobalAddress( + dapp_definition_account.into(), + )), + lock: false, + }, + ); + + let manifest = ManifestBuilder::new() + .call_function( + resolved_blueprint_ids + .protocol_entities + .simple_oracle + .package_address, + resolved_blueprint_ids + .protocol_entities + .simple_oracle + .blueprint_name + .clone(), + "instantiate", + ( + resolved_rules.oracle_manager_badge.clone(), + metadata_init, + OwnerRole::Fixed( + resolved_rules.protocol_owner_badge.clone(), + ), + None::, + ), + ) + .build(); + + execution_service + .execute_manifest(manifest)? + .new_entities + .new_component_addresses + .first() + .copied() + .unwrap() + }; + + // Instantiating the Ignition component + let ignition_component_address = { + let mut metadata_init = configuration + .protocol_configuration + .entities_metadata + .protocol_entities + .ignition + .clone(); + + metadata_init.data.insert( + "dapp_definition".to_owned(), + KeyValueStoreInitEntry { + value: Some(MetadataValue::GlobalAddress( + dapp_definition_account.into(), + )), + lock: false, + }, + ); + + let ignition_initialization_parameters = + InitializationParametersManifest { + initial_pool_information: Some( + resolved_exchange_data + .clone() + .zip(resolved_adapter_component_addresses) + .iter() + .filter_map( + |(exchange_information, adapter_component)| { + exchange_information.as_ref().map( + |exchange_information| { + ( + exchange_information + .blueprint_id + .clone(), + PoolBlueprintInformation { + adapter: *adapter_component, + allowed_pools: + exchange_information + .pools + .iter() + .copied() + .collect(), + liquidity_receipt: + exchange_information + .liquidity_receipt, + }, + ) + }, + ) + }, + ) + .collect(), + ), + initial_user_resource_volatility: Some( + resolved_user_resources + .zip( + configuration + .protocol_configuration + .user_resource_volatility, + ) + .iter() + .map(|(address, volatility)| (*address, *volatility)) + .collect(), + ), + initial_reward_rates: Some( + configuration.protocol_configuration.reward_rates.clone(), + ), + initial_volatile_protocol_resources: None, + initial_non_volatile_protocol_resources: None, + initial_is_open_position_enabled: Some( + configuration + .protocol_configuration + .allow_opening_liquidity_positions, + ), + initial_is_close_position_enabled: Some( + configuration + .protocol_configuration + .allow_closing_liquidity_positions, + ), + }; + + let manifest = ManifestBuilder::new() + .call_function( + resolved_blueprint_ids + .protocol_entities + .ignition + .package_address, + resolved_blueprint_ids + .protocol_entities + .ignition + .blueprint_name + .clone(), + "instantiate", + ( + metadata_init, + OwnerRole::Fixed( + resolved_rules.protocol_owner_badge.clone(), + ), + resolved_rules.protocol_owner_badge.clone(), + resolved_rules.protocol_manager_badge.clone(), + configuration.protocol_configuration.protocol_resource, + oracle_component_address, + configuration + .protocol_configuration + .maximum_allowed_price_staleness, + configuration + .protocol_configuration + .maximum_allowed_price_difference_percentage, + ignition_initialization_parameters, + None::, + ), + ) + .build(); + execution_service + .execute_manifest(manifest)? + .new_entities + .new_component_addresses + .first() + .copied() + .unwrap() + }; + + let resolved_entity_component_addresses = Entities { + protocol_entities: ProtocolIndexedData { + ignition: ignition_component_address, + simple_oracle: oracle_component_address, + }, + exchange_adapter_entities: resolved_adapter_component_addresses, + }; + + // Submitting the defiplaza pool pair config to the adapter + { + if let Some(ref defiplaza_v2_exchange_information) = + resolved_exchange_data.defiplaza_v2 + { + let pair_config = execution_service + .with_network_connection_provider(|provider| { + defiplaza_v2_exchange_information.pools.try_map(|address| { + provider.read_component_state::(*address) + }) + })?; + + let pair_config_map = defiplaza_v2_exchange_information + .pools + .zip(pair_config) + .iter() + .map(|(pool_address, plaza_pair)| { + (*pool_address, plaza_pair.config) + }) + .collect::>(); + + let manifest = ManifestBuilder::new() + .create_proof_from_account_of_amount( + resolved_badges.protocol_manager_badge.0, + resolved_badges.protocol_manager_badge.1, + dec!(1), + ) + .call_method( + resolved_adapter_component_addresses.defiplaza_v2, + "add_pair_config", + (pair_config_map,), + ) + .build(); + execution_service.execute_manifest(manifest)?; + } + } + + // Caching the information of the Caviarnine pools + { + if let Some(ExchangeInformation { pools, .. }) = + resolved_exchange_data.caviarnine_v1 + { + let instructions = pools + .iter() + .map(|address| InstructionV1::CallMethod { + address: resolved_adapter_component_addresses + .caviarnine_v1 + .into(), + method_name: "preload_pool_information".to_owned(), + args: to_manifest_value(&(*address,)).expect("Can't fail!"), + }) + .collect::>(); + let manifest = TransactionManifestV1 { + instructions, + blobs: Default::default(), + }; + execution_service.execute_manifest(manifest)?; + } + } + + // Setting the dApp definition metadata + { + let claimed_entities = resolved_badges + .iter() + .map(|(_, address)| GlobalAddress::from(*address)) + .chain(resolved_blueprint_ids.exchange_adapter_entities.iter().map( + |blueprint_id| { + GlobalAddress::from(blueprint_id.package_address) + }, + )) + .chain(resolved_blueprint_ids.protocol_entities.iter().map( + |blueprint_id| { + GlobalAddress::from(blueprint_id.package_address) + }, + )) + .chain(resolved_exchange_data.iter().filter_map(|information| { + information.as_ref().map(|information| { + GlobalAddress::from(information.liquidity_receipt) + }) + })) + .chain( + resolved_entity_component_addresses + .exchange_adapter_entities + .iter() + .map(|component_address| { + GlobalAddress::from(*component_address) + }), + ) + .chain( + resolved_entity_component_addresses + .protocol_entities + .iter() + .map(|component_address| { + GlobalAddress::from(*component_address) + }), + ) + .collect::>(); + + let manifest = ManifestBuilder::new() + .create_proof_from_account_of_amount( + resolved_badges.protocol_owner_badge.0, + resolved_badges.protocol_owner_badge.1, + dec!(1), + ) + .set_metadata( + dapp_definition_account, + "claimed_entities", + claimed_entities, + ) + .set_owner_role( + dapp_definition_account, + resolved_rules.protocol_owner_badge.clone(), + ) + .lock_owner_role(dapp_definition_account) + .build(); + execution_service.execute_manifest(manifest)?; + } + + // Depositing the created badges into their accounts. + { + let mut manifest_builder = ManifestBuilder::new(); + for ((current_holder_address, resource_address), handling) in + resolved_badges.zip_borrowed(&configuration.badges).iter() + { + if let BadgeHandling::CreateAndSend { + account_address: destination_account_address, + .. + } = handling + { + manifest_builder = manifest_builder + .withdraw_from_account( + *current_holder_address, + *resource_address, + dec!(1), + ) + .try_deposit_entire_worktop_or_abort( + *destination_account_address, + None, + ) + } + } + let manifest = manifest_builder.build(); + execution_service.execute_manifest(manifest)?; + } + + Ok(PublishingReceipt { + packages: Entities { + protocol_entities: resolved_blueprint_ids + .protocol_entities + .map(|blueprint_id| blueprint_id.package_address), + exchange_adapter_entities: resolved_blueprint_ids + .exchange_adapter_entities + .map(|blueprint_id| blueprint_id.package_address), + }, + components: resolved_entity_component_addresses, + exchange_information: resolved_exchange_data.clone(), + protocol_configuration: ProtocolConfigurationReceipt { + protocol_resource: configuration + .protocol_configuration + .protocol_resource, + user_resource_volatility: configuration + .protocol_configuration + .user_resource_volatility, + reward_rates: configuration + .protocol_configuration + .reward_rates + .clone(), + allow_opening_liquidity_positions: configuration + .protocol_configuration + .allow_opening_liquidity_positions, + allow_closing_liquidity_positions: configuration + .protocol_configuration + .allow_closing_liquidity_positions, + maximum_allowed_price_staleness: configuration + .protocol_configuration + .maximum_allowed_price_staleness, + maximum_allowed_price_difference_percentage: configuration + .protocol_configuration + .maximum_allowed_price_difference_percentage, + user_resources: resolved_user_resources, + registered_pools: resolved_exchange_data.map(|information| { + information.as_ref().map(|information| information.pools) + }), + }, + }) +} + +fn handle_ociswap_v2_exchange_information( + execution_service: &mut ExecutionService, + exchange_information: Option< + &ExchangeInformation, + >, + dapp_definition: ComponentAddress, + badge_rules: &BadgeIndexedData, + entity_package_caller_rules: &Entities, + user_resources: &UserResourceIndexedData, + protocol_resource: ResourceAddress, + additional_information: &AdditionalInformation, +) -> Result< + Option>, + ExecutionServiceError<::Error>, +> { + // No ociswap registry component is passed even through it is needed. + let AdditionalInformation { + ociswap_v2_registry_component: Some(ociswap_v2_registry_component), + } = additional_information + else { + return Ok(None); + }; + + match exchange_information { + Some(exchange_information) => { + // Create the liquidity receipt if it needs to be created. + let liquidity_receipt = match exchange_information.liquidity_receipt + { + LiquidityReceiptHandling::CreateNew { + ref non_fungible_schema, + ref metadata, + } => handle_liquidity_receipt_creation( + execution_service, + non_fungible_schema, + metadata, + dapp_definition, + badge_rules, + entity_package_caller_rules, + )?, + LiquidityReceiptHandling::UseExisting { resource_address } => { + resource_address + } + }; + + // Creating the liquidity pools that need to be created + let pools = + exchange_information.pools.zip(*user_resources).try_map( + |(pool_handling, user_resource_address)| -> Result< + ComponentAddress, + ExecutionServiceError< + ::Error, + >, + > { + let (resource_x, resource_y) = + if *user_resource_address > protocol_resource { + (protocol_resource, *user_resource_address) + } else { + (*user_resource_address, protocol_resource) + }; + + match pool_handling { + PoolHandling::Create => { + let manifest = ManifestBuilder::new() + .call_function( + exchange_information + .blueprint_id + .package_address, + exchange_information + .blueprint_id + .blueprint_name + .clone(), + "instantiate", + ( + resource_x, + resource_y, + pdec!(1.4142135624), + dec!(0.01), + dec!(0.009), + ociswap_v2_registry_component, + Vec::<( + ComponentAddress, + ManifestBucket, + )>::new( + ), + // TODO: Specify their dapp definition? + FAUCET, + ), + ) + .build(); + + Ok(execution_service + .execute_manifest(manifest)? + .new_entities + .new_component_addresses + .first() + .copied() + .unwrap()) + } + PoolHandling::UseExisting { pool_address } => { + Ok(*pool_address) + } + } + }, + )?; + + Ok(Some(ExchangeInformation { + blueprint_id: exchange_information.blueprint_id.clone(), + pools, + liquidity_receipt, + })) + } + None => Ok(None), + } +} + +fn handle_defiplaza_v2_exchange_information( + execution_service: &mut ExecutionService, + exchange_information: Option< + &ExchangeInformation, + >, + dapp_definition: ComponentAddress, + badge_rules: &BadgeIndexedData, + entity_package_caller_rules: &Entities, + user_resources: &UserResourceIndexedData, + protocol_resource: ResourceAddress, +) -> Result< + Option>, + ExecutionServiceError<::Error>, +> { + match exchange_information { + Some(exchange_information) => { + // Create the liquidity receipt if it needs to be created. + let liquidity_receipt = match exchange_information.liquidity_receipt + { + LiquidityReceiptHandling::CreateNew { + ref non_fungible_schema, + ref metadata, + } => handle_liquidity_receipt_creation( + execution_service, + non_fungible_schema, + metadata, + dapp_definition, + badge_rules, + entity_package_caller_rules, + )?, + LiquidityReceiptHandling::UseExisting { resource_address } => { + resource_address + } + }; + + // Creating the liquidity pools that need to be created + let pools = + exchange_information.pools.zip(*user_resources).try_map( + |(pool_handling, user_resource_address)| -> Result< + ComponentAddress, + ExecutionServiceError< + ::Error, + >, + > { + match pool_handling { + PoolHandling::Create => { + let manifest = ManifestBuilder::new() + .call_function( + exchange_information + .blueprint_id + .package_address, + exchange_information + .blueprint_id + .blueprint_name + .clone(), + "instantiate_pair", + ( + OwnerRole::None, + user_resource_address, + protocol_resource, + PairConfig { + k_in: dec!(1), + k_out: dec!(1.5), + fee: dec!(0.01), + decay_factor: dec!(0.9995), + }, + dec!(1), + ), + ) + .build(); + + Ok(execution_service + .execute_manifest(manifest)? + .new_entities + .new_component_addresses + .first() + .copied() + .unwrap()) + } + PoolHandling::UseExisting { pool_address } => { + Ok(*pool_address) + } + } + }, + )?; + + Ok(Some(ExchangeInformation { + blueprint_id: exchange_information.blueprint_id.clone(), + pools, + liquidity_receipt, + })) + } + None => Ok(None), + } +} + +fn handle_caviarnine_v1_exchange_information( + execution_service: &mut ExecutionService, + exchange_information: Option< + &ExchangeInformation, + >, + dapp_definition: ComponentAddress, + badge_rules: &BadgeIndexedData, + entity_package_caller_rules: &Entities, + user_resources: &UserResourceIndexedData, + protocol_resource: ResourceAddress, +) -> Result< + Option>, + ExecutionServiceError<::Error>, +> { + match exchange_information { + Some(exchange_information) => { + // Create the liquidity receipt if it needs to be created. + let liquidity_receipt = match exchange_information.liquidity_receipt + { + LiquidityReceiptHandling::CreateNew { + ref non_fungible_schema, + ref metadata, + } => handle_liquidity_receipt_creation( + execution_service, + non_fungible_schema, + metadata, + dapp_definition, + badge_rules, + entity_package_caller_rules, + )?, + LiquidityReceiptHandling::UseExisting { resource_address } => { + resource_address + } + }; + + // Creating the liquidity pools that need to be created + let pools = + exchange_information.pools.zip(*user_resources).try_map( + |(pool_handling, user_resource_address)| -> Result< + ComponentAddress, + ExecutionServiceError< + ::Error, + >, + > { + match pool_handling { + PoolHandling::Create => { + let manifest = ManifestBuilder::new() + .call_function( + exchange_information + .blueprint_id + .package_address, + exchange_information + .blueprint_id + .blueprint_name + .clone(), + "new", + ( + rule!(allow_all), + rule!(allow_all), + user_resource_address, + protocol_resource, + 100u32, + None::, + ), + ) + .build(); + + Ok(execution_service + .execute_manifest(manifest)? + .new_entities + .new_component_addresses + .first() + .copied() + .unwrap()) + } + PoolHandling::UseExisting { pool_address } => { + Ok(*pool_address) + } + } + }, + )?; + + Ok(Some(ExchangeInformation { + blueprint_id: exchange_information.blueprint_id.clone(), + pools, + liquidity_receipt, + })) + } + None => Ok(None), + } +} + +fn handle_liquidity_receipt_creation( + execution_service: &mut ExecutionService, + non_fungible_schema: &NonFungibleDataSchema, + metadata_init: &MetadataInit, + dapp_definition_account: ComponentAddress, + badge_rules: &BadgeIndexedData, + entity_package_caller_rules: &Entities, +) -> Result< + ResourceAddress, + ExecutionServiceError<::Error>, +> { + // Adding the dapp definition to the metadata + let mut metadata_init = metadata_init.clone(); + metadata_init.data.insert( + "dapp_definitions".to_owned(), + KeyValueStoreInitEntry { + value: Some(MetadataValue::GlobalAddressArray(vec![ + dapp_definition_account.into(), + ])), + lock: false, + }, + ); + + let manifest = ManifestBuilder::new() + .call_function( + RESOURCE_PACKAGE, + NON_FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT.to_owned(), + NON_FUNGIBLE_RESOURCE_MANAGER_CREATE_RUID_WITH_INITIAL_SUPPLY_IDENT.to_owned(), + NonFungibleResourceManagerCreateRuidWithInitialSupplyManifestInput { + owner_role: OwnerRole::Fixed(badge_rules.protocol_owner_badge.clone()), + track_total_supply: true, + non_fungible_schema: non_fungible_schema.clone(), + entries: Default::default(), + resource_roles: NonFungibleResourceRoles { + // Mintable and burnable by the Ignition package and the + // protocol owner can update who can do that. + mint_roles: mint_roles! { + minter => entity_package_caller_rules.protocol_entities.ignition.clone(); + minter_updater => badge_rules.protocol_owner_badge.clone(); + }, + burn_roles: burn_roles! { + burner => entity_package_caller_rules.protocol_entities.ignition.clone(); + burner_updater => badge_rules.protocol_owner_badge.clone(); + }, + // The protocol owner reserves the rights to update the data + // of the non-fungibles as they see fit. + non_fungible_data_update_roles: non_fungible_data_update_roles! { + non_fungible_data_updater => rule!(deny_all); + non_fungible_data_updater_updater => badge_rules.protocol_owner_badge.clone(); + }, + // Everything else is deny all and can't be changed. + recall_roles: recall_roles! { + recaller => rule!(deny_all); + recaller_updater => rule!(deny_all); + }, + freeze_roles: freeze_roles! { + freezer => rule!(deny_all); + freezer_updater => rule!(deny_all); + }, + deposit_roles: deposit_roles! { + depositor => rule!(allow_all); + depositor_updater => rule!(deny_all); + }, + withdraw_roles: withdraw_roles! { + withdrawer => rule!(allow_all); + withdrawer_updater => rule!(deny_all); + }, + }, + metadata: ModuleConfig { + init: metadata_init, + roles: metadata_roles! { + metadata_setter => badge_rules.protocol_owner_badge.clone(); + metadata_setter_updater => badge_rules.protocol_owner_badge.clone(); + metadata_locker => badge_rules.protocol_owner_badge.clone(); + metadata_locker_updater => badge_rules.protocol_owner_badge.clone(); + } + }, + address_reservation: None, + }, + ) + .build(); + + execution_service + .execute_manifest(manifest) + .map(|new_entities| { + new_entities + .new_entities + .new_resource_addresses + .first() + .copied() + .unwrap() + }) +} + +#[derive(Debug)] +pub enum PublishingError { + NetworkConnectionProviderError(E), + ExecutionServiceError(ExecutionServiceError), +} + +impl From for PublishingError { + fn from(value: E) -> Self { + Self::NetworkConnectionProviderError(value) + } +} + +impl From> for PublishingError { + fn from(value: ExecutionServiceError) -> Self { + Self::ExecutionServiceError(value) + } +} diff --git a/tools/publishing-tool/src/publishing/macros.rs b/tools/publishing-tool/src/publishing/macros.rs new file mode 100644 index 00000000..d3e4f67e --- /dev/null +++ b/tools/publishing-tool/src/publishing/macros.rs @@ -0,0 +1,134 @@ +macro_rules! name_indexed_struct { + ( + $(#[$meta: meta])* + $struct_vis: vis struct $struct_ident: ident <$generic: ident> { + $( + $(#[$field_meta: meta])* + $field_vis: vis $field_ident: ident: $field_ty: ty + ),* $(,)? + } + ) => { + // Pass through the struct definition + $(#[$meta])* + $struct_vis struct $struct_ident <$generic> { + $( + $(#[$field_meta])* + $field_vis $field_ident: $field_ty + ),* + } + + impl<$generic> $struct_ident<$generic> { + // Map function + pub fn map(&self, mut map: F) -> $struct_ident + where + F: FnMut(&$generic) -> O, + { + $struct_ident:: { + $( + $field_ident: map(&self.$field_ident) + ),* + } + } + + // Map owned function + pub fn map_owned(self, mut map: F) -> $struct_ident + where + F: FnMut($generic) -> O, + { + $struct_ident:: { + $( + $field_ident: map(self.$field_ident) + ),* + } + } + + // Map function + pub fn try_map(&self, mut map: F) -> Result<$struct_ident, E> + where + F: FnMut(&$generic) -> Result, + { + Ok($struct_ident:: { + $( + $field_ident: map(&self.$field_ident)? + ),* + }) + } + + // Zip two together + pub fn zip(self, other: $struct_ident) -> $struct_ident<($generic, Other)> { + $struct_ident { + $( + $field_ident: (self.$field_ident, other.$field_ident) + ),* + } + } + + pub fn zip_borrowed(self, other: &$struct_ident) -> $struct_ident<($generic, &Other)> { + $struct_ident { + $( + $field_ident: (self.$field_ident, &other.$field_ident) + ),* + } + } + + // Creating from a map + pub fn from_map( + map: M + ) -> Result + where + M: IntoIterator, + S: AsRef + { + $( + let mut $field_ident = None::<$generic>; + )* + + for (key, value) in map.into_iter() { + match key.as_ref() { + $( + stringify!($field_ident) => { + $field_ident = Some(value) + } + ),* + _ => {} + } + } + + $( + let $field_ident = $field_ident + .ok_or_else( + || $crate::publishing::KeyNotFound { + key: stringify!($field_ident).to_owned() + } + )?; + )* + + Ok($struct_ident { + $( + $field_ident + ),* + }) + } + + pub fn iter(&self) -> impl Iterator { + vec![ + $( + &self.$field_ident + ),* + ].into_iter() + } + + // Creating a map of everything in the name indexed struct + pub fn into_map(&self) -> ::radix_engine_common::prelude::IndexMap<&'static str, &$generic> { + let mut map = ::radix_engine_common::prelude::IndexMap::<&'static str, &$generic>::new(); + + $( + map.insert(stringify!($field_ident), &self.$field_ident); + )* + + map + } + } + }; +} +pub(super) use name_indexed_struct; diff --git a/tools/publishing-tool/src/publishing/mod.rs b/tools/publishing-tool/src/publishing/mod.rs new file mode 100644 index 00000000..52b4e0c2 --- /dev/null +++ b/tools/publishing-tool/src/publishing/mod.rs @@ -0,0 +1,9 @@ +mod configuration; +#[macro_use] +mod macros; +mod error; +mod handler; + +pub use configuration::*; +pub use error::*; +pub use handler::*; diff --git a/tools/publishing-tool/src/utils.rs b/tools/publishing-tool/src/utils.rs new file mode 100644 index 00000000..c2e376fe --- /dev/null +++ b/tools/publishing-tool/src/utils.rs @@ -0,0 +1,39 @@ +use sbor::representations::SerializationParameters; +use transaction::prelude::*; + +pub fn clone_private_key(private_key: &PrivateKey) -> PrivateKey { + match private_key { + PrivateKey::Secp256k1(private_key) => PrivateKey::Secp256k1( + Secp256k1PrivateKey::from_bytes(&private_key.to_bytes()).unwrap(), + ), + PrivateKey::Ed25519(private_key) => PrivateKey::Ed25519( + Ed25519PrivateKey::from_bytes(&private_key.to_bytes()).unwrap(), + ), + } +} + +pub fn to_json( + value: &S, + network_definition: &NetworkDefinition, +) -> String { + let encoder = AddressBech32Encoder::new(network_definition); + + let (local_type_id, schema) = + generate_full_schema_from_single_type::(); + let schema = schema.into_latest(); + + let context = + ScryptoValueDisplayContext::with_optional_bech32(Some(&encoder)); + let payload = scrypto_encode(value).unwrap(); + let raw_payload = ScryptoRawPayload::new_from_valid_slice(&payload); + let serializable = + raw_payload.serializable(SerializationParameters::WithSchema { + mode: representations::SerializationMode::Natural, + custom_context: context, + schema: &schema, + type_id: local_type_id, + depth_limit: SCRYPTO_SBOR_V1_MAX_DEPTH, + }); + + serde_json::to_string_pretty(&serializable).unwrap() +} From 1870bb0a2f9366bdc91b46ecd030e6ac99805aa7 Mon Sep 17 00:00:00 2001 From: Omar Date: Tue, 5 Mar 2024 10:08:27 +0300 Subject: [PATCH 04/17] [Publishing Tool]: Misc changes. --- .../src/cli/default_configurations/mainnet_testing.rs | 4 ++-- tools/publishing-tool/src/cli/publish.rs | 2 -- tools/publishing-tool/src/main.rs | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tools/publishing-tool/src/cli/default_configurations/mainnet_testing.rs b/tools/publishing-tool/src/cli/default_configurations/mainnet_testing.rs index 0d15cbdc..f22b1969 100644 --- a/tools/publishing-tool/src/cli/default_configurations/mainnet_testing.rs +++ b/tools/publishing-tool/src/cli/default_configurations/mainnet_testing.rs @@ -75,9 +75,9 @@ pub fn mainnet_testing( "icon_url".to_owned() => MetadataValue::Url(UncheckedUrl::of("https://assets.radixdlt.com/icons/icon-Ignition-LP.png")) }, transaction_configuration: TransactionConfiguration { - notary: clone_private_key(¬ary_private_key), + notary: clone_private_key(notary_private_key), fee_payer_information: AccountAndControllingKey::new_virtual_account( - clone_private_key(¬ary_private_key), + clone_private_key(notary_private_key), ), }, // TODO: Determine where they should be sent to. diff --git a/tools/publishing-tool/src/cli/publish.rs b/tools/publishing-tool/src/cli/publish.rs index b5664862..3815d301 100644 --- a/tools/publishing-tool/src/cli/publish.rs +++ b/tools/publishing-tool/src/cli/publish.rs @@ -2,8 +2,6 @@ use super::default_configurations::*; use crate::utils::*; use crate::*; use clap::Parser; -use common::prelude::LockupPeriod; -use defiplaza_v2_adapter_v1::DefiPlazaV2PoolInterfaceManifestBuilderExtensionTrait; use radix_engine_common::prelude::*; use state_manager::RocksDBStore; use std::path::*; diff --git a/tools/publishing-tool/src/main.rs b/tools/publishing-tool/src/main.rs index 341c3d0c..dd1d9a88 100644 --- a/tools/publishing-tool/src/main.rs +++ b/tools/publishing-tool/src/main.rs @@ -1,4 +1,4 @@ -#![allow(dead_code, clippy::enum_variant_names)] +#![allow(dead_code, clippy::enum_variant_names, clippy::wrong_self_convention)] mod cli; mod database_overlay; From fd26ddf374192aa75dddf067ddc54745228f5c48 Mon Sep 17 00:00:00 2001 From: Omar Date: Tue, 5 Mar 2024 15:18:53 +0300 Subject: [PATCH 05/17] [Ociswap v2 Adapter v1]: Add the global id of the underlying LP. --- packages/ociswap-v2-adapter-v1/src/lib.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/ociswap-v2-adapter-v1/src/lib.rs b/packages/ociswap-v2-adapter-v1/src/lib.rs index 3ad18d58..72099637 100644 --- a/packages/ociswap-v2-adapter-v1/src/lib.rs +++ b/packages/ociswap-v2-adapter-v1/src/lib.rs @@ -171,12 +171,22 @@ pub mod adapter { let (receipt, change_x, change_y) = pool.add_liquidity(lower_tick, upper_tick, bucket_x, bucket_y); + let non_fungible_global_id = NonFungibleGlobalId::new( + receipt.as_non_fungible().resource_address(), + receipt.as_non_fungible().non_fungible_local_id(), + ); + OpenLiquidityPositionOutput { pool_units: IndexedBuckets::from_bucket(receipt), change: IndexedBuckets::from_buckets([change_x, change_y]), others: Default::default(), - adapter_specific_information: AnyValue::from_typed(&()) - .expect(UNEXPECTED_ERROR), + adapter_specific_information: AnyValue::from_typed( + &OciswapV2AdapterSpecificInformation { + liquidity_receipt_non_fungible_global_id: + non_fungible_global_id.clone(), + }, + ) + .expect(UNEXPECTED_ERROR), } } @@ -246,7 +256,10 @@ pub mod adapter { } #[derive(ScryptoSbor, Debug, Clone)] -pub struct OciswapV2AdapterSpecificInformation {} +pub struct OciswapV2AdapterSpecificInformation { + /// Stores the non-fungible global id of the liquidity receipt. + pub liquidity_receipt_non_fungible_global_id: NonFungibleGlobalId, +} impl From for AnyValue { fn from(value: OciswapV2AdapterSpecificInformation) -> Self { From e150d04bc8289128a0f4e776403e8ed469e5d1ce Mon Sep 17 00:00:00 2001 From: Omar Date: Tue, 5 Mar 2024 16:37:04 +0300 Subject: [PATCH 06/17] [Publishing Tool]: Allow the publishing tool to run against stokenet --- Cargo.lock | 1 + tools/publishing-tool/Cargo.toml | 1 + .../default_configurations/mainnet_testing.rs | 1 + .../src/cli/default_configurations/mod.rs | 7 + .../stokenet_testing.rs | 243 ++++++++++++++++++ tools/publishing-tool/src/cli/publish.rs | 53 ++-- tools/publishing-tool/src/error.rs | 3 + .../src/publishing/configuration.rs | 23 ++ .../publishing-tool/src/publishing/handler.rs | 50 ++++ 9 files changed, 360 insertions(+), 22 deletions(-) create mode 100644 tools/publishing-tool/src/cli/default_configurations/stokenet_testing.rs diff --git a/Cargo.lock b/Cargo.lock index 617daa46..13486e6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2050,6 +2050,7 @@ dependencies = [ name = "publishing-tool" version = "0.1.0" dependencies = [ + "bitflags 2.4.2", "caviarnine-v1-adapter-v1", "clap", "common", diff --git a/tools/publishing-tool/Cargo.toml b/tools/publishing-tool/Cargo.toml index a3d462e3..c7735f98 100644 --- a/tools/publishing-tool/Cargo.toml +++ b/tools/publishing-tool/Cargo.toml @@ -43,6 +43,7 @@ hex-literal = "0.4.1" itertools = "0.12.1" serde_json = "1.0.114" clap = { version = "4.5.1", features = ["derive"] } +bitflags = "2.4.2" [lints] workspace = true diff --git a/tools/publishing-tool/src/cli/default_configurations/mainnet_testing.rs b/tools/publishing-tool/src/cli/default_configurations/mainnet_testing.rs index f22b1969..20d6f239 100644 --- a/tools/publishing-tool/src/cli/default_configurations/mainnet_testing.rs +++ b/tools/publishing-tool/src/cli/default_configurations/mainnet_testing.rs @@ -299,6 +299,7 @@ pub fn mainnet_testing( additional_information: AdditionalInformation { ociswap_v2_registry_component: None, }, + additional_operation_flags: AdditionalOperationFlags::empty() // cSpell:enable } } diff --git a/tools/publishing-tool/src/cli/default_configurations/mod.rs b/tools/publishing-tool/src/cli/default_configurations/mod.rs index 49a6f636..fa6d4188 100644 --- a/tools/publishing-tool/src/cli/default_configurations/mod.rs +++ b/tools/publishing-tool/src/cli/default_configurations/mod.rs @@ -1,10 +1,12 @@ use crate::*; use clap::*; mod mainnet_testing; +mod stokenet_testing; #[derive(ValueEnum, Clone, Copy, Debug)] pub enum ConfigurationSelector { MainnetTesting, + StokenetTesting, } impl ConfigurationSelector { @@ -16,18 +18,23 @@ impl ConfigurationSelector { Self::MainnetTesting => { mainnet_testing::mainnet_testing(notary_private_key) } + Self::StokenetTesting => { + stokenet_testing::stokenet_testing(notary_private_key) + } } } pub fn gateway_base_url(self) -> String { match self { Self::MainnetTesting => "https://mainnet.radixdlt.com".to_owned(), + Self::StokenetTesting => "https://stokenet.radixdlt.com".to_owned(), } } pub fn network_definition(self) -> NetworkDefinition { match self { Self::MainnetTesting => NetworkDefinition::mainnet(), + Self::StokenetTesting => NetworkDefinition::stokenet(), } } } diff --git a/tools/publishing-tool/src/cli/default_configurations/stokenet_testing.rs b/tools/publishing-tool/src/cli/default_configurations/stokenet_testing.rs new file mode 100644 index 00000000..9a273334 --- /dev/null +++ b/tools/publishing-tool/src/cli/default_configurations/stokenet_testing.rs @@ -0,0 +1,243 @@ +use common::prelude::*; + +use self::utils::*; +use crate::*; + +pub fn stokenet_testing( + notary_private_key: &PrivateKey, +) -> PublishingConfiguration { + let notary_account_address = + ComponentAddress::virtual_account_from_public_key( + ¬ary_private_key.public_key(), + ); + + // cSpell:disable + PublishingConfiguration { + protocol_configuration: ProtocolConfiguration { + protocol_resource: XRD, + user_resource_volatility: UserResourceIndexedData { + bitcoin: Volatility::Volatile, + ethereum: Volatility::Volatile, + usdc: Volatility::NonVolatile, + usdt: Volatility::NonVolatile, + }, + reward_rates: indexmap! { + LockupPeriod::from_minutes(0).unwrap() => dec!(0.125), // 12.5% + LockupPeriod::from_minutes(1).unwrap() => dec!(0.15), // 15.0% + }, + allow_opening_liquidity_positions: true, + allow_closing_liquidity_positions: true, + maximum_allowed_price_staleness: i64::MAX, + maximum_allowed_price_difference_percentage: Decimal::MAX, + entities_metadata: Entities { + protocol_entities: ProtocolIndexedData { + ignition: metadata_init! { + "name" => "Ignition", updatable; + "description" => "The main entrypoint into the Ignition liquidity incentive program.", updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + simple_oracle: metadata_init! { + "name" => "Ignition Oracle", updatable; + "description" => "The oracle used by the Ignition protocol.", updatable; + "tags" => vec!["oracle"], updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + }, + exchange_adapter_entities: ExchangeIndexedData { + ociswap_v2: metadata_init! { + "name" => "Ignition Ociswap v2 Adapter", updatable; + "description" => "An adapter used by the Ignition protocol to communicate with Ociswap v2 pools.", updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + defiplaza_v2: metadata_init! { + "name" => "Ignition DefiPlaza v2 Adapter", updatable; + "description" => "An adapter used by the Ignition protocol to communicate with DefiPlaza v2 pools.", updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + caviarnine_v1: metadata_init! { + "name" => "Ignition Caviarnine v1 Adapter", updatable; + "description" => "An adapter used by the Ignition protocol to communicate with Caviarnine v1 pools.", updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + }, + }, + }, + dapp_definition_metadata: indexmap! { + "name".to_owned() => MetadataValue::String("Project Ignition".to_owned()), + "description".to_owned() => MetadataValue::String("A Radix liquidity incentives program, offered in partnership with select decentralized exchange dApps in the Radix ecosystem.".to_owned()), + "icon_url".to_owned() => MetadataValue::Url(UncheckedUrl::of("https://assets.radixdlt.com/icons/icon-Ignition-LP.png")) + }, + transaction_configuration: TransactionConfiguration { + notary: clone_private_key(notary_private_key), + fee_payer_information: AccountAndControllingKey::new_virtual_account( + clone_private_key(notary_private_key), + ), + }, + // TODO: Determine where they should be sent to. + badges: BadgeIndexedData { + oracle_manager_badge: BadgeHandling::CreateAndSend { + account_address: notary_account_address, + metadata_init: metadata_init! { + "name" => "Ignition Oracle Manager", updatable; + "symbol" => "IGNOM", updatable; + "description" => "A badge with the authority to update the Oracle prices of the Ignition oracle.", updatable; + "tags" => vec!["badge"], updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + }, + protocol_manager_badge: BadgeHandling::CreateAndSend { + account_address: notary_account_address, + metadata_init: metadata_init! { + "name" => "Ignition Protocol Manager", updatable; + "symbol" => "IGNPM", updatable; + "description" => "A badge with the authority to manage the Ignition protocol.", updatable; + "tags" => vec!["badge"], updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + }, + protocol_owner_badge: BadgeHandling::CreateAndSend { + account_address: notary_account_address, + metadata_init: metadata_init! { + "name" => "Ignition Protocol Owner", updatable; + "symbol" => "IGNPO", updatable; + "description" => "A badge with owner authority over the Ignition protocol.", updatable; + "tags" => vec!["badge"], updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + }, + }, + // TODO: Not real resources, just the notXYZ resources. + user_resources: UserResourceIndexedData { + bitcoin: UserResourceHandling::UseExisting { + resource_address: resource_address!( + "resource_tdx_2_1thltk578jr4v7axqpu5ceznhlha6ca2qtzcflqdmytgtf37xncu7l9" + ), + }, + ethereum: UserResourceHandling::UseExisting { + resource_address: resource_address!( + "resource_tdx_2_1t59gx963vzd6u6fz63h5de2zh9nmgwxc8y832edmr6pxvz98wg6zu3" + ), + }, + usdc: UserResourceHandling::UseExisting { + resource_address: resource_address!( + "resource_tdx_2_1thfv477eqwlh8x4wt6xsc62myt4z0zxmdpr4ea74fa8jnxh243y60r" + ), + }, + usdt: UserResourceHandling::UseExisting { + resource_address: resource_address!( + "resource_tdx_2_1t4p3ytx933n576pdps4ua7jkjh36zrh36a543u0tfcsu2vthavlqg8" + ), + }, + }, + packages: Entities { + protocol_entities: ProtocolIndexedData { + ignition: PackageHandling::LoadAndPublish { + crate_package_name: "ignition".to_owned(), + metadata: metadata_init! { + "name" => "Ignition Package", updatable; + "description" => "The implementation of the Ignition protocol.", updatable; + "tags" => Vec::::new(), updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + blueprint_name: "Ignition".to_owned(), + }, + simple_oracle: PackageHandling::LoadAndPublish { + crate_package_name: "simple-oracle".to_owned(), + metadata: metadata_init! { + "name" => "Ignition Simple Oracle Package", updatable; + "description" => "The implementation of the Oracle used by the Ignition protocol.", updatable; + "tags" => vec!["oracle"], updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + blueprint_name: "SimpleOracle".to_owned(), + }, + }, + exchange_adapter_entities: ExchangeIndexedData { + ociswap_v2: PackageHandling::LoadAndPublish { + crate_package_name: "ociswap-v2-adapter-v1".to_owned(), + metadata: metadata_init! { + "name" => "Ignition Ociswap v2 Adapter Package", updatable; + "description" => "The implementation of an adapter for Ociswap v2 for the Ignition protocol.", updatable; + "tags" => vec!["adapter"], updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + blueprint_name: "OciswapV2Adapter".to_owned(), + }, + defiplaza_v2: PackageHandling::LoadAndPublish { + crate_package_name: "defiplaza-v2-adapter-v1".to_owned(), + metadata: metadata_init! { + "name" => "Ignition DefiPlaza v2 Adapter Package", updatable; + "description" => "The implementation of an adapter for DefiPlaza v1 for the Ignition protocol.", updatable; + "tags" => vec!["adapter"], updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + blueprint_name: "DefiPlazaV2Adapter".to_owned(), + }, + caviarnine_v1: PackageHandling::LoadAndPublish { + crate_package_name: "caviarnine-v1-adapter-v1".to_owned(), + metadata: metadata_init! { + "name" => "Ignition Caviarnine v1 Adapter Package", updatable; + "description" => "The implementation of an adapter for Caviarnine v1 for the Ignition protocol.", updatable; + "tags" => vec!["adapter"], updatable; + // Dapp definition will be automatically added by the + // publisher accordingly. + }, + blueprint_name: "CaviarnineV1Adapter".to_owned(), + }, + }, + }, + exchange_information: ExchangeIndexedData { + // No ociswap v2 currently on mainnet. + ociswap_v2: Some(ExchangeInformation { + blueprint_id: BlueprintId { + package_address: package_address!( + "package_tdx_2_1phgf5er6zx60wu4jjhtps97akqjpv787f6k7rjqkxgdpacng89a4uz" + ), + blueprint_name: "LiquidityPool".to_owned(), + }, + pools: UserResourceIndexedData { + bitcoin: PoolHandling::Create, + ethereum: PoolHandling::Create, + usdc: PoolHandling::Create, + usdt: PoolHandling::Create, + }, + liquidity_receipt: LiquidityReceiptHandling::CreateNew { + non_fungible_schema: + NonFungibleDataSchema::new_local_without_self_package_replacement::< + LiquidityReceipt, + >(), + metadata: metadata_init! { + "name" => "Ignition LP: Ociswap", updatable; + "description" => "Represents a particular contribution of liquidity to Ociswap through the Ignition liquidity incentives program. See the redeem_url metadata for where to redeem these NFTs.", updatable; + "tags" => vec!["lp token"], updatable; + "icon_url" => UncheckedUrl::of("https://assets.radixdlt.com/icons/icon-Ignition-LP.png"), updatable; + "DEX" => "Ociswap", updatable; + // TODO: Must get this from the DEX + "redeem_url" => UncheckedUrl::of("https://www.google.com"), updatable; + }, + }, + }), + caviarnine_v1: None, + defiplaza_v2: None, + }, + additional_information: AdditionalInformation { + ociswap_v2_registry_component: Some(component_address!( + "component_tdx_2_1cpwm3sjxr48gmsnh7lgmh5de3eqqzthqkazztc4qv6n3fvedgjepwk" + )), + }, + additional_operation_flags: AdditionalOperationFlags::SUBMIT_ORACLE_PRICES_OF_ONE + // cSpell:enable + } +} diff --git a/tools/publishing-tool/src/cli/publish.rs b/tools/publishing-tool/src/cli/publish.rs index 3815d301..31e1daeb 100644 --- a/tools/publishing-tool/src/cli/publish.rs +++ b/tools/publishing-tool/src/cli/publish.rs @@ -12,11 +12,15 @@ pub struct Publish { /// The configuration that the user wants to use when publishing. configuration_selector: ConfigurationSelector, - /// The path to the state manager database. - state_manager_database_path: PathBuf, - /// The hex-encoded private key of the notary. notary_ed25519_private_key_hex: String, + + /// The path to the state manager database. If no path is provided for the + /// state manager database then it will be assumed that the user does not + /// wish to do a simulation before publishing and is comfortable doing an + /// actual run straightaway. + #[clap(short, long)] + state_manager_database_path: Option, } impl Publish { @@ -33,18 +37,31 @@ impl Publish { let configuration = self .configuration_selector .configuration(¬ary_private_key); - - // Creating the network connection providers to use for the deployments let network_definition = self.configuration_selector.network_definition(); + + // Creating the network connection providers to use for the deployments + if let Some(state_manager_database_path) = + self.state_manager_database_path + { + let database = + RocksDBStore::new_read_only(state_manager_database_path) + .map_err(Error::RocksDbOpenError)?; + + let mut simulator_network_provider = SimulatorNetworkConnector::new( + &database, + network_definition.clone(), + ); + + // Running a dry run of the publishing process against the simulator + // network provider. + log::info!("Publishing against the simulator"); + publish(&configuration, &mut simulator_network_provider)?; + } + + // Running the transactions against the network. + log::info!("Publishing against the gateway"); let gateway_base_url = self.configuration_selector.gateway_base_url(); - let database = - RocksDBStore::new_read_only(self.state_manager_database_path) - .unwrap(); - let mut simulator_network_provider = SimulatorNetworkConnector::new( - &database, - network_definition.clone(), - ); let mut gateway_network_provider = GatewayNetworkConnector::new( gateway_base_url, network_definition.clone(), @@ -53,16 +70,8 @@ impl Publish { retries: 10, }, ); - - // Running a dry run of the publishing process against the simulator - // network provider. - log::info!("Publishing against the simulator"); - publish(&configuration, &mut simulator_network_provider)?; - - // Running the transactions against the network. - log::info!("Publishing against the gateway"); let receipt = publish(&configuration, &mut gateway_network_provider)?; - writeln!(f, "{}", to_json(&receipt, &network_definition)).unwrap(); - Ok(()) + writeln!(f, "{}", to_json(&receipt, &network_definition)) + .map_err(Error::IoError) } } diff --git a/tools/publishing-tool/src/error.rs b/tools/publishing-tool/src/error.rs index 42cdceaa..20ecbca7 100644 --- a/tools/publishing-tool/src/error.rs +++ b/tools/publishing-tool/src/error.rs @@ -1,10 +1,13 @@ use crate::*; +use state_manager::traits::*; #[derive(Debug)] pub enum Error { PrivateKeyError, GatewayExecutorError(PublishingError), SimulatorExecutorError(PublishingError), + IoError(std::io::Error), + RocksDbOpenError(DatabaseConfigValidationError), } impl From> for Error { diff --git a/tools/publishing-tool/src/publishing/configuration.rs b/tools/publishing-tool/src/publishing/configuration.rs index 9125fe9a..510734d2 100644 --- a/tools/publishing-tool/src/publishing/configuration.rs +++ b/tools/publishing-tool/src/publishing/configuration.rs @@ -44,6 +44,10 @@ pub struct PublishingConfiguration { /// Additional information that doesn't quite fit into any of the above /// categories nicely. pub additional_information: AdditionalInformation, + + /// Bit flags for additional operations that can be done by the publishing + /// logic during the publishing process. + pub additional_operation_flags: AdditionalOperationFlags, } #[derive(Debug, Clone, ScryptoSbor)] @@ -54,6 +58,25 @@ pub struct PublishingReceipt { Option>, >, pub protocol_configuration: ProtocolConfigurationReceipt, + pub badges: BadgeIndexedData, +} + +bitflags::bitflags! { + /// Additional operations that the publishing process can be instructed to + /// perform. + #[repr(transparent)] + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct AdditionalOperationFlags: u8 { + /// Submits prices to the oracle that are just one for all of the assets + /// supported in the deployment. + const SUBMIT_ORACLE_PRICES_OF_ONE = 0b00000001; + + /// Provide initial liquidity to Ignition. How this is done depends on + /// the selected protocol resource. If it is XRD then the publisher will + /// attempt to get the XRD from the faucet. If otherwise then it will + /// attempt to mint it. + const PROVIDE_INITIAL_IGNITION_LIQUIDITY = 0b00000010; + } } #[derive(Debug, Clone, ScryptoSbor)] diff --git a/tools/publishing-tool/src/publishing/handler.rs b/tools/publishing-tool/src/publishing/handler.rs index 769af58e..f53e9358 100644 --- a/tools/publishing-tool/src/publishing/handler.rs +++ b/tools/publishing-tool/src/publishing/handler.rs @@ -927,6 +927,55 @@ pub fn publish( execution_service.execute_manifest(manifest)?; } + // Processing the additional operations specified in the publishing config + { + // Submitting prices to the oracle with (user_asset, protocol_asset) + // and (protocol_asset, user_asset) where the price of both is equal + // to one. + if configuration + .additional_operation_flags + .contains(AdditionalOperationFlags::SUBMIT_ORACLE_PRICES_OF_ONE) + { + let price_updates = resolved_user_resources + .iter() + .copied() + .flat_map(|address| { + [ + ( + address, + configuration + .protocol_configuration + .protocol_resource, + ), + ( + configuration + .protocol_configuration + .protocol_resource, + address, + ), + ] + }) + .map(|address_pair| (address_pair, dec!(1))) + .collect::>(); + + let manifest = ManifestBuilder::new() + .create_proof_from_account_of_amount( + resolved_badges.oracle_manager_badge.0, + resolved_badges.oracle_manager_badge.1, + dec!(1), + ) + .call_method( + resolved_entity_component_addresses + .protocol_entities + .simple_oracle, + "set_price_batch", + (price_updates,), + ) + .build(); + execution_service.execute_manifest(manifest)?; + } + } + // Depositing the created badges into their accounts. { let mut manifest_builder = ManifestBuilder::new(); @@ -993,6 +1042,7 @@ pub fn publish( information.as_ref().map(|information| information.pools) }), }, + badges: resolved_badges.map(|(_, address)| *address), }) } From 12692831529a6985c7b760a82d5ceb815b913d8e Mon Sep 17 00:00:00 2001 From: Omar Date: Tue, 5 Mar 2024 16:46:16 +0300 Subject: [PATCH 07/17] [Ociswap v2 Adapter v1]: Add non-fungible data to adapter specific data. --- packages/ociswap-v2-adapter-v1/src/lib.rs | 27 ++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/packages/ociswap-v2-adapter-v1/src/lib.rs b/packages/ociswap-v2-adapter-v1/src/lib.rs index 72099637..7e03d93d 100644 --- a/packages/ociswap-v2-adapter-v1/src/lib.rs +++ b/packages/ociswap-v2-adapter-v1/src/lib.rs @@ -171,10 +171,11 @@ pub mod adapter { let (receipt, change_x, change_y) = pool.add_liquidity(lower_tick, upper_tick, bucket_x, bucket_y); - let non_fungible_global_id = NonFungibleGlobalId::new( - receipt.as_non_fungible().resource_address(), - receipt.as_non_fungible().non_fungible_local_id(), - ); + let non_fungible = receipt + .as_non_fungible() + .non_fungible::(); + let non_fungible_data = non_fungible.data(); + let non_fungible_global_id = non_fungible.global_id().clone(); OpenLiquidityPositionOutput { pool_units: IndexedBuckets::from_bucket(receipt), @@ -183,7 +184,8 @@ pub mod adapter { adapter_specific_information: AnyValue::from_typed( &OciswapV2AdapterSpecificInformation { liquidity_receipt_non_fungible_global_id: - non_fungible_global_id.clone(), + non_fungible_global_id, + liquidity_receipt_data: non_fungible_data, }, ) .expect(UNEXPECTED_ERROR), @@ -259,6 +261,9 @@ pub mod adapter { pub struct OciswapV2AdapterSpecificInformation { /// Stores the non-fungible global id of the liquidity receipt. pub liquidity_receipt_non_fungible_global_id: NonFungibleGlobalId, + + /// The data of the underlying liquidity receipt + pub liquidity_receipt_data: LiquidityPosition, } impl From for AnyValue { @@ -266,3 +271,15 @@ impl From for AnyValue { AnyValue::from_typed(&value).unwrap() } } + +#[derive(NonFungibleData, ScryptoSbor, Debug, Clone)] +pub struct LiquidityPosition { + liquidity: PreciseDecimal, + left_bound: i32, + right_bound: i32, + shape_id: Option, + x_fee_checkpoint: PreciseDecimal, + y_fee_checkpoint: PreciseDecimal, + x_total_fee_checkpoint: PreciseDecimal, + y_total_fee_checkpoint: PreciseDecimal, +} From 0842a6bb69b89250d04d6f5fbb004b40ef4e1ab3 Mon Sep 17 00:00:00 2001 From: Omar Date: Tue, 5 Mar 2024 20:54:30 +0300 Subject: [PATCH 08/17] [Publishing Tool]: Have a lib and bin in same crate --- tools/publishing-tool/Cargo.toml | 7 + tools/publishing-tool/src/cli/bin.rs | 29 +++++ .../default_configurations/mainnet_testing.rs | 13 +- .../src/cli/default_configurations/mod.rs | 6 +- .../stokenet_testing.rs | 26 +++- tools/publishing-tool/src/cli/mod.rs | 18 --- tools/publishing-tool/src/cli/publish.rs | 6 +- tools/publishing-tool/src/error.rs | 3 +- tools/publishing-tool/src/lib.rs | 6 + tools/publishing-tool/src/main.rs | 23 ---- .../src/publishing/configuration.rs | 8 +- .../publishing-tool/src/publishing/handler.rs | 123 +++++++++++++++++- 12 files changed, 205 insertions(+), 63 deletions(-) create mode 100644 tools/publishing-tool/src/cli/bin.rs delete mode 100644 tools/publishing-tool/src/cli/mod.rs create mode 100644 tools/publishing-tool/src/lib.rs delete mode 100644 tools/publishing-tool/src/main.rs diff --git a/tools/publishing-tool/Cargo.toml b/tools/publishing-tool/Cargo.toml index c7735f98..70229bb8 100644 --- a/tools/publishing-tool/Cargo.toml +++ b/tools/publishing-tool/Cargo.toml @@ -47,3 +47,10 @@ bitflags = "2.4.2" [lints] workspace = true + +[lib] +crate-type = ["cdylib", "lib"] + +[[bin]] +name = "publishing-tool" +path = "src/cli/bin.rs" \ No newline at end of file diff --git a/tools/publishing-tool/src/cli/bin.rs b/tools/publishing-tool/src/cli/bin.rs new file mode 100644 index 00000000..083e5238 --- /dev/null +++ b/tools/publishing-tool/src/cli/bin.rs @@ -0,0 +1,29 @@ +#![allow(dead_code, clippy::enum_variant_names, clippy::wrong_self_convention)] + +mod default_configurations; +mod publish; + +use clap::Parser; +use publishing_tool::error::*; +use radix_engine_common::prelude::*; +use transaction::prelude::*; + +fn main() -> Result<(), Error> { + env_logger::init(); + let mut out = std::io::stdout(); + let cli = ::parse(); + cli.run(&mut out) +} + +#[derive(Parser, Debug)] +pub enum Cli { + Publish(publish::Publish), +} + +impl Cli { + pub fn run(self, out: &mut O) -> Result<(), Error> { + match self { + Self::Publish(cmd) => cmd.run(out), + } + } +} diff --git a/tools/publishing-tool/src/cli/default_configurations/mainnet_testing.rs b/tools/publishing-tool/src/cli/default_configurations/mainnet_testing.rs index 20d6f239..e03ae788 100644 --- a/tools/publishing-tool/src/cli/default_configurations/mainnet_testing.rs +++ b/tools/publishing-tool/src/cli/default_configurations/mainnet_testing.rs @@ -1,7 +1,9 @@ use common::prelude::*; - -use self::utils::*; -use crate::*; +use publishing_tool::publishing::*; +use publishing_tool::utils::*; +use publishing_tool::*; +use radix_engine_interface::prelude::*; +use transaction::prelude::*; pub fn mainnet_testing( notary_private_key: &PrivateKey, @@ -297,9 +299,8 @@ pub fn mainnet_testing( }), }, additional_information: AdditionalInformation { - ociswap_v2_registry_component: None, + ociswap_v2_registry_component_and_dapp_definition: None, }, - additional_operation_flags: AdditionalOperationFlags::empty() - // cSpell:enable + additional_operation_flags: AdditionalOperationFlags::empty(), // cSpell:enable } } diff --git a/tools/publishing-tool/src/cli/default_configurations/mod.rs b/tools/publishing-tool/src/cli/default_configurations/mod.rs index fa6d4188..d5f6e946 100644 --- a/tools/publishing-tool/src/cli/default_configurations/mod.rs +++ b/tools/publishing-tool/src/cli/default_configurations/mod.rs @@ -1,8 +1,10 @@ -use crate::*; -use clap::*; mod mainnet_testing; mod stokenet_testing; +use clap::*; +use publishing_tool::publishing::*; +use transaction::prelude::*; + #[derive(ValueEnum, Clone, Copy, Debug)] pub enum ConfigurationSelector { MainnetTesting, diff --git a/tools/publishing-tool/src/cli/default_configurations/stokenet_testing.rs b/tools/publishing-tool/src/cli/default_configurations/stokenet_testing.rs index 9a273334..eee3d1f6 100644 --- a/tools/publishing-tool/src/cli/default_configurations/stokenet_testing.rs +++ b/tools/publishing-tool/src/cli/default_configurations/stokenet_testing.rs @@ -1,7 +1,9 @@ use common::prelude::*; - -use self::utils::*; -use crate::*; +use publishing_tool::publishing::*; +use publishing_tool::utils::*; +use publishing_tool::*; +use radix_engine_interface::prelude::*; +use transaction::prelude::*; pub fn stokenet_testing( notary_private_key: &PrivateKey, @@ -233,11 +235,21 @@ pub fn stokenet_testing( defiplaza_v2: None, }, additional_information: AdditionalInformation { - ociswap_v2_registry_component: Some(component_address!( - "component_tdx_2_1cpwm3sjxr48gmsnh7lgmh5de3eqqzthqkazztc4qv6n3fvedgjepwk" + ociswap_v2_registry_component_and_dapp_definition: Some(( + component_address!( + "component_tdx_2_1cpwm3sjxr48gmsnh7lgmh5de3eqqzthqkazztc4qv6n3fvedgjepwk" + ), + component_address!( + "account_tdx_2_12yhfrtak5j0pmaju5l3p752wpye4z4nzua679ypns0094hmu66p2yk" + ), )), }, - additional_operation_flags: AdditionalOperationFlags::SUBMIT_ORACLE_PRICES_OF_ONE - // cSpell:enable + additional_operation_flags: + AdditionalOperationFlags::SUBMIT_ORACLE_PRICES_OF_ONE + .union(AdditionalOperationFlags::PROVIDE_INITIAL_IGNITION_LIQUIDITY) + .union( + AdditionalOperationFlags::PROVIDE_INITIAL_LIQUIDITY_TO_OCISWAP_BY_MINTING_USER_RESOURCE + ) } + // cSpell:enable } diff --git a/tools/publishing-tool/src/cli/mod.rs b/tools/publishing-tool/src/cli/mod.rs deleted file mode 100644 index e9eb4a83..00000000 --- a/tools/publishing-tool/src/cli/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -mod default_configurations; -mod publish; - -use crate::Error; -use clap::Parser; - -#[derive(Parser, Debug)] -pub enum Cli { - Publish(publish::Publish), -} - -impl Cli { - pub fn run(self, out: &mut O) -> Result<(), Error> { - match self { - Self::Publish(cmd) => cmd.run(out), - } - } -} diff --git a/tools/publishing-tool/src/cli/publish.rs b/tools/publishing-tool/src/cli/publish.rs index 31e1daeb..51faba50 100644 --- a/tools/publishing-tool/src/cli/publish.rs +++ b/tools/publishing-tool/src/cli/publish.rs @@ -1,7 +1,9 @@ -use super::default_configurations::*; -use crate::utils::*; +use crate::default_configurations::*; use crate::*; use clap::Parser; +use publishing_tool::network_connection_provider::*; +use publishing_tool::publishing::*; +use publishing_tool::utils::*; use radix_engine_common::prelude::*; use state_manager::RocksDBStore; use std::path::*; diff --git a/tools/publishing-tool/src/error.rs b/tools/publishing-tool/src/error.rs index 20ecbca7..d428f402 100644 --- a/tools/publishing-tool/src/error.rs +++ b/tools/publishing-tool/src/error.rs @@ -1,4 +1,5 @@ -use crate::*; +use crate::network_connection_provider::*; +use crate::publishing::*; use state_manager::traits::*; #[derive(Debug)] diff --git a/tools/publishing-tool/src/lib.rs b/tools/publishing-tool/src/lib.rs new file mode 100644 index 00000000..57f31540 --- /dev/null +++ b/tools/publishing-tool/src/lib.rs @@ -0,0 +1,6 @@ +pub mod database_overlay; +pub mod error; +pub mod macros; +pub mod network_connection_provider; +pub mod publishing; +pub mod utils; diff --git a/tools/publishing-tool/src/main.rs b/tools/publishing-tool/src/main.rs deleted file mode 100644 index dd1d9a88..00000000 --- a/tools/publishing-tool/src/main.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![allow(dead_code, clippy::enum_variant_names, clippy::wrong_self_convention)] - -mod cli; -mod database_overlay; -mod error; -mod network_connection_provider; -mod publishing; -mod utils; -#[macro_use] -mod macros; - -use error::*; -use network_connection_provider::*; -use publishing::*; -use radix_engine_common::prelude::*; -use transaction::prelude::*; - -fn main() -> Result<(), Error> { - env_logger::init(); - let mut out = std::io::stdout(); - let cli = ::parse(); - cli.run(&mut out) -} diff --git a/tools/publishing-tool/src/publishing/configuration.rs b/tools/publishing-tool/src/publishing/configuration.rs index 510734d2..1defbba4 100644 --- a/tools/publishing-tool/src/publishing/configuration.rs +++ b/tools/publishing-tool/src/publishing/configuration.rs @@ -76,6 +76,11 @@ bitflags::bitflags! { /// attempt to get the XRD from the faucet. If otherwise then it will /// attempt to mint it. const PROVIDE_INITIAL_IGNITION_LIQUIDITY = 0b00000010; + + /// Provides initial liquidity to ociswap v2 pools by minting the user + /// asset. If the protocol asset is mintable then it mints them in the + /// process and if they're not then it gets them from the faucet. + const PROVIDE_INITIAL_LIQUIDITY_TO_OCISWAP_BY_MINTING_USER_RESOURCE = 0b00000100; } } @@ -94,7 +99,8 @@ pub struct ProtocolConfigurationReceipt { } pub struct AdditionalInformation { - pub ociswap_v2_registry_component: Option, + pub ociswap_v2_registry_component_and_dapp_definition: + Option<(ComponentAddress, ComponentAddress)>, } pub struct ProtocolConfiguration { diff --git a/tools/publishing-tool/src/publishing/handler.rs b/tools/publishing-tool/src/publishing/handler.rs index f53e9358..98c170c6 100644 --- a/tools/publishing-tool/src/publishing/handler.rs +++ b/tools/publishing-tool/src/publishing/handler.rs @@ -3,6 +3,7 @@ use defiplaza_v2_adapter_v1::*; use ignition::{InitializationParametersManifest, PoolBlueprintInformation}; use itertools::*; +use ociswap_v2_adapter_v1::OciswapV2PoolInterfaceManifestBuilderExtensionTrait; use package_loader::*; use radix_engine::blueprints::package::*; use radix_engine::types::node_modules::*; @@ -974,6 +975,119 @@ pub fn publish( .build(); execution_service.execute_manifest(manifest)?; } + + // Seeding Ignition with the initial set of XRD if requested. + if configuration.additional_operation_flags.contains( + AdditionalOperationFlags::PROVIDE_INITIAL_IGNITION_LIQUIDITY, + ) { + let total_amount_of_protocol_resource = dec!(10_000); + let mut manifest_builder = ManifestBuilder::new() + .create_proof_from_account_of_amount( + resolved_badges.protocol_owner_badge.0, + resolved_badges.protocol_owner_badge.1, + dec!(1), + ); + if configuration.protocol_configuration.protocol_resource == XRD { + manifest_builder = manifest_builder.get_free_xrd_from_faucet() + } else { + manifest_builder = manifest_builder.mint_fungible( + configuration.protocol_configuration.protocol_resource, + total_amount_of_protocol_resource, + ) + } + + let manifest = manifest_builder + .take_from_worktop( + XRD, + total_amount_of_protocol_resource / 2, + "volatile", + ) + .take_from_worktop( + XRD, + total_amount_of_protocol_resource / 2, + "non_volatile", + ) + .with_name_lookup(|builder, _| { + let volatile = builder.bucket("volatile"); + let non_volatile = builder.bucket("non_volatile"); + + builder + .call_method( + resolved_entity_component_addresses + .protocol_entities + .ignition, + "deposit_protocol_resources", + (volatile, common::prelude::Volatility::Volatile), + ) + .call_method( + resolved_entity_component_addresses + .protocol_entities + .ignition, + "deposit_protocol_resources", + ( + non_volatile, + common::prelude::Volatility::NonVolatile, + ), + ) + }) + .build(); + execution_service.execute_manifest(manifest)?; + } + + // Contributing initial liquidity to Ociswap if requested + if configuration.additional_operation_flags.contains( + AdditionalOperationFlags::PROVIDE_INITIAL_LIQUIDITY_TO_OCISWAP_BY_MINTING_USER_RESOURCE, + ) { + if let Some(ExchangeInformation { pools, .. }) = resolved_exchange_data.ociswap_v2 { + for (pool_address, user_resource_address) in + pools.zip_borrowed(&resolved_user_resources).iter() + { + let (pool_address, user_resource_address) = + (*pool_address, **user_resource_address); + + let mut manifest_builder = ManifestBuilder::new(); + if configuration.protocol_configuration.protocol_resource == XRD { + manifest_builder = manifest_builder.get_free_xrd_from_faucet() + } else { + manifest_builder = manifest_builder.mint_fungible( + configuration.protocol_configuration.protocol_resource, + dec!(10_000), + ) + } + let manifest = manifest_builder + .mint_fungible(user_resource_address, dec!(10_000)) + .take_all_from_worktop( + configuration.protocol_configuration.protocol_resource, + "protocol", + ) + .take_all_from_worktop(user_resource_address, "user") + .then(|builder| { + let protocol_resource = builder.bucket("protocol"); + let user_resource = builder.bucket("user"); + + let (x_bucket, y_bucket) = + if configuration.protocol_configuration.protocol_resource + < user_resource_address + { + (protocol_resource, user_resource) + } else { + (user_resource, protocol_resource) + }; + + builder.ociswap_v2_pool_add_liquidity( + pool_address, + -3921i32, + 9942i32, + x_bucket, + y_bucket, + ) + }) + .try_deposit_entire_worktop_or_abort(ephemeral_account, None) + .build(); + execution_service.execute_manifest(manifest)?; + } + } + } } // Depositing the created badges into their accounts. @@ -1063,7 +1177,11 @@ fn handle_ociswap_v2_exchange_information( > { // No ociswap registry component is passed even through it is needed. let AdditionalInformation { - ociswap_v2_registry_component: Some(ociswap_v2_registry_component), + ociswap_v2_registry_component_and_dapp_definition: + Some(( + ociswap_v2_registry_component, + ociswap_v2_dapp_definition_account, + )), } = additional_information else { return Ok(None); @@ -1130,8 +1248,7 @@ fn handle_ociswap_v2_exchange_information( ManifestBucket, )>::new( ), - // TODO: Specify their dapp definition? - FAUCET, + ociswap_v2_dapp_definition_account, ), ) .build(); From cc5b086bd92289707998e350c0bc36672934f461 Mon Sep 17 00:00:00 2001 From: Omar Date: Tue, 5 Mar 2024 11:47:03 +0300 Subject: [PATCH 09/17] [Defiplaza v2 Adapter v1]: Some cleanups and tests --- Cargo.lock | 13 +++ packages/defiplaza-v2-adapter-v1/src/lib.rs | 40 +++------ tests/tests/defiplaza_v2.rs | 94 +++++++++++++++++++++ 3 files changed, 121 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 13486e6a..0e69f336 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -669,6 +669,19 @@ dependencies = [ "transaction", ] +[[package]] +name = "defiplaza-v2-adapter-v1" +version = "0.1.0" +dependencies = [ + "common", + "ports-interface", + "radix-engine-interface", + "sbor", + "scrypto", + "scrypto-interface", + "transaction", +] + [[package]] name = "deranged" version = "0.3.11" diff --git a/packages/defiplaza-v2-adapter-v1/src/lib.rs b/packages/defiplaza-v2-adapter-v1/src/lib.rs index 38ca81fa..0a99dda0 100644 --- a/packages/defiplaza-v2-adapter-v1/src/lib.rs +++ b/packages/defiplaza-v2-adapter-v1/src/lib.rs @@ -6,8 +6,6 @@ use ports_interface::prelude::*; use scrypto::prelude::*; use scrypto_interface::*; -// TODO: Remove all logging. - macro_rules! define_error { ( $( @@ -59,7 +57,8 @@ pub mod adapter { /// The pair config of the various pools is constant but there is no /// getter function that can be used to get it on ledger. As such, the /// protocol owner or manager must submit this information to the - /// adapter for its operation. + /// adapter for its operation. This does not change, so, once set we + /// do not expect to remove it again. pair_config: KeyValueStore, } @@ -230,11 +229,6 @@ pub mod adapter { // // In the case of equilibrium we do not contribute the second bucket // and instead just the first bucket. - info!("Doing the first one"); - info!( - "Shortage before first contribution: {:?}", - pool.get_state().shortage - ); let (first_pool_units, second_change) = match shortage_state { ShortageState::Equilibrium => ( pool.add_liquidity(first_bucket, None).0, @@ -244,10 +238,6 @@ pub mod adapter { pool.add_liquidity(first_bucket, Some(second_bucket)) } }; - info!( - "Shortage after first contribution: {:?}", - pool.get_state().shortage - ); // Step 5: Calculate and store the original target of the second // liquidity position. This is calculated as the amount of assets @@ -256,17 +246,19 @@ pub mod adapter { let second_original_target = second_bucket.amount(); // Step 6: Add liquidity with the second resource & no co-liquidity. - info!("Doing the second one"); let (second_pool_units, change) = pool.add_liquidity(second_bucket, None); - info!( - "Shortage after second contribution: {:?}", - pool.get_state().shortage - ); - // TODO: Should we subtract the change from the second original - // target? Seems like we should if the price if not the same in - // some way? + // We've been told that the change should be zero. Therefore, we + // assert for it to make sure that everything is as we expect it + // to be. + assert_eq!( + change + .as_ref() + .map(|bucket| bucket.amount()) + .unwrap_or(Decimal::ZERO), + Decimal::ZERO + ); // A sanity check to make sure that everything is correct. The pool // units obtained from the first contribution should be different @@ -433,7 +425,6 @@ pub mod adapter { Global::::from(base_pool), Global::::from(quote_pool), ); - info!("bid ask = {bid_ask:?}"); let average_price = bid_ask .bid @@ -441,7 +432,6 @@ pub mod adapter { .and_then(|value| value.checked_div(dec!(2))) .expect(OVERFLOW_ERROR); - info!("average_price = {average_price}"); Price { base: base_resource_address, quote: quote_resource_address, @@ -488,8 +478,8 @@ impl From for AnyValue { // source code is licensed under the MIT license which allows us to do such // copies and modification of code. // -// This module exposes two main functions which are the entrypoints into this -// module's functionality which calculate the incoming and outgoing spot prices. +// The `calculate_pair_prices` function is the entrypoint into the module and is +// the function to calculate the current bid and ask prices of the pairs. #[allow(clippy::arithmetic_side_effects)] mod price_math { use super::*; @@ -564,8 +554,6 @@ mod price_math { let bid = incoming_spot; let ask = outgoing_spot; - info!("Shortage = {:?}", pair_state.shortage); - // TODO: What to do at equilibrium? match pair_state.shortage { Shortage::Equilibrium | Shortage::BaseShortage => { diff --git a/tests/tests/defiplaza_v2.rs b/tests/tests/defiplaza_v2.rs index 323bc054..c058189e 100644 --- a/tests/tests/defiplaza_v2.rs +++ b/tests/tests/defiplaza_v2.rs @@ -822,3 +822,97 @@ fn user_resources_are_contributed_in_full_when_oracle_price_is_lower_than_pool_p Ok(()) } + +#[test] +fn pool_reported_price_and_quote_reported_price_are_similar_with_base_resource_as_input( +) -> Result<(), RuntimeError> { + // Arrange + let Environment { + environment: ref mut env, + mut defiplaza_v2, + .. + } = ScryptoTestEnv::new_with_configuration(Configuration { + maximum_allowed_relative_price_difference: dec!(0.05), + ..Default::default() + })?; + + let pool = defiplaza_v2.pools.bitcoin; + let (base_resource, quote_resource) = pool.get_tokens(env)?; + let input_amount = dec!(100); + let input_resource = base_resource; + let output_resource = if input_resource == base_resource { + quote_resource + } else { + base_resource + }; + + let pool_reported_price = defiplaza_v2 + .adapter + .price(ComponentAddress::try_from(pool).unwrap(), env)?; + + // Act + let (output_amount, remainder, ..) = + pool.quote(input_amount, input_resource == quote_resource, env)?; + + // Assert + let input_amount = input_amount - remainder; + let quote_reported_price = Price { + price: output_amount / input_amount, + base: input_resource, + quote: output_resource, + }; + let relative_difference = pool_reported_price + .relative_difference("e_reported_price) + .unwrap(); + + assert!(relative_difference <= dec!(0.0001)); + + Ok(()) +} + +#[test] +fn pool_reported_price_and_quote_reported_price_are_similar_with_quote_resource_as_input( +) -> Result<(), RuntimeError> { + // Arrange + let Environment { + environment: ref mut env, + mut defiplaza_v2, + .. + } = ScryptoTestEnv::new_with_configuration(Configuration { + maximum_allowed_relative_price_difference: dec!(0.05), + ..Default::default() + })?; + + let pool = defiplaza_v2.pools.bitcoin; + let (base_resource, quote_resource) = pool.get_tokens(env)?; + let input_amount = dec!(100); + let input_resource = quote_resource; + let output_resource = if input_resource == base_resource { + quote_resource + } else { + base_resource + }; + + let pool_reported_price = defiplaza_v2 + .adapter + .price(ComponentAddress::try_from(pool).unwrap(), env)?; + + // Act + let (output_amount, remainder, ..) = + pool.quote(input_amount, input_resource == quote_resource, env)?; + + // Assert + let input_amount = input_amount - remainder; + let quote_reported_price = Price { + price: output_amount / input_amount, + base: input_resource, + quote: output_resource, + }; + let relative_difference = pool_reported_price + .relative_difference("e_reported_price) + .unwrap(); + + assert!(relative_difference <= dec!(0.0001)); + + Ok(()) +} From c4910fc9cd7e4c0a889b8b23970f4c36efdd77be Mon Sep 17 00:00:00 2001 From: Omar Date: Mon, 11 Mar 2024 15:21:19 +0300 Subject: [PATCH 10/17] [Misc]: Fix cargo.lock issue --- Cargo.lock | 117 ++++++++++++++++++++++++++--------------------------- 1 file changed, 57 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e69f336..c7e03397 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -238,9 +238,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.3" +version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" [[package]] name = "bytecount" @@ -328,10 +328,11 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.88" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" dependencies = [ + "jobserver", "libc", ] @@ -358,9 +359,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.34" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" dependencies = [ "android-tzdata", "iana-time-zone", @@ -382,9 +383,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.1" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +checksum = "b230ab84b0ffdf890d5a10abdbc8b83ae1c4918275daea1ab8801f71536b2651" dependencies = [ "clap_builder", "clap_derive", @@ -392,9 +393,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.1" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", @@ -566,9 +567,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.118" +version = "1.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2673ca5ae28334544ec2a6b18ebe666c42a2650abfb48abbd532ed409a44be2b" +checksum = "635179be18797d7e10edb9cd06c859580237750c7351f39ed9b298bfc17544ad" dependencies = [ "cc", "cxxbridge-flags", @@ -578,9 +579,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.118" +version = "1.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9df46fe0eb43066a332586114174c449a62c25689f85a08f28fdcc8e12c380b9" +checksum = "9324397d262f63ef77eb795d900c0d682a34a43ac0932bec049ed73055d52f63" dependencies = [ "cc", "codespan-reporting", @@ -593,15 +594,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.118" +version = "1.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "886acf875df67811c11cd015506b3392b9e1820b1627af1a6f4e93ccdfc74d11" +checksum = "a87ff7342ffaa54b7c61618e0ce2bbcf827eba6d55b923b83d82551acbbecfe5" [[package]] name = "cxxbridge-macro" -version = "1.0.118" +version = "1.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d151cc139c3080e07f448f93a1284577ab2283d2a44acd902c6fba9ec20b6de" +checksum = "70b5b86cf65fa0626d85720619d80b288013477a91a0389fa8bc716bf4903ad1" dependencies = [ "proc-macro2", "quote", @@ -669,19 +670,6 @@ dependencies = [ "transaction", ] -[[package]] -name = "defiplaza-v2-adapter-v1" -version = "0.1.0" -dependencies = [ - "common", - "ports-interface", - "radix-engine-interface", - "sbor", - "scrypto", - "scrypto-interface", - "transaction", -] - [[package]] name = "deranged" version = "0.3.11" @@ -786,9 +774,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c012a26a7f605efc424dd53697843a72be7dc86ad2d01f7814337794a12231d" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" dependencies = [ "anstream", "anstyle", @@ -1104,9 +1092,9 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "http" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -1345,11 +1333,20 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jobserver" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -1389,9 +1386,9 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2caa5afb8bf9f3a2652760ce7d4f62d21c4d5a423e68466fca30df82f2330164" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", "windows-targets 0.52.4", @@ -2384,9 +2381,9 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "reqwest" -version = "0.11.24" +version = "0.11.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" +checksum = "0eea5a9eb898d3783f17c6407670e3592fd174cb81a10e51d4c37f49450b9946" dependencies = [ "base64", "bytes", @@ -3076,20 +3073,20 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "system-configuration" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "658bc6ee10a9b4fcf576e9b0819d95ec16f4d2c02d39fd83ac1c8789785c4a42" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.2", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", @@ -3637,9 +3634,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3647,9 +3644,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", @@ -3662,9 +3659,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -3674,9 +3671,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3684,9 +3681,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", @@ -3697,9 +3694,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-encoder" @@ -3831,9 +3828,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", From ae47f63bcf22d8d79c267ad021b06c474ea0bea1 Mon Sep 17 00:00:00 2001 From: Omar Date: Tue, 5 Mar 2024 12:25:02 +0300 Subject: [PATCH 11/17] [Defiplaza v2 Adapter v1]: Small correction --- packages/defiplaza-v2-adapter-v1/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/defiplaza-v2-adapter-v1/src/lib.rs b/packages/defiplaza-v2-adapter-v1/src/lib.rs index 0a99dda0..ac106f4e 100644 --- a/packages/defiplaza-v2-adapter-v1/src/lib.rs +++ b/packages/defiplaza-v2-adapter-v1/src/lib.rs @@ -13,7 +13,7 @@ macro_rules! define_error { )* ) => { $( - pub const $name: &'static str = concat!("[DefiPlaza v2 Adapter v2]", " ", $item); + pub const $name: &'static str = concat!("[DefiPlaza v2 Adapter v1]", " ", $item); )* }; } From 20faa96f75ff8bf4638f922bb63e35de8790cb55 Mon Sep 17 00:00:00 2001 From: Omar Date: Wed, 6 Mar 2024 18:02:22 +0300 Subject: [PATCH 12/17] [Defiplaza v2 Adapter v1]: Rename `add_pair_config` to plural --- packages/defiplaza-v2-adapter-v1/src/lib.rs | 4 ++-- tests/src/environment.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/defiplaza-v2-adapter-v1/src/lib.rs b/packages/defiplaza-v2-adapter-v1/src/lib.rs index ac106f4e..ee007db5 100644 --- a/packages/defiplaza-v2-adapter-v1/src/lib.rs +++ b/packages/defiplaza-v2-adapter-v1/src/lib.rs @@ -43,7 +43,7 @@ pub mod adapter { protocol_manager => updatable_by: [protocol_manager, protocol_owner]; }, methods { - add_pair_config => restrict_to: [protocol_manager, protocol_owner]; + add_pair_configs => restrict_to: [protocol_manager, protocol_owner]; /* User methods */ price => PUBLIC; resource_addresses => PUBLIC; @@ -96,7 +96,7 @@ pub mod adapter { .globalize() } - pub fn add_pair_config( + pub fn add_pair_configs( &mut self, pair_config: IndexMap, ) { diff --git a/tests/src/environment.rs b/tests/src/environment.rs index 9b77ab26..4d2cacfc 100644 --- a/tests/src/environment.rs +++ b/tests/src/environment.rs @@ -540,7 +540,7 @@ impl ScryptoTestEnv { )?; // Registering all of pair configs to the adapter. - defiplaza_v2_adapter_v1.add_pair_config( + defiplaza_v2_adapter_v1.add_pair_configs( defiplaza_v2_pools .iter() .map(|pool| ComponentAddress::try_from(pool).unwrap()) @@ -1307,7 +1307,7 @@ impl ScryptoUnitEnv { .lock_fee_from_faucet() .call_method( defiplaza_v2_adapter_v1, - "add_pair_config", + "add_pair_configs", (defiplaza_v2_pools .iter() .map(|address| { From 731c9515591ee0814b7de4e7a2b791424deb529d Mon Sep 17 00:00:00 2001 From: Omar Date: Wed, 6 Mar 2024 18:04:47 +0300 Subject: [PATCH 13/17] [Defiplaza v2 Adapter v1]: Rename first and second to shortage and surplus assets --- packages/defiplaza-v2-adapter-v1/src/lib.rs | 47 +++++++++++---------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/packages/defiplaza-v2-adapter-v1/src/lib.rs b/packages/defiplaza-v2-adapter-v1/src/lib.rs index ee007db5..6e3fcfab 100644 --- a/packages/defiplaza-v2-adapter-v1/src/lib.rs +++ b/packages/defiplaza-v2-adapter-v1/src/lib.rs @@ -199,7 +199,7 @@ pub mod adapter { let shortage = pair_state.shortage; let shortage_state = ShortageState::from(shortage); - let [(first_resource_address, first_bucket), (second_resource_address, second_bucket)] = + let [(shortage_asset_resource_address, shortage_asset_bucket), (surplus_asset_resource_address, surplus_asset_bucket)] = match shortage_state { ShortageState::Equilibrium => [ (base_resource_address, base_bucket), @@ -218,9 +218,9 @@ pub mod adapter { // Step 3: Calculate tate.target_ratio * bucket1.amount() where // bucket1 is the bucket currently in shortage or the resource that // will be contributed first. - let first_original_target = pair_state + let shortage_asset_original_target = pair_state .target_ratio - .checked_mul(first_bucket.amount()) + .checked_mul(shortage_asset_bucket.amount()) .expect(OVERFLOW_ERROR); // Step 4: Contribute to the pool. The first bucket to provide the @@ -229,25 +229,28 @@ pub mod adapter { // // In the case of equilibrium we do not contribute the second bucket // and instead just the first bucket. - let (first_pool_units, second_change) = match shortage_state { - ShortageState::Equilibrium => ( - pool.add_liquidity(first_bucket, None).0, - Some(second_bucket), - ), - ShortageState::Shortage(_) => { - pool.add_liquidity(first_bucket, Some(second_bucket)) - } - }; + let (shortage_asset_pool_units, surplus_asset_change) = + match shortage_state { + ShortageState::Equilibrium => ( + pool.add_liquidity(shortage_asset_bucket, None).0, + Some(surplus_asset_bucket), + ), + ShortageState::Shortage(_) => pool.add_liquidity( + shortage_asset_bucket, + Some(surplus_asset_bucket), + ), + }; // Step 5: Calculate and store the original target of the second // liquidity position. This is calculated as the amount of assets // that are in the remainder (change) bucket. - let second_bucket = second_change.expect(UNEXPECTED_ERROR); - let second_original_target = second_bucket.amount(); + let surplus_asset_bucket = + surplus_asset_change.expect(UNEXPECTED_ERROR); + let surplus_asset_original_target = surplus_asset_bucket.amount(); // Step 6: Add liquidity with the second resource & no co-liquidity. - let (second_pool_units, change) = - pool.add_liquidity(second_bucket, None); + let (surplus_asset_pool_units, change) = + pool.add_liquidity(surplus_asset_bucket, None); // We've been told that the change should be zero. Therefore, we // assert for it to make sure that everything is as we expect it @@ -264,16 +267,16 @@ pub mod adapter { // units obtained from the first contribution should be different // from those obtained in the second contribution. assert_ne!( - first_pool_units.resource_address(), - second_pool_units.resource_address(), + shortage_asset_pool_units.resource_address(), + surplus_asset_pool_units.resource_address(), ); // The procedure for adding liquidity to the pool is now complete. // We can now construct the output. OpenLiquidityPositionOutput { pool_units: IndexedBuckets::from_buckets([ - first_pool_units, - second_pool_units, + shortage_asset_pool_units, + surplus_asset_pool_units, ]), change: change .map(IndexedBuckets::from_bucket) @@ -282,8 +285,8 @@ pub mod adapter { adapter_specific_information: DefiPlazaV2AdapterSpecificInformation { original_targets: indexmap! { - first_resource_address => first_original_target, - second_resource_address => second_original_target + shortage_asset_resource_address => shortage_asset_original_target, + surplus_asset_resource_address => surplus_asset_original_target }, } .into(), From 14c3cfe35451f49eb2896b3a4de61ef88148e182 Mon Sep 17 00:00:00 2001 From: Omar Date: Wed, 6 Mar 2024 23:27:33 +0300 Subject: [PATCH 14/17] [Caviarnine v1 Adapter v1]: Optimize fees for opening positions. --- packages/caviarnine-v1-adapter-v1/src/lib.rs | 11 +- tests/tests/caviarnine_v1.rs | 293 +++++++++++++++++++ 2 files changed, 297 insertions(+), 7 deletions(-) diff --git a/packages/caviarnine-v1-adapter-v1/src/lib.rs b/packages/caviarnine-v1-adapter-v1/src/lib.rs index d379a79b..ad5f3560 100644 --- a/packages/caviarnine-v1-adapter-v1/src/lib.rs +++ b/packages/caviarnine-v1-adapter-v1/src/lib.rs @@ -475,7 +475,7 @@ pub mod adapter { } let (receipt, change_x, change_y) = - pool.add_liquidity(bucket_x, bucket_y, positions); + pool.add_liquidity(bucket_x, bucket_y, positions.clone()); let receipt_global_id = { let resource_address = receipt.resource_address(); @@ -486,14 +486,11 @@ pub mod adapter { let adapter_specific_information = CaviarnineV1AdapterSpecificInformation { - bin_contributions: pool - .get_redemption_bin_values( - receipt_global_id.local_id().clone(), - ) + bin_contributions: positions .into_iter() - .map(|(tick, amount_x, amount_y)| { + .map(|(bin, amount_x, amount_y)| { ( - tick, + bin, ResourceIndexedData { resource_x: amount_x, resource_y: amount_y, diff --git a/tests/tests/caviarnine_v1.rs b/tests/tests/caviarnine_v1.rs index 6a8ca582..cfd0a7f9 100644 --- a/tests/tests/caviarnine_v1.rs +++ b/tests/tests/caviarnine_v1.rs @@ -1472,3 +1472,296 @@ fn user_resources_are_contributed_in_full_when_oracle_price_is_lower_than_pool_p Ok(()) } + +#[test] +fn bin_amounts_reported_on_receipt_match_whats_reported_by_caviarnine( +) -> Result<(), RuntimeError> { + // Arrange + let Environment { + environment: ref mut env, + mut caviarnine_v1, + resources, + .. + } = ScryptoTestEnv::new_with_configuration(Configuration { + maximum_allowed_relative_price_difference: dec!(0.05), + ..Default::default() + })?; + + let user_resource = resources.bitcoin; + let pool = caviarnine_v1.pools.bitcoin; + + let [user_resource_bucket, xrd_bucket] = + [user_resource, XRD].map(|resource| { + ResourceManager(resource) + .mint_fungible(dec!(100), env) + .unwrap() + }); + + // Act + let OpenLiquidityPositionOutput { + pool_units, + adapter_specific_information, + .. + } = caviarnine_v1.adapter.open_liquidity_position( + pool.try_into().unwrap(), + (user_resource_bucket, xrd_bucket), + env, + )?; + + // Assert + let mut caviarnine_reported_redemption_value = pool + .get_redemption_bin_values( + pool_units + .non_fungible_local_ids(env)? + .first() + .unwrap() + .clone(), + env, + )?; + caviarnine_reported_redemption_value.sort_by(|a, b| a.0.cmp(&b.0)); + let adapter_reported_redemption_value = adapter_specific_information + .as_typed::() + .unwrap() + .bin_contributions; + + assert_eq!( + caviarnine_reported_redemption_value.len(), + adapter_reported_redemption_value.len(), + ); + + for ( + i, + ( + caviarnine_reported_bin, + caviarnine_reported_amount_x, + caviarnine_reported_amount_y, + ), + ) in caviarnine_reported_redemption_value.into_iter().enumerate() + { + let Some(ResourceIndexedData { + resource_x: adapter_reported_amount_x, + resource_y: adapter_reported_amount_y, + }) = adapter_reported_redemption_value + .get(&caviarnine_reported_bin) + .copied() + else { + panic!( + "Bin {} does not have an entry in the adapter data", + caviarnine_reported_bin + ) + }; + + assert_eq!( + round_down_to_5_decimal_places(caviarnine_reported_amount_x), + round_down_to_5_decimal_places(adapter_reported_amount_x), + "Failed at bin with index: {i}" + ); + assert_eq!( + round_down_to_5_decimal_places(caviarnine_reported_amount_y), + round_down_to_5_decimal_places(adapter_reported_amount_y), + "Failed at bin with index: {i}" + ); + } + + Ok(()) +} + +#[test] +fn bin_amounts_reported_on_receipt_match_whats_reported_by_caviarnine_with_price_movement1( +) -> Result<(), RuntimeError> { + // Arrange + let Environment { + environment: ref mut env, + mut caviarnine_v1, + resources, + .. + } = ScryptoTestEnv::new_with_configuration(Configuration { + maximum_allowed_relative_price_difference: dec!(0.05), + ..Default::default() + })?; + + let user_resource = resources.bitcoin; + let mut pool = caviarnine_v1.pools.bitcoin; + + let _ = ResourceManager(user_resource) + .mint_fungible(dec!(1_000_000_000), env) + .and_then(|bucket| pool.swap(bucket, env))?; + + let [user_resource_bucket, xrd_bucket] = + [user_resource, XRD].map(|resource| { + ResourceManager(resource) + .mint_fungible(dec!(100), env) + .unwrap() + }); + + // Act + let OpenLiquidityPositionOutput { + pool_units, + adapter_specific_information, + .. + } = caviarnine_v1.adapter.open_liquidity_position( + pool.try_into().unwrap(), + (user_resource_bucket, xrd_bucket), + env, + )?; + + // Assert + let mut caviarnine_reported_redemption_value = pool + .get_redemption_bin_values( + pool_units + .non_fungible_local_ids(env)? + .first() + .unwrap() + .clone(), + env, + )?; + caviarnine_reported_redemption_value.sort_by(|a, b| a.0.cmp(&b.0)); + let adapter_reported_redemption_value = adapter_specific_information + .as_typed::() + .unwrap() + .bin_contributions; + + assert_eq!( + caviarnine_reported_redemption_value.len(), + adapter_reported_redemption_value.len(), + ); + + for ( + i, + ( + caviarnine_reported_bin, + caviarnine_reported_amount_x, + caviarnine_reported_amount_y, + ), + ) in caviarnine_reported_redemption_value.into_iter().enumerate() + { + let Some(ResourceIndexedData { + resource_x: adapter_reported_amount_x, + resource_y: adapter_reported_amount_y, + }) = adapter_reported_redemption_value + .get(&caviarnine_reported_bin) + .copied() + else { + panic!( + "Bin {} does not have an entry in the adapter data", + caviarnine_reported_bin + ) + }; + + assert_eq!( + round_down_to_5_decimal_places(caviarnine_reported_amount_x), + round_down_to_5_decimal_places(adapter_reported_amount_x), + "Failed at bin with index: {i}" + ); + assert_eq!( + round_down_to_5_decimal_places(caviarnine_reported_amount_y), + round_down_to_5_decimal_places(adapter_reported_amount_y), + "Failed at bin with index: {i}" + ); + } + + Ok(()) +} + +#[test] +fn bin_amounts_reported_on_receipt_match_whats_reported_by_caviarnine_with_price_movement2( +) -> Result<(), RuntimeError> { + // Arrange + let Environment { + environment: ref mut env, + mut caviarnine_v1, + resources, + .. + } = ScryptoTestEnv::new_with_configuration(Configuration { + maximum_allowed_relative_price_difference: dec!(0.05), + ..Default::default() + })?; + + let user_resource = resources.bitcoin; + let mut pool = caviarnine_v1.pools.bitcoin; + + let _ = ResourceManager(XRD) + .mint_fungible(dec!(1_000_000_000), env) + .and_then(|bucket| pool.swap(bucket, env))?; + + let [user_resource_bucket, xrd_bucket] = + [user_resource, XRD].map(|resource| { + ResourceManager(resource) + .mint_fungible(dec!(100), env) + .unwrap() + }); + + // Act + let OpenLiquidityPositionOutput { + pool_units, + adapter_specific_information, + .. + } = caviarnine_v1.adapter.open_liquidity_position( + pool.try_into().unwrap(), + (user_resource_bucket, xrd_bucket), + env, + )?; + + // Assert + let mut caviarnine_reported_redemption_value = pool + .get_redemption_bin_values( + pool_units + .non_fungible_local_ids(env)? + .first() + .unwrap() + .clone(), + env, + )?; + caviarnine_reported_redemption_value.sort_by(|a, b| a.0.cmp(&b.0)); + let adapter_reported_redemption_value = adapter_specific_information + .as_typed::() + .unwrap() + .bin_contributions; + + assert_eq!( + caviarnine_reported_redemption_value.len(), + adapter_reported_redemption_value.len(), + ); + + for ( + i, + ( + caviarnine_reported_bin, + caviarnine_reported_amount_x, + caviarnine_reported_amount_y, + ), + ) in caviarnine_reported_redemption_value.into_iter().enumerate() + { + let Some(ResourceIndexedData { + resource_x: adapter_reported_amount_x, + resource_y: adapter_reported_amount_y, + }) = adapter_reported_redemption_value + .get(&caviarnine_reported_bin) + .copied() + else { + panic!( + "Bin {} does not have an entry in the adapter data", + caviarnine_reported_bin + ) + }; + + assert_eq!( + round_down_to_5_decimal_places(caviarnine_reported_amount_x), + round_down_to_5_decimal_places(adapter_reported_amount_x), + "Failed at bin with index: {i}" + ); + assert_eq!( + round_down_to_5_decimal_places(caviarnine_reported_amount_y), + round_down_to_5_decimal_places(adapter_reported_amount_y), + "Failed at bin with index: {i}" + ); + } + + Ok(()) +} + +fn round_down_to_5_decimal_places(decimal: Decimal) -> Decimal { + decimal + .checked_round(5, RoundingMode::ToNegativeInfinity) + .unwrap() +} From 7160d6d811bc40bfd0f3fa0d34f2317c1b87c2b0 Mon Sep 17 00:00:00 2001 From: Omar Date: Thu, 7 Mar 2024 11:51:12 +0300 Subject: [PATCH 15/17] [Tests]: Add basis for defiplaza v2 fee tests --- tests/src/environment.rs | 14 +-- tests/tests/defiplaza_v2.rs | 194 ++++++++++++++++++++++++++++++++++++ 2 files changed, 198 insertions(+), 10 deletions(-) diff --git a/tests/src/environment.rs b/tests/src/environment.rs index 4d2cacfc..438cf334 100644 --- a/tests/src/environment.rs +++ b/tests/src/environment.rs @@ -430,16 +430,10 @@ impl ScryptoTestEnv { Self::publish_package("defiplaza-v2-adapter-v1", &mut env)?; let defiplaza_v2_pools = resource_addresses.try_map(|resource_address| { - let (resource_x, resource_y) = if XRD < *resource_address { - (XRD, *resource_address) - } else { - (*resource_address, XRD) - }; - let mut defiplaza_pool = DefiPlazaV2PoolInterfaceScryptoTestStub::instantiate_pair( OwnerRole::None, - resource_x, - resource_y, + *resource_address, + XRD, // This pair config is obtained from DefiPlaza's // repo. PairConfig { @@ -454,9 +448,9 @@ impl ScryptoTestEnv { )?; let resource_x = - ResourceManager(resource_x).mint_fungible(dec!(100_000_000), &mut env)?; + ResourceManager(*resource_address).mint_fungible(dec!(100_000_000), &mut env)?; let resource_y = - ResourceManager(resource_y).mint_fungible(dec!(100_000_000), &mut env)?; + ResourceManager(XRD).mint_fungible(dec!(100_000_000), &mut env)?; let (_, change1) = defiplaza_pool.add_liquidity(resource_x, None, &mut env)?; let (_, change2) = defiplaza_pool.add_liquidity(resource_y, None, &mut env)?; diff --git a/tests/tests/defiplaza_v2.rs b/tests/tests/defiplaza_v2.rs index c058189e..04f9e792 100644 --- a/tests/tests/defiplaza_v2.rs +++ b/tests/tests/defiplaza_v2.rs @@ -916,3 +916,197 @@ fn pool_reported_price_and_quote_reported_price_are_similar_with_quote_resource_ Ok(()) } + +#[test] +#[ignore = "Awaiting defiplaza response"] +fn exact_fee_test1() { + test_exact_defiplaza_fees_amounts( + // Initial supply for the pool. + AssetIndexedData { + protocol_resource: dec!(100_000), + user_resource: dec!(100_000), + }, + // Initial price of the pool + dec!(1), + // The fee percentage of the pool + dec!(0.03), + // User contribution to the pool. This would mean that the user would + // own 0.1% of the pool + AssetIndexedData { + user_resource: dec!(100), + protocol_resource: dec!(100), + }, + // The swaps to perform - the asset you see is the input asset + vec![(Asset::ProtocolResource, dec!(1_000))], + // The fees to expect - with 0.1% pool ownership of the pool and fees of + // 3% then we expect to see 0.03 of the protocol resource in fees (as it + // was the input in the swap) and none of the user resource in fees. + AssetIndexedData { + user_resource: EqualityCheck::ExactlyEquals(dec!(0)), + protocol_resource: EqualityCheck::ExactlyEquals(dec!(0.03)), + }, + ) + .expect("Should not fail!") +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum Asset { + UserResource, + ProtocolResource, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct AssetIndexedData { + user_resource: T, + protocol_resource: T, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum EqualityCheck { + ExactlyEquals(T), + ApproximatelyEquals { value: T, acceptable_difference: T }, +} + +fn test_exact_defiplaza_fees_amounts( + // The initial amount of liquidity to provide when creating the liquidity + // pool. + initial_liquidity: AssetIndexedData, + // The price to set as the initial price of the pool. + initial_price: Decimal, + // The fee percentage of the pool + fee_percentage: Decimal, + // The contribution that the user will make to the pool + user_contribution: AssetIndexedData, + // The swaps to perform on the pool. + swaps: Vec<(Asset, Decimal)>, + // Equality checks to perform when closing the liquidity position. + expected_fees: AssetIndexedData>, +) -> Result<(), RuntimeError> { + let Environment { + environment: ref mut env, + mut defiplaza_v2, + resources: ResourceInformation { bitcoin, .. }, + .. + } = ScryptoTestEnv::new_with_configuration(Configuration { + maximum_allowed_relative_price_difference: dec!(0.05), + ..Default::default() + })?; + + let resources = AssetIndexedData { + user_resource: bitcoin, + protocol_resource: XRD, + }; + + // Creating a new defiplaza pair so we can initialize it the way that we + // desire and without any constraints from the environment. + let mut pool = DefiPlazaV2PoolInterfaceScryptoTestStub::instantiate_pair( + OwnerRole::None, + resources.user_resource, + resources.protocol_resource, + PairConfig { + k_in: dec!("0.4"), + k_out: dec!("1"), + fee: fee_percentage, + decay_factor: dec!("0.9512"), + }, + initial_price, + defiplaza_v2.package, + env, + )?; + + // Providing the desired initial contribution to the pool. + [ + (resources.user_resource, initial_liquidity.user_resource), + ( + resources.protocol_resource, + initial_liquidity.protocol_resource, + ), + ] + .map(|(resource_address, amount)| { + let bucket = ResourceManager(resource_address) + .mint_fungible(amount, env) + .unwrap(); + let (_, change) = pool.add_liquidity(bucket, None, env).unwrap(); + let change_amount = change + .map(|bucket| bucket.amount(env).unwrap()) + .unwrap_or(Decimal::ZERO); + assert_eq!(change_amount, Decimal::ZERO); + }); + + // Providing the user's contribution to the pool through the adapter + let [bucket_x, bucket_y] = [ + ( + resources.protocol_resource, + user_contribution.protocol_resource, + ), + (resources.user_resource, user_contribution.user_resource), + ] + .map(|(resource_address, amount)| { + ResourceManager(resource_address) + .mint_fungible(amount, env) + .unwrap() + }); + let OpenLiquidityPositionOutput { + pool_units, + change, + adapter_specific_information, + .. + } = defiplaza_v2.adapter.open_liquidity_position( + pool.try_into().unwrap(), + (bucket_x, bucket_y), + env, + )?; + + // Asset the user got back no change in this contribution + for bucket in change.into_values() { + let amount = bucket.amount(env)?; + assert_eq!(amount, Decimal::ZERO); + } + + // Performing the swaps specified by the user + for (asset, amount) in swaps.into_iter() { + let address = match asset { + Asset::ProtocolResource => resources.protocol_resource, + Asset::UserResource => resources.user_resource, + }; + let bucket = + ResourceManager(address).mint_fungible(amount, env).unwrap(); + let _ = pool.swap(bucket, env)?; + } + + // Close the liquidity position + let CloseLiquidityPositionOutput { fees, .. } = + defiplaza_v2.adapter.close_liquidity_position( + pool.try_into().unwrap(), + pool_units.into_values().collect(), + adapter_specific_information, + env, + )?; + + // Assert that the fees is what's expected. + for (resource_address, equality_check) in [ + (resources.protocol_resource, expected_fees.protocol_resource), + (resources.user_resource, expected_fees.user_resource), + ] { + // Get the fees + let resource_fees = fees.get(&resource_address).copied().unwrap(); + + // Perform the assertion + match equality_check { + EqualityCheck::ExactlyEquals(value) => { + assert_eq!(resource_fees, value) + } + EqualityCheck::ApproximatelyEquals { + value, + acceptable_difference, + } => { + assert!( + (resource_fees - value).checked_abs().unwrap() + <= acceptable_difference + ) + } + } + } + + Ok(()) +} From ed3d11cd715934b2bcd956c87d6f3de839960a4f Mon Sep 17 00:00:00 2001 From: Omar Date: Thu, 7 Mar 2024 13:30:21 +0300 Subject: [PATCH 16/17] [Tests]: Fix tests --- tests/tests/caviarnine_v1.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/tests/caviarnine_v1.rs b/tests/tests/caviarnine_v1.rs index cfd0a7f9..57645299 100644 --- a/tests/tests/caviarnine_v1.rs +++ b/tests/tests/caviarnine_v1.rs @@ -1512,6 +1512,9 @@ fn bin_amounts_reported_on_receipt_match_whats_reported_by_caviarnine( let mut caviarnine_reported_redemption_value = pool .get_redemption_bin_values( pool_units + .into_values() + .next() + .unwrap() .non_fungible_local_ids(env)? .first() .unwrap() @@ -1609,6 +1612,9 @@ fn bin_amounts_reported_on_receipt_match_whats_reported_by_caviarnine_with_price let mut caviarnine_reported_redemption_value = pool .get_redemption_bin_values( pool_units + .into_values() + .next() + .unwrap() .non_fungible_local_ids(env)? .first() .unwrap() @@ -1706,6 +1712,9 @@ fn bin_amounts_reported_on_receipt_match_whats_reported_by_caviarnine_with_price let mut caviarnine_reported_redemption_value = pool .get_redemption_bin_values( pool_units + .into_values() + .next() + .unwrap() .non_fungible_local_ids(env)? .first() .unwrap() From 14241898a852927644001bd17f9da1475cf1a087 Mon Sep 17 00:00:00 2001 From: Omar Date: Mon, 11 Mar 2024 14:45:14 +0300 Subject: [PATCH 17/17] [Defiplaza v2 Adapter v1]: Update the fee calculation and add tests for fees --- libraries/common/src/indexed_buckets.rs | 7 + packages/defiplaza-v2-adapter-v1/src/lib.rs | 301 +++++++++++++----- tests/tests/defiplaza_v2.rs | 336 +++++++++++++++----- 3 files changed, 502 insertions(+), 142 deletions(-) diff --git a/libraries/common/src/indexed_buckets.rs b/libraries/common/src/indexed_buckets.rs index b675e82f..b20cfe5d 100644 --- a/libraries/common/src/indexed_buckets.rs +++ b/libraries/common/src/indexed_buckets.rs @@ -130,6 +130,13 @@ impl IndexedBuckets { pub fn into_inner(self) -> IndexMap { self.0 } + + pub fn combine(mut self, other: Self) -> Self { + for bucket in other.0.into_values() { + self.insert(bucket) + } + self + } } impl Default for IndexedBuckets { diff --git a/packages/defiplaza-v2-adapter-v1/src/lib.rs b/packages/defiplaza-v2-adapter-v1/src/lib.rs index 6e3fcfab..8ff2c0de 100644 --- a/packages/defiplaza-v2-adapter-v1/src/lib.rs +++ b/packages/defiplaza-v2-adapter-v1/src/lib.rs @@ -326,89 +326,248 @@ pub mod adapter { (pool_units_bucket1, pool_units_bucket2) }; - // Getting the base and quote assets - let (base_resource_address, quote_resource_address) = - pool.get_tokens(); - // Decoding the adapter specific information as the type we expect // it to be. let DefiPlazaV2AdapterSpecificInformation { original_targets } = adapter_specific_information.as_typed().unwrap(); - let [old_base_target, old_quote_target] = - [base_resource_address, quote_resource_address].map( - |address| original_targets.get(&address).copied().unwrap(), - ); - - // Step 1: Get the pair's state - let pair_state = pool.get_state(); - - // Step 2 & 3: Determine which of the resources is in shortage and - // based on that determine what the new target should be. - let claimed_tokens = IndexedBuckets::from_buckets( - [pool_units1, pool_units2].into_iter().flat_map(|bucket| { - let resource_manager = bucket.resource_manager(); - let entry = ComponentAddress::try_from( - resource_manager - .get_metadata::<_, GlobalAddress>("pool") - .unwrap() - .unwrap(), - ) - .unwrap(); - let mut two_resource_pool = - Global::::from(entry); - let (bucket1, bucket2) = two_resource_pool.redeem(bucket); - [bucket1, bucket2] - }), - ); - let base_bucket = - claimed_tokens.get(&base_resource_address).unwrap(); - let quote_bucket = - claimed_tokens.get("e_resource_address).unwrap(); - let base_bucket_amount = base_bucket.amount(); - let quote_bucket_amount = quote_bucket.amount(); + // We have gotten two pools units, one of the base pool and + // another for the quote pool. We need to determine which is + // which and overall split things according to shortage and + // surplus. So, instead of referring to them as base and quote + // we would figure out what is in shortage and what is surplus. + + // First thing we do is store the pool units in a map where the + // key the address of the pool and the value is the pool units + // bucket. We find the address of the pool through metadata on + // the pool units since there is currently no other way to find + // this information. + let mut pool_component_to_pool_unit_mapping = + [pool_units1, pool_units2] + .into_iter() + .map(|bucket| { + let resource_manager = bucket.resource_manager(); + let pool_address = ComponentAddress::try_from( + resource_manager + .get_metadata::<_, GlobalAddress>("pool") + .unwrap() + .unwrap(), + ) + .unwrap(); + (pool_address, bucket) + }) + .collect::>(); + + // With the way we combined them above we now want to split them + // into base pool units and quote pool units. This is simple to + // do, just get the address of the base and quote pools and then + // do a simple `remove` from the above map. + let (base_pool_component, quote_pool_component) = pool.get_pools(); + let base_pool_units = pool_component_to_pool_unit_mapping + .remove(&base_pool_component) + .unwrap(); + let quote_pool_units = pool_component_to_pool_unit_mapping + .remove("e_pool_component) + .unwrap(); - let shortage = pair_state.shortage; - let shortage_state = ShortageState::from(shortage); - let (new_base_target, new_quote_target) = match shortage_state { + // At this point we have the the base and quote token addresses, + // pool addresses, and pool units. We can now split things as + // shortage and quote and stop referring to things as base and + // quote. + let (base_resource_address, quote_resource_address) = + pool.get_tokens(); + let pair_state = pool.get_state(); + let (claimed_resources, fees) = match ShortageState::from( + pair_state.shortage, + ) { + // The pool is in equilibrium, none of the assets are in + // shortage so there is no need to multiply anything by the + // target ratio. ShortageState::Equilibrium => { - (base_bucket_amount, quote_bucket_amount) + // Claiming the assets from the pools. + let [resources_claimed_from_base_resource_pool, resources_claimed_from_quote_resource_pool] = + [ + (base_pool_component, base_pool_units), + (quote_pool_component, quote_pool_units), + ] + .map( + |(pool_component_address, pool_units_bucket)| { + let mut pool = Global::::from( + pool_component_address, + ); + let (bucket1, bucket2) = + pool.redeem(pool_units_bucket); + IndexedBuckets::from_buckets([bucket1, bucket2]) + }, + ); + + // The target of the two resources is just the amount we + // got back when closing the liquidity position. + let new_target_of_base_resource = + resources_claimed_from_base_resource_pool + .get(&base_resource_address) + .map(|bucket| bucket.amount()) + .expect(UNEXPECTED_ERROR); + let new_target_of_quote_resource = + resources_claimed_from_quote_resource_pool + .get("e_resource_address) + .map(|bucket| bucket.amount()) + .expect(UNEXPECTED_ERROR); + + // Now that we have the target for the base and quote + // resources we can calculate the fees. + let base_resource_fees = original_targets + .get(&base_resource_address) + .expect(UNEXPECTED_ERROR) + .checked_sub(new_target_of_base_resource) + .expect(OVERFLOW_ERROR) + .max(dec!(0)); + let quote_resource_fees = original_targets + .get("e_resource_address) + .expect(UNEXPECTED_ERROR) + .checked_sub(new_target_of_quote_resource) + .expect(OVERFLOW_ERROR) + .max(dec!(0)); + + let fees = indexmap! { + base_resource_address => base_resource_fees, + quote_resource_address => quote_resource_fees, + }; + + let claimed_resources = + resources_claimed_from_base_resource_pool.combine( + resources_claimed_from_quote_resource_pool, + ); + + (claimed_resources, fees) + } + // One of the assets is in shortage and the other is in + // surplus. Determine which is which and sort the info. + ShortageState::Shortage(asset) => { + let ( + ( + shortage_asset_pool_component, + shortage_asset_pool_units, + shortage_asset_resource_address, + ), + ( + surplus_asset_pool_component, + surplus_asset_pool_units, + surplus_asset_resource_address, + ), + ) = match asset { + Asset::Base => ( + ( + base_pool_component, + base_pool_units, + base_resource_address, + ), + ( + quote_pool_component, + quote_pool_units, + quote_resource_address, + ), + ), + Asset::Quote => ( + ( + quote_pool_component, + quote_pool_units, + quote_resource_address, + ), + ( + base_pool_component, + base_pool_units, + base_resource_address, + ), + ), + }; + + // We have now split them into shortage and surplus and + // we can now close the liquidity positions and compute + // the new targets for the base and shortage assets. + let [resources_claimed_from_shortage_asset_pool, resources_claimed_from_surplus_asset_pool] = + [ + ( + shortage_asset_pool_component, + shortage_asset_pool_units, + ), + ( + surplus_asset_pool_component, + surplus_asset_pool_units, + ), + ] + .map( + |(pool_component_address, pool_units_bucket)| { + let mut pool = Global::::from( + pool_component_address, + ); + let (bucket1, bucket2) = + pool.redeem(pool_units_bucket); + IndexedBuckets::from_buckets([bucket1, bucket2]) + }, + ); + + // The target of the shortage asset can be calculated by + // multiplying the amount we got back from closing the + // position in the shortage pool by the target ratio of + // the pool in the current state. + let new_target_of_shortage_asset = + resources_claimed_from_shortage_asset_pool + .get(&shortage_asset_resource_address) + .map(|bucket| bucket.amount()) + .expect(UNEXPECTED_ERROR) + .checked_mul(pair_state.target_ratio) + .expect(OVERFLOW_ERROR); + + // The target of the surplus asset is simple, its the + // amount we got back when we closed the position in + // the surplus pool. + let new_target_of_surplus_asset = + resources_claimed_from_surplus_asset_pool + .get(&surplus_asset_resource_address) + .map(|bucket| bucket.amount()) + .expect(UNEXPECTED_ERROR); + + // Now that we have the target for the shortage and + // surplus assets we can calculate the fees earned on + // those assets. Its calculated by subtracting the + // new targets from the original targets. + let shortage_asset_fees = new_target_of_shortage_asset + .checked_sub( + original_targets + .get(&shortage_asset_resource_address) + .copied() + .expect(UNEXPECTED_ERROR), + ) + .expect(OVERFLOW_ERROR) + .max(dec!(0)); + let surplus_asset_fees = new_target_of_surplus_asset + .checked_sub( + original_targets + .get(&surplus_asset_resource_address) + .copied() + .expect(UNEXPECTED_ERROR), + ) + .expect(OVERFLOW_ERROR) + .max(dec!(0)); + + let fees = indexmap! { + shortage_asset_resource_address => shortage_asset_fees, + surplus_asset_resource_address => surplus_asset_fees, + }; + + let claimed_resources = + resources_claimed_from_shortage_asset_pool + .combine(resources_claimed_from_surplus_asset_pool); + + (claimed_resources, fees) } - ShortageState::Shortage(Asset::Base) => ( - base_bucket_amount - .checked_mul(pair_state.target_ratio) - .expect(OVERFLOW_ERROR), - quote_bucket_amount, - ), - ShortageState::Shortage(Asset::Quote) => ( - base_bucket_amount, - quote_bucket_amount - .checked_mul(pair_state.target_ratio) - .expect(OVERFLOW_ERROR), - ), }; - // Steps 4 and 5 - let base_fees = std::cmp::max( - new_base_target - .checked_sub(old_base_target) - .expect(OVERFLOW_ERROR), - Decimal::ZERO, - ); - let quote_fees = std::cmp::max( - new_quote_target - .checked_sub(old_quote_target) - .expect(OVERFLOW_ERROR), - Decimal::ZERO, - ); - CloseLiquidityPositionOutput { - resources: claimed_tokens, + resources: claimed_resources, others: vec![], - fees: indexmap! { - base_resource_address => base_fees, - quote_resource_address => quote_fees, - }, + fees, } } diff --git a/tests/tests/defiplaza_v2.rs b/tests/tests/defiplaza_v2.rs index 04f9e792..2df0e7b2 100644 --- a/tests/tests/defiplaza_v2.rs +++ b/tests/tests/defiplaza_v2.rs @@ -918,37 +918,231 @@ fn pool_reported_price_and_quote_reported_price_are_similar_with_quote_resource_ } #[test] -#[ignore = "Awaiting defiplaza response"] fn exact_fee_test1() { test_exact_defiplaza_fees_amounts( // Initial supply for the pool. + None, + // Initial price of the pool + dec!(1), + // The pair config of the pool* + PairConfig { + k_in: dec!(0.5), + k_out: dec!(1), + fee: dec!(0.02), + decay_factor: dec!(0.9512), + }, + // User contribution to the pool. This would mean that the user would + // own 100% of the pool. + AssetIndexedData { + user_resource: dec!(5000), + protocol_resource: dec!(5000), + }, + // The swaps to perform - the asset you see is the input asset + vec![(Asset::UserResource, dec!(5000))], + ) + .expect("Should not fail!") +} + +#[test] +fn exact_fee_test2() { + test_exact_defiplaza_fees_amounts( + // Initial supply for the pool. + None, + // Initial price of the pool + dec!(1), + // The pair config of the pool* + PairConfig { + k_in: dec!(0.5), + k_out: dec!(1), + fee: dec!(0.02), + decay_factor: dec!(0.9512), + }, + // User contribution to the pool. This would mean that the user would + // own 100% of the pool. + AssetIndexedData { + user_resource: dec!(5000), + protocol_resource: dec!(5000), + }, + // The swaps to perform - the asset you see is the input asset + vec![(Asset::ProtocolResource, dec!(5000))], + ) + .expect("Should not fail!") +} + +#[test] +fn exact_fee_test3() { + test_exact_defiplaza_fees_amounts( + // Initial supply for the pool. + None, + // Initial price of the pool + dec!(1), + // The pair config of the pool* + PairConfig { + k_in: dec!(0.5), + k_out: dec!(1), + fee: dec!(0.02), + decay_factor: dec!(0.9512), + }, + // User contribution to the pool. This would mean that the user would + // own 100% of the pool. + AssetIndexedData { + user_resource: dec!(5000), + protocol_resource: dec!(5000), + }, + // The swaps to perform - the asset you see is the input asset + vec![ + (Asset::UserResource, dec!(5000)), + (Asset::ProtocolResource, dec!(1000)), + ], + ) + .expect("Should not fail!") +} + +#[test] +fn exact_fee_test4() { + test_exact_defiplaza_fees_amounts( + // Initial supply for the pool. + None, + // Initial price of the pool + dec!(1), + // The pair config of the pool* + PairConfig { + k_in: dec!(0.5), + k_out: dec!(1), + fee: dec!(0.02), + decay_factor: dec!(0.9512), + }, + // User contribution to the pool. This would mean that the user would + // own 100% of the pool. + AssetIndexedData { + user_resource: dec!(5000), + protocol_resource: dec!(5000), + }, + // The swaps to perform - the asset you see is the input asset + vec![ + (Asset::ProtocolResource, dec!(5000)), + (Asset::UserResource, dec!(1000)), + ], + ) + .expect("Should not fail!") +} + +#[test] +fn exact_fee_test5() { + test_exact_defiplaza_fees_amounts( + // Initial supply for the pool. + None, + // Initial price of the pool + dec!(1), + // The pair config of the pool* + PairConfig { + k_in: dec!(0.5), + k_out: dec!(1), + fee: dec!(0.02), + decay_factor: dec!(0.9512), + }, + // User contribution to the pool. This would mean that the user would + // own 100% of the pool. AssetIndexedData { - protocol_resource: dec!(100_000), - user_resource: dec!(100_000), + user_resource: dec!(5000), + protocol_resource: dec!(5000), }, + // The swaps to perform - the asset you see is the input asset + vec![ + (Asset::UserResource, dec!(5000)), + (Asset::ProtocolResource, dec!(10_000)), + ], + ) + .expect("Should not fail!") +} + +#[test] +fn exact_fee_test6() { + test_exact_defiplaza_fees_amounts( + // Initial supply for the pool. + None, // Initial price of the pool dec!(1), - // The fee percentage of the pool - dec!(0.03), + // The pair config of the pool* + PairConfig { + k_in: dec!(0.5), + k_out: dec!(1), + fee: dec!(0.02), + decay_factor: dec!(0.9512), + }, // User contribution to the pool. This would mean that the user would - // own 0.1% of the pool + // own 100% of the pool. AssetIndexedData { - user_resource: dec!(100), - protocol_resource: dec!(100), + user_resource: dec!(5000), + protocol_resource: dec!(5000), }, // The swaps to perform - the asset you see is the input asset - vec![(Asset::ProtocolResource, dec!(1_000))], - // The fees to expect - with 0.1% pool ownership of the pool and fees of - // 3% then we expect to see 0.03 of the protocol resource in fees (as it - // was the input in the swap) and none of the user resource in fees. + vec![ + (Asset::ProtocolResource, dec!(5000)), + (Asset::UserResource, dec!(10_000)), + ], + ) + .expect("Should not fail!") +} + +#[test] +fn exact_fee_test7() { + test_exact_defiplaza_fees_amounts( + // Initial supply for the pool. + None, + // Initial price of the pool + dec!(1), + // The pair config of the pool* + PairConfig { + k_in: dec!(0.5), + k_out: dec!(1), + fee: dec!(0.02), + decay_factor: dec!(0.9512), + }, + // User contribution to the pool. This would mean that the user would + // own 100% of the pool. AssetIndexedData { - user_resource: EqualityCheck::ExactlyEquals(dec!(0)), - protocol_resource: EqualityCheck::ExactlyEquals(dec!(0.03)), + user_resource: dec!(5000), + protocol_resource: dec!(5000), }, + // The swaps to perform - the asset you see is the input asset + vec![ + (Asset::ProtocolResource, dec!(3996)), + (Asset::UserResource, dec!(898)), + (Asset::ProtocolResource, dec!(7953)), + (Asset::ProtocolResource, dec!(3390)), + (Asset::ProtocolResource, dec!(4297)), + (Asset::ProtocolResource, dec!(2252)), + (Asset::UserResource, dec!(5835)), + (Asset::ProtocolResource, dec!(5585)), + (Asset::UserResource, dec!(7984)), + (Asset::ProtocolResource, dec!(8845)), + (Asset::ProtocolResource, dec!(4511)), + (Asset::UserResource, dec!(1407)), + (Asset::UserResource, dec!(4026)), + (Asset::UserResource, dec!(8997)), + (Asset::ProtocolResource, dec!(1950)), + (Asset::UserResource, dec!(8016)), + (Asset::UserResource, dec!(8322)), + (Asset::UserResource, dec!(5149)), + (Asset::ProtocolResource, dec!(6411)), + (Asset::ProtocolResource, dec!(1013)), + (Asset::ProtocolResource, dec!(3333)), + (Asset::ProtocolResource, dec!(4130)), + (Asset::UserResource, dec!(2786)), + (Asset::UserResource, dec!(5828)), + (Asset::UserResource, dec!(8974)), + (Asset::UserResource, dec!(6476)), + (Asset::ProtocolResource, dec!(8942)), + (Asset::UserResource, dec!(2159)), + (Asset::UserResource, dec!(8387)), + (Asset::UserResource, dec!(2830)), + ], ) .expect("Should not fail!") } +#[allow(dead_code)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] enum Asset { UserResource, @@ -961,26 +1155,24 @@ struct AssetIndexedData { protocol_resource: T, } -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -enum EqualityCheck { - ExactlyEquals(T), - ApproximatelyEquals { value: T, acceptable_difference: T }, -} - +/// This test will open a position for the user in a Defiplaza liquidity +/// pool and then perform a bunch of swaps to generate fees and then asset +/// that the amount of fees obtained as reported by the adapter matches the +/// amount that we expect the fees to be. An important note, Defiplaza fees +/// are collected on the output token and not the input token, so they're a +/// percentage of the output amount. fn test_exact_defiplaza_fees_amounts( // The initial amount of liquidity to provide when creating the liquidity // pool. - initial_liquidity: AssetIndexedData, + initial_liquidity: Option>, // The price to set as the initial price of the pool. initial_price: Decimal, - // The fee percentage of the pool - fee_percentage: Decimal, + // The pair configuration of the defiplaza pool + pair_configuration: PairConfig, // The contribution that the user will make to the pool user_contribution: AssetIndexedData, // The swaps to perform on the pool. swaps: Vec<(Asset, Decimal)>, - // Equality checks to perform when closing the liquidity position. - expected_fees: AssetIndexedData>, ) -> Result<(), RuntimeError> { let Environment { environment: ref mut env, @@ -1003,35 +1195,32 @@ fn test_exact_defiplaza_fees_amounts( OwnerRole::None, resources.user_resource, resources.protocol_resource, - PairConfig { - k_in: dec!("0.4"), - k_out: dec!("1"), - fee: fee_percentage, - decay_factor: dec!("0.9512"), - }, + pair_configuration, initial_price, defiplaza_v2.package, env, )?; // Providing the desired initial contribution to the pool. - [ - (resources.user_resource, initial_liquidity.user_resource), - ( - resources.protocol_resource, - initial_liquidity.protocol_resource, - ), - ] - .map(|(resource_address, amount)| { - let bucket = ResourceManager(resource_address) - .mint_fungible(amount, env) - .unwrap(); - let (_, change) = pool.add_liquidity(bucket, None, env).unwrap(); - let change_amount = change - .map(|bucket| bucket.amount(env).unwrap()) - .unwrap_or(Decimal::ZERO); - assert_eq!(change_amount, Decimal::ZERO); - }); + if let Some(initial_liquidity) = initial_liquidity { + [ + (resources.user_resource, initial_liquidity.user_resource), + ( + resources.protocol_resource, + initial_liquidity.protocol_resource, + ), + ] + .map(|(resource_address, amount)| { + let bucket = ResourceManager(resource_address) + .mint_fungible(amount, env) + .unwrap(); + let (_, change) = pool.add_liquidity(bucket, None, env).unwrap(); + let change_amount = change + .map(|bucket| bucket.amount(env).unwrap()) + .unwrap_or(Decimal::ZERO); + assert_eq!(change_amount, Decimal::ZERO); + }); + } // Providing the user's contribution to the pool through the adapter let [bucket_x, bucket_y] = [ @@ -1064,6 +1253,7 @@ fn test_exact_defiplaza_fees_amounts( } // Performing the swaps specified by the user + let mut expected_fee_amounts = IndexMap::::new(); for (asset, amount) in swaps.into_iter() { let address = match asset { Asset::ProtocolResource => resources.protocol_resource, @@ -1071,7 +1261,14 @@ fn test_exact_defiplaza_fees_amounts( }; let bucket = ResourceManager(address).mint_fungible(amount, env).unwrap(); - let _ = pool.swap(bucket, env)?; + let (output, _) = pool.swap(bucket, env)?; + let output_resource_address = output.resource_address(env)?; + let swap_output_amount = output.amount(env)?; + let fee = swap_output_amount / (Decimal::ONE - pair_configuration.fee) + * pair_configuration.fee; + *expected_fee_amounts + .entry(output_resource_address) + .or_default() += fee; } // Close the liquidity position @@ -1084,28 +1281,25 @@ fn test_exact_defiplaza_fees_amounts( )?; // Assert that the fees is what's expected. - for (resource_address, equality_check) in [ - (resources.protocol_resource, expected_fees.protocol_resource), - (resources.user_resource, expected_fees.user_resource), - ] { - // Get the fees - let resource_fees = fees.get(&resource_address).copied().unwrap(); - - // Perform the assertion - match equality_check { - EqualityCheck::ExactlyEquals(value) => { - assert_eq!(resource_fees, value) - } - EqualityCheck::ApproximatelyEquals { - value, - acceptable_difference, - } => { - assert!( - (resource_fees - value).checked_abs().unwrap() - <= acceptable_difference - ) - } - } + for resource_address in + [resources.protocol_resource, resources.user_resource] + { + let expected_fees = expected_fee_amounts + .get(&resource_address) + .copied() + .unwrap_or_default(); + let fees = fees.get(&resource_address).copied().unwrap_or_default(); + + let resource_name = if resource_address == resources.protocol_resource { + "protocol" + } else { + "user" + }; + + assert!( + expected_fees - fees <= dec!(0.000001), + "{resource_name} resource assertion failed. Expected: {expected_fees}, Actual: {fees}" + ); } Ok(())