diff --git a/Cargo.lock b/Cargo.lock index 892e2728..c7e03397 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" @@ -186,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.4" @@ -231,6 +260,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" @@ -296,6 +336,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" @@ -315,6 +370,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.2" @@ -390,6 +456,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" @@ -568,6 +644,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" @@ -661,6 +750,41 @@ 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 = "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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -749,6 +873,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" @@ -756,6 +895,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -764,12 +904,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" @@ -788,8 +950,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", @@ -919,6 +1084,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.12" @@ -1046,6 +1217,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" @@ -1083,6 +1268,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" @@ -1098,12 +1289,50 @@ 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 = "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" 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 = "jobserver" version = "0.1.28" @@ -1137,6 +1366,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" @@ -1149,12 +1384,49 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +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" @@ -1186,6 +1458,38 @@ 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 = "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" @@ -1208,6 +1512,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" @@ -1253,7 +1563,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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "radix-engine-common", "radix-engine-derive", @@ -1280,6 +1590,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" @@ -1400,21 +1758,104 @@ dependencies = [ ] [[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.101" +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" +dependencies = [ + "cc", + "libc", + "pkg-config", + "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 = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" +checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7" dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", + "num-traits", ] [[package]] @@ -1441,6 +1882,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" @@ -1486,6 +1933,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" @@ -1546,6 +1999,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" @@ -1579,6 +2042,53 @@ 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 = [ + "bitflags 2.4.2", + "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", + "radix-engine-interface", + "radix-engine-store-interface", + "rand 0.8.5", + "sbor", + "sbor-json", + "scrypto-unit", + "serde_json", + "state-manager", + "transaction", +] + [[package]] name = "pulldown-cmark" version = "0.9.6" @@ -1602,7 +2112,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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "bitflags 1.3.2", "colored", @@ -1632,7 +2142,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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "bech32", "blake2", @@ -1657,7 +2167,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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "proc-macro2", "quote", @@ -1668,7 +2178,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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "bitflags 1.3.2", "const-sha1", @@ -1690,7 +2200,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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "paste", "proc-macro2", @@ -1702,7 +2212,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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "fixedstr", ] @@ -1710,10 +2220,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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "hex", - "itertools", + "itertools 0.10.5", "paste", "radix-engine", "radix-engine-interface", @@ -1726,10 +2236,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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "hex", - "itertools", + "itertools 0.10.5", "radix-engine-common", "radix-engine-derive", "radix-engine-interface", @@ -1740,10 +2250,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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "hex", - "itertools", + "itertools 0.10.5", "radix-engine-common", "radix-engine-derive", "radix-engine-store-interface", @@ -1822,6 +2332,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" @@ -1837,6 +2356,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", ] @@ -1847,6 +2368,8 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" dependencies = [ + "aho-corasick", + "memchr", "regex-syntax", ] @@ -1858,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", @@ -1900,7 +2423,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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "proc-macro2", "quote", @@ -1908,12 +2431,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" @@ -1969,7 +2508,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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "const-sha1", "hex", @@ -1983,7 +2522,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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "proc-macro2", "sbor-derive-common", @@ -1992,15 +2531,30 @@ 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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "const-sha1", - "itertools", + "itertools 0.10.5", "proc-macro2", "quote", "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" @@ -2034,7 +2588,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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "bech32", "const-sha1", @@ -2056,7 +2610,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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "proc-macro2", "quote", @@ -2093,7 +2647,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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "bitflags 1.3.2", "radix-engine-common", @@ -2104,7 +2658,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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "native-sdk", "ouroboros", @@ -2122,7 +2676,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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "radix-engine", "radix-engine-interface", @@ -2214,6 +2768,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", @@ -2293,6 +2848,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" @@ -2312,6 +2891,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" @@ -2336,6 +2925,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" @@ -2358,6 +2956,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" @@ -2444,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", @@ -2537,6 +3166,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" @@ -2546,6 +3185,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" @@ -2603,11 +3255,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" @@ -2618,6 +3284,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" @@ -2679,9 +3356,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" @@ -2689,12 +3378,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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "bech32", "hex", @@ -2706,6 +3446,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" @@ -2786,7 +3544,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=4887c5e4be2603433592ed290b70b1a0c03cced3#4887c5e4be2603433592ed290b70b1a0c03cced3" dependencies = [ "indexmap 2.0.0-pre", "serde", @@ -2802,6 +3560,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" @@ -3288,3 +4052,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..1b59d8db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ members = [ "libraries/ports-interface", "libraries/scrypto-math", # Tools - "tools/bootstrap", + "tools/publishing-tool", # Tests "tests" ] @@ -27,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 = "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 = "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 = "ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" } -scrypto-test = { git = "https://github.com/radixdlt/radixdlt-scrypto", rev = "ef169b1e1348b8dbad977ba81d086ee1e80d6ff8" } +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' @@ -52,4 +52,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 = "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 edbe013a..47845e4a 100644 --- a/packages/ignition/src/blueprint.rs +++ b/packages/ignition/src/blueprint.rs @@ -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/packages/ociswap-v2-adapter-v1/src/lib.rs b/packages/ociswap-v2-adapter-v1/src/lib.rs index 3ad18d58..7e03d93d 100644 --- a/packages/ociswap-v2-adapter-v1/src/lib.rs +++ b/packages/ociswap-v2-adapter-v1/src/lib.rs @@ -171,12 +171,24 @@ pub mod adapter { let (receipt, change_x, change_y) = pool.add_liquidity(lower_tick, upper_tick, bucket_x, bucket_y); + 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), 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, + liquidity_receipt_data: non_fungible_data, + }, + ) + .expect(UNEXPECTED_ERROR), } } @@ -246,10 +258,28 @@ 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, + + /// The data of the underlying liquidity receipt + pub liquidity_receipt_data: LiquidityPosition, +} impl From for AnyValue { fn from(value: OciswapV2AdapterSpecificInformation) -> Self { 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, +} 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/tests/src/environment.rs b/tests/src/environment.rs index f6cc53cc..438cf334 100644 --- a/tests/src/environment.rs +++ b/tests/src/environment.rs @@ -452,8 +452,7 @@ impl ScryptoTestEnv { let resource_y = ResourceManager(XRD).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 59dea3d9..57645299 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 bb3f4fb4..2df0e7b2 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 new file mode 100644 index 00000000..70229bb8 --- /dev/null +++ b/tools/publishing-tool/Cargo.toml @@ -0,0 +1,56 @@ +[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", +] } +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 = { 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"] } +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 new file mode 100644 index 00000000..e03ae788 --- /dev/null +++ b/tools/publishing-tool/src/cli/default_configurations/mainnet_testing.rs @@ -0,0 +1,306 @@ +use common::prelude::*; +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, +) -> 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(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: 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_and_dapp_definition: 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 new file mode 100644 index 00000000..d5f6e946 --- /dev/null +++ b/tools/publishing-tool/src/cli/default_configurations/mod.rs @@ -0,0 +1,42 @@ +mod mainnet_testing; +mod stokenet_testing; + +use clap::*; +use publishing_tool::publishing::*; +use transaction::prelude::*; + +#[derive(ValueEnum, Clone, Copy, Debug)] +pub enum ConfigurationSelector { + MainnetTesting, + StokenetTesting, +} + +impl ConfigurationSelector { + pub fn configuration( + self, + notary_private_key: &PrivateKey, + ) -> PublishingConfiguration { + match self { + 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..eee3d1f6 --- /dev/null +++ b/tools/publishing-tool/src/cli/default_configurations/stokenet_testing.rs @@ -0,0 +1,255 @@ +use common::prelude::*; +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, +) -> 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_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 + .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/publish.rs b/tools/publishing-tool/src/cli/publish.rs new file mode 100644 index 00000000..51faba50 --- /dev/null +++ b/tools/publishing-tool/src/cli/publish.rs @@ -0,0 +1,79 @@ +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::*; +use transaction::prelude::*; + +#[derive(Parser, Debug)] +pub struct Publish { + /// The configuration that the user wants to use when publishing. + configuration_selector: ConfigurationSelector, + + /// 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 { + 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); + 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 mut gateway_network_provider = GatewayNetworkConnector::new( + gateway_base_url, + network_definition.clone(), + PollingConfiguration { + interval_in_seconds: 10, + retries: 10, + }, + ); + let receipt = publish(&configuration, &mut gateway_network_provider)?; + writeln!(f, "{}", to_json(&receipt, &network_definition)) + .map_err(Error::IoError) + } +} 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/error.rs b/tools/publishing-tool/src/error.rs new file mode 100644 index 00000000..d428f402 --- /dev/null +++ b/tools/publishing-tool/src/error.rs @@ -0,0 +1,24 @@ +use crate::network_connection_provider::*; +use crate::publishing::*; +use state_manager::traits::*; + +#[derive(Debug)] +pub enum Error { + PrivateKeyError, + GatewayExecutorError(PublishingError), + SimulatorExecutorError(PublishingError), + IoError(std::io::Error), + RocksDbOpenError(DatabaseConfigValidationError), +} + +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/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/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/network_connection_provider/execution_service.rs b/tools/publishing-tool/src/network_connection_provider/execution_service.rs new file mode 100644 index 00000000..d3108a1b --- /dev/null +++ b/tools/publishing-tool/src/network_connection_provider/execution_service.rs @@ -0,0 +1,207 @@ +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::*; + +use super::*; + +/// A simple execution service whose main responsibilities is to construct, +/// submit, and return the result of transactions. +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. + 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 [&'e PrivateKey], +} + +impl<'e, E: NetworkConnectionProvider> ExecutionService<'e, E> { + pub fn new( + executor: &'e mut E, + fee_payer_account_address: ComponentAddress, + notary_private_key: &'e PrivateKey, + signers_private_keys: &'e [&'e PrivateKey], + ) -> Self { + Self { + executor, + fee_payer_account_address, + notary_private_key, + signers_private_keys, + } + } + + pub fn execute_manifest( + &mut self, + mut manifest: TransactionManifestV1, + ) -> 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| { + 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 + .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: self + .signers_private_keys + .iter() + .map(|private_key| private_key.public_key()) + .unique() + .collect(), + flags: PreviewFlags { + use_free_credit: true, + 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 + 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( + 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.clone()); + 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)?; + + // 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(E), + ManifestDecompilationFailed(DecompileError), + TransactionExecutionWasNotSuccessful { + manifest: String, + reason: String, + }, + TransactionPreviewWasNotSuccessful( + TransactionManifestV1, + TransactionReceipt, + ), +} diff --git a/tools/publishing-tool/src/network_connection_provider/gateway_connector.rs b/tools/publishing-tool/src/network_connection_provider/gateway_connector.rs new file mode 100644 index 00000000..cfb8e810 --- /dev/null +++ b/tools/publishing-tool/src/network_connection_provider/gateway_connector.rs @@ -0,0 +1,356 @@ +use super::*; +use gateway_client::apis::configuration::*; +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::*; +use radix_engine::transaction::*; +use transaction::manifest::*; +use transaction::prelude::*; + +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. + pub network_definition: NetworkDefinition, + /// The configuration to use when polling for the transaction status. + pub polling_configuration: PollingConfiguration, +} + +impl GatewayNetworkConnector { + 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 NetworkConnectionProvider for GatewayNetworkConnector { + 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 => { + // 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 { + 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( + ExecutionReceiptSuccessContents { 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 get_network_definition( + &mut self, + ) -> 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( + 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), + 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/network_connection_provider/traits.rs b/tools/publishing-tool/src/network_connection_provider/traits.rs new file mode 100644 index 00000000..7a1cc9a8 --- /dev/null +++ b/tools/publishing-tool/src/network_connection_provider/traits.rs @@ -0,0 +1,57 @@ +use radix_engine::transaction::TransactionReceiptV1; +use transaction::prelude::*; + +/// 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( + &mut self, + notarized_transaction: &NotarizedTransactionV1, + ) -> Result; + + fn preview_transaction( + &mut self, + preview_intent: PreviewIntentV1, + ) -> Result; + + fn get_current_epoch(&mut self) -> Result; + + 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 +/// 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(ExecutionReceiptSuccessContents), + 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, +} + +#[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..1defbba4 --- /dev/null +++ b/tools/publishing-tool/src/publishing/configuration.rs @@ -0,0 +1,283 @@ +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, + + /// 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)] +pub struct PublishingReceipt { + pub packages: Entities, + pub components: Entities, + pub exchange_information: ExchangeIndexedData< + 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; + + /// 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; + } +} + +#[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_and_dapp_definition: + Option<(ComponentAddress, ComponentAddress)>, +} + +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..98c170c6 --- /dev/null +++ b/tools/publishing-tool/src/publishing/handler.rs @@ -0,0 +1,1579 @@ +#![allow(clippy::arithmetic_side_effects, clippy::too_many_arguments)] + +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::*; +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)?; + } + + // 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)?; + } + + // 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. + { + 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) + }), + }, + badges: resolved_badges.map(|(_, address)| *address), + }) +} + +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_and_dapp_definition: + Some(( + ociswap_v2_registry_component, + ociswap_v2_dapp_definition_account, + )), + } = 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( + ), + ociswap_v2_dapp_definition_account, + ), + ) + .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() +}