diff --git a/Cargo.lock b/Cargo.lock index db57d0a0..1668d26d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,18 @@ dependencies = [ "gimli", ] +[[package]] +name = "address-macros" +version = "0.1.0" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "radix-engine-common", + "radix-engine-interface", + "syn 2.0.52", +] + [[package]] name = "adler" version = "1.0.2" @@ -2990,13 +3002,13 @@ dependencies = [ name = "stateful-tests" version = "0.1.0" dependencies = [ + "address-macros", "caviarnine-v1-adapter-v1", "common", "defiplaza-v2-adapter-v1", "extend", "gateway-client", "ignition", - "lazy_static", "macro_rules_attribute", "ociswap-v1-adapter-v1", "ociswap-v2-adapter-v1", diff --git a/Cargo.toml b/Cargo.toml index fd9163db..877b1ddd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ "libraries/scrypto-interface", "libraries/ports-interface", "libraries/scrypto-math", + "libraries/address-macros", # Tools "tools/publishing-tool", # Tests diff --git a/libraries/address-macros/Cargo.toml b/libraries/address-macros/Cargo.toml new file mode 100644 index 00000000..22018ac4 --- /dev/null +++ b/libraries/address-macros/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "address-macros" +version.workspace = true +edition.workspace = true +description = "A crate for decoding addresses at compile-time." + +[dependencies] +radix-engine-common = { workspace = true } +radix-engine-interface = { workspace = true } + +proc-macro2 = { version = "1.0.76" } +syn = "2.0.52" +quote = "1.0.35" +paste = "1.0.14" + +[lib] +doctest = false +proc-macro = true + +[lints] +workspace = true diff --git a/libraries/address-macros/src/lib.rs b/libraries/address-macros/src/lib.rs new file mode 100644 index 00000000..f5653a73 --- /dev/null +++ b/libraries/address-macros/src/lib.rs @@ -0,0 +1,70 @@ +use proc_macro::*; +use radix_engine_interface::prelude::*; + +macro_rules! tri { + ($expr: expr) => { + match $expr { + Ok(item) => item, + Err(err) => return err.into_compile_error().into(), + } + }; +} + +macro_rules! impl_address_proc_macro { + ( + $type_ident: ident + ) => { + paste::paste! { + #[proc_macro] + pub fn [< $type_ident: snake >](item: TokenStream) -> TokenStream { + let literal_string = tri!(syn::parse::(item)); + let node_id = tri!(decode_string_into_node_id(&literal_string)); + let node_id_bytes = node_id.0; + let _ = tri!($type_ident::try_from(node_id_bytes).map_err(|err| { + syn::Error::new_spanned(&literal_string, format!("{err:?}")) + })); + quote::quote! { + ::radix_engine_interface::prelude::$type_ident::new_or_panic( + [ #(#node_id_bytes),* ] + ) + } + .into() + } + } + }; +} + +impl_address_proc_macro!(ComponentAddress); +impl_address_proc_macro!(ResourceAddress); +impl_address_proc_macro!(PackageAddress); +impl_address_proc_macro!(InternalAddress); +impl_address_proc_macro!(GlobalAddress); + +#[proc_macro] +pub fn node_id(item: TokenStream) -> TokenStream { + let literal_string = tri!(syn::parse::(item)); + let node_id = tri!(decode_string_into_node_id(&literal_string)); + let node_id_bytes = node_id.0; + + quote::quote! { + ::radix_engine_interface::prelude::NodeId([ #(#node_id_bytes),* ]) + } + .into() +} + +fn decode_string_into_node_id( + address: &syn::LitStr, +) -> Result { + // Attempt to decode the value without network context. Error out if we + // can't decode it. + let (_, _, node_id_bytes) = + AddressBech32Decoder::validate_and_decode_ignore_hrp(&address.value()) + .map_err(|err| { + syn::Error::new_spanned(address, format!("{err:?}")) + })?; + + // Try to convert this into a NodeId which can fail due to the length. + node_id_bytes.try_into().map(NodeId).map_err(|_| { + syn::Error::new_spanned(address, "Address length is invalid") + }) +} diff --git a/testing/stateful-tests/Cargo.toml b/testing/stateful-tests/Cargo.toml index 9c6959dd..d872f5cc 100644 --- a/testing/stateful-tests/Cargo.toml +++ b/testing/stateful-tests/Cargo.toml @@ -36,15 +36,16 @@ caviarnine-v1-adapter-v1 = { path = "../../packages/caviarnine-v1-adapter-v1", f "manifest-builder-stubs" ] } +address-macros = { path = "../../libraries/address-macros" } + package-loader = { path = "../../libraries/package-loader" } gateway-client = { path = "../../libraries/gateway-client" } publishing-tool = { path = "../../tools/publishing-tool" } paste = { version = "1.0.14" } extend = { version = "1.2.0" } - macro_rules_attribute = { version = "0.2.0" } -lazy_static = "1.4.0" + [lints] workspace = true diff --git a/testing/stateful-tests/src/lib.rs b/testing/stateful-tests/src/lib.rs index b8662268..d5f5c70c 100644 --- a/testing/stateful-tests/src/lib.rs +++ b/testing/stateful-tests/src/lib.rs @@ -1,10 +1,10 @@ +use address_macros::*; +use common::prelude::Volatility::*; use common::prelude::*; use extend::*; -use publishing_tool::configuration_selector::*; use publishing_tool::database_overlay::*; -use publishing_tool::network_connection_provider::*; use publishing_tool::publishing::*; -use publishing_tool::*; +use publishing_tool::utils::to_json; use radix_engine::system::system_modules::*; use radix_engine::transaction::*; use radix_engine::vm::*; @@ -44,12 +44,12 @@ pub fn execute_test_within_environment(test_function: F) -> O where F: Fn( AccountAndControllingKey, - PublishingConfiguration, - PublishingReceipt, - ComponentAddress, + &PublishingReceipt, &mut StatefulTestRunner<'_>, ) -> O, { + let publishing_receipt = publishing_receipt(); + // Creating the database and the necessary overlays to run the tests. let overlayed_state_manager_database = UnmergeableSubstateDatabaseOverlay::new_unmergeable(get_database()); @@ -81,40 +81,10 @@ where ) .expect_commit_success(); - // The notary account address now has the fees required to be able to pay - // for the deployment of Ignition. We now get the configuration and run the - // deployment. - let mut simulator_network_connection_provider = - SimulatorNetworkConnector::new_with_test_runner( - test_runner, - NetworkDefinition::mainnet(), - ); - let publishing_configuration = ConfigurationSelector::MainnetProduction - .configuration(¬ary_private_key); - let publishing_receipt = publish( - &publishing_configuration, - &mut simulator_network_connection_provider, - ) - .expect("Publishing of Ignition must succeed!"); - let mut test_runner = - simulator_network_connection_provider.into_test_runner(); - - // Modifying the Ignition component state so that we can use it in tests. - // What we will modify is the Oracle to use where we will be using an actual - // oracle that is live on mainnet that prices are being submitted to. We - // also need to allow for opening and closing of liquidity positions. - // Additionally, we fund Ignition with XRD. - let oracle = component_address!( - "component_rdx1crty68w9d6ud4ecreewvpsvgyq0u9ta8syqrmuzelem593putyu79e" - ); + // Enabling contributions through Ignition test_runner .execute_manifest_with_enabled_modules( ManifestBuilder::new() - .call_method( - publishing_receipt.components.protocol_entities.ignition, - "set_oracle_adapter", - (oracle,), - ) .call_method( publishing_receipt.components.protocol_entities.ignition, "set_is_open_position_enabled", @@ -125,35 +95,11 @@ where "set_is_close_position_enabled", (true,), ) - .mint_fungible(XRD, dec!(200_000_000_000_000)) - .take_from_worktop(XRD, dec!(100_000_000_000_000), "volatile") - .take_from_worktop( - XRD, - dec!(100_000_000_000_000), - "non_volatile", + .call_method( + publishing_receipt.components.protocol_entities.ignition, + "set_maximum_allowed_price_difference_percentage", + (dec!(0.05),), ) - .with_name_lookup(|builder, _| { - let volatile = builder.bucket("volatile"); - let non_volatile = builder.bucket("non_volatile"); - - builder - .call_method( - publishing_receipt - .components - .protocol_entities - .ignition, - "deposit_protocol_resources", - (volatile, Volatility::Volatile), - ) - .call_method( - publishing_receipt - .components - .protocol_entities - .ignition, - "deposit_protocol_resources", - (non_volatile, Volatility::NonVolatile), - ) - }) .build(), EnabledModules::for_notarized_transaction() & !EnabledModules::COSTING @@ -206,13 +152,7 @@ where // We are now ready to execute the function callback and return its output // back - test_function( - test_account, - publishing_configuration, - publishing_receipt, - oracle, - &mut test_runner, - ) + test_function(test_account, publishing_receipt, &mut test_runner) } /// This macro can be applied to any function to turn it into a test function @@ -329,3 +269,233 @@ pub impl<'a> StatefulTestRunner<'a> { ) } } + +fn publishing_receipt() -> &'static PublishingReceipt { + static PUBLISHING_RECEIPT: OnceLock = OnceLock::new(); + PUBLISHING_RECEIPT.get_or_init(|| PublishingReceipt { + dapp_definition_account: component_address!( + "account_rdx1cxh9jq27n5vllmsexah8jj3txzue8yu236uekcnfr4hq5ptw8nn7f0" + ), + packages: Entities { + protocol_entities: ProtocolIndexedData { + ignition: package_address!( + "package_rdx1pksyy7cyun85mgnuqdv4z3wm68d3pkfwzfkqrchhsu358zpjjuv426" + ), + simple_oracle: package_address!( + "package_rdx1phx0yptt32290n0uym4aqh3zyyup4ykth4enph8a68ggp7c38dqaxw" + ), + }, + exchange_adapter_entities: ExchangeIndexedData { + ociswap_v2: package_address!( + "package_rdx1pknh02tzgdjk7fs9nxyckpdkjkz5jhcu87m78vajexurh99dk9yt22" + ), + defiplaza_v2: package_address!( + "package_rdx1p5q7uhr7kkrtr2ta6xl938txrk8r2cra02cpvf2le548jjrcsfvzkc" + ), + caviarnine_v1: package_address!( + "package_rdx1p5c0rcv7kwnjlyfpam5qfp0xnknz9rpdy0de7fhxj689mvfxdzj558" + ), + }, + }, + components: Entities { + protocol_entities: ProtocolIndexedData { + ignition: component_address!( + "component_rdx1cqplswlzpvw9yx687mcnvjuguy24veqk4c55rscjxl3pll7rxfs2dz" + ), + simple_oracle: component_address!( + "component_rdx1cr3psyfptwkktqusfg8ngtupr4wwfg32kz2xvh9tqh4c7pwkvlk2kn" + ), + }, + exchange_adapter_entities: ExchangeIndexedData { + ociswap_v2: component_address!( + "component_rdx1cqrsdg6ag5urfe3av7d6z9q04emgjv726f48uhmzpex54jpwcxasq3" + ), + defiplaza_v2: component_address!( + "component_rdx1cr2asvvh7s02l4pzez8szp6kck4f230h8rkxmf56347hwje5gg7vtc" + ), + caviarnine_v1: component_address!( + "component_rdx1cpjs0phmgzwmhxel74l256zqdp39d2rfvj6m54e5k758k2vma8grp9" + ), + }, + }, + exchange_information: ExchangeIndexedData { + ociswap_v2: Some(ExchangeInformation { + blueprint_id: BlueprintId { + package_address: package_address!( + "package_rdx1pkrgvskdkglfd2ar4jkpw5r2tsptk85gap4hzr9h3qxw6ca40ts8dt" + ), + blueprint_name: "PrecisionPool".into(), + }, + pools: UserResourceIndexedData { + bitcoin: component_address!( + "component_rdx1cpgmgrskahkxe4lnpp9s2f5ga0z8jkl7ne8gjmw3fc2224lxq505mr" + ), + ethereum: component_address!( + "component_rdx1crahf8qdh8fgm8mvzmq5w832h97q5099svufnqn26ue44fyezn7gnm" + ), + usdc: component_address!( + "component_rdx1cz8daq5nwmtdju4hj5rxud0ta26wf90sdk5r4nj9fqjcde5eht8p0f" + ), + usdt: component_address!( + "component_rdx1cz79xc57dpuhzd3wylnc88m3pyvfk7c5e03me2qv7x8wh9t6c3aw4g" + ), + }, + liquidity_receipt: resource_address!( + "resource_rdx1ngeqqquzmjrd6q6atyawlh7p29jrpshdayw7rklyjw4n5k7ks6plm8" + ), + }), + defiplaza_v2: Some(ExchangeInformation { + blueprint_id: BlueprintId { + package_address: package_address!( + "package_rdx1p4dhfl7qwthqqu6p2267m5nedlqnzdvfxdl6q7h8g85dflx8n06p93" + ), + blueprint_name: "PlazaPair".into(), + }, + pools: UserResourceIndexedData { + bitcoin: component_address!( + "component_rdx1czzqr5m40x3sklwntcmx8uw3ld5nj7marq66nm6erp3prw7rv8zu29" + ), + ethereum: component_address!( + "component_rdx1cr0nw5ppvryyqcv6thkslcltkw5cm3c2lvm2yr8jhh9rqe76stmars" + ), + usdc: component_address!( + "component_rdx1czmha58h7vw0e4qpxz8ga68cq6h5fjm27w2z43r0n6k9x65nvrjp4g" + ), + usdt: component_address!( + "component_rdx1crhrzxe6x35hwx3wmnnw0g8qs84p2hle6ud7n2q4ffzp0udluqm8hj" + ), + }, + liquidity_receipt: resource_address!( + "resource_rdx1ntmgj3amlsrj0qxzqwzlk99d7g0xkzv6mg8vd5egawvgd8nt5ypwa7" + ), + }), + caviarnine_v1: Some(ExchangeInformation { + blueprint_id: BlueprintId { + package_address: package_address!( + "package_rdx1p4r9rkp0cq67wmlve544zgy0l45mswn6h798qdqm47x4762h383wa3" + ), + blueprint_name: "QuantaSwap".into(), + }, + pools: UserResourceIndexedData { + bitcoin: component_address!( + "component_rdx1cp9w8443uyz2jtlaxnkcq84q5a5ndqpg05wgckzrnd3lgggpa080ed" + ), + ethereum: component_address!( + "component_rdx1cpsvw207842gafeyvf6tc0gdnq47u3mn74kvzszqlhc03lrns52v82" + ), + usdc: component_address!( + "component_rdx1cr6lxkr83gzhmyg4uxg49wkug5s4wwc3c7cgmhxuczxraa09a97wcu" + ), + usdt: component_address!( + "component_rdx1cqs338cyje65rk44zgmjvvy42qcszrhk9ewznedtkqd8l3crtgnmh5" + ), + }, + liquidity_receipt: resource_address!( + "resource_rdx1n2uzpxdlg90ajqy9r597xkffeefhacl8hqd6kpvmfmt56wlda0dzk9" + ), + }), + }, + protocol_configuration: ProtocolConfigurationReceipt { + protocol_resource: XRD, + user_resource_volatility: UserResourceIndexedData { + bitcoin: Volatile, + ethereum: Volatile, + usdc: NonVolatile, + usdt: NonVolatile, + }, + reward_rates: indexmap! { + LockupPeriod::from_months(9).unwrap() => dec!(0.125), + LockupPeriod::from_months(10).unwrap() => dec!(0.145), + LockupPeriod::from_months(11).unwrap() => dec!(0.17), + LockupPeriod::from_months(12).unwrap() => dec!(0.2), + }, + allow_opening_liquidity_positions: false, + allow_closing_liquidity_positions: false, + maximum_allowed_price_staleness_in_seconds: 60, + maximum_allowed_price_difference_percentage: dec!(0.05), + user_resources: UserResourceIndexedData { + bitcoin: resource_address!( + "resource_rdx1t580qxc7upat7lww4l2c4jckacafjeudxj5wpjrrct0p3e82sq4y75" + ), + ethereum: resource_address!( + "resource_rdx1th88qcj5syl9ghka2g9l7tw497vy5x6zaatyvgfkwcfe8n9jt2npww" + ), + usdc: resource_address!( + "resource_rdx1t4upr78guuapv5ept7d7ptekk9mqhy605zgms33mcszen8l9fac8vf" + ), + usdt: resource_address!( + "resource_rdx1thrvr3xfs2tarm2dl9emvs26vjqxu6mqvfgvqjne940jv0lnrrg7rw" + ), + }, + registered_pools: ExchangeIndexedData { + ociswap_v2: Some(UserResourceIndexedData { + bitcoin: component_address!( + "component_rdx1cpgmgrskahkxe4lnpp9s2f5ga0z8jkl7ne8gjmw3fc2224lxq505mr" + ), + ethereum: component_address!( + "component_rdx1crahf8qdh8fgm8mvzmq5w832h97q5099svufnqn26ue44fyezn7gnm" + ), + usdc: component_address!( + "component_rdx1cz8daq5nwmtdju4hj5rxud0ta26wf90sdk5r4nj9fqjcde5eht8p0f" + ), + usdt: component_address!( + "component_rdx1cz79xc57dpuhzd3wylnc88m3pyvfk7c5e03me2qv7x8wh9t6c3aw4g" + ), + }), + defiplaza_v2: Some(UserResourceIndexedData { + bitcoin: component_address!( + "component_rdx1czzqr5m40x3sklwntcmx8uw3ld5nj7marq66nm6erp3prw7rv8zu29" + ), + ethereum: component_address!( + "component_rdx1cr0nw5ppvryyqcv6thkslcltkw5cm3c2lvm2yr8jhh9rqe76stmars" + ), + usdc: component_address!( + "component_rdx1czmha58h7vw0e4qpxz8ga68cq6h5fjm27w2z43r0n6k9x65nvrjp4g" + ), + usdt: component_address!( + "component_rdx1crhrzxe6x35hwx3wmnnw0g8qs84p2hle6ud7n2q4ffzp0udluqm8hj" + ), + }), + caviarnine_v1: Some(UserResourceIndexedData { + bitcoin: component_address!( + "component_rdx1cp9w8443uyz2jtlaxnkcq84q5a5ndqpg05wgckzrnd3lgggpa080ed" + ), + ethereum: component_address!( + "component_rdx1cpsvw207842gafeyvf6tc0gdnq47u3mn74kvzszqlhc03lrns52v82" + ), + usdc: component_address!( + "component_rdx1cr6lxkr83gzhmyg4uxg49wkug5s4wwc3c7cgmhxuczxraa09a97wcu" + ), + usdt: component_address!( + "component_rdx1cqs338cyje65rk44zgmjvvy42qcszrhk9ewznedtkqd8l3crtgnmh5" + ), + }), + }, + }, + user_resources: UserResourceIndexedData { + bitcoin: resource_address!( + "resource_rdx1t580qxc7upat7lww4l2c4jckacafjeudxj5wpjrrct0p3e82sq4y75" + ), + ethereum: resource_address!( + "resource_rdx1th88qcj5syl9ghka2g9l7tw497vy5x6zaatyvgfkwcfe8n9jt2npww" + ), + usdc: resource_address!( + "resource_rdx1t4upr78guuapv5ept7d7ptekk9mqhy605zgms33mcszen8l9fac8vf" + ), + usdt: resource_address!( + "resource_rdx1thrvr3xfs2tarm2dl9emvs26vjqxu6mqvfgvqjne940jv0lnrrg7rw" + ), + }, + badges: BadgeIndexedData { + oracle_manager_badge: resource_address!( + "resource_rdx1th3yr5dlydnhw0lfp6r22x5l2fj9lv3t8f0enkp7j5ttnx3e09rhna" + ), + protocol_owner_badge: resource_address!( + "resource_rdx1t5ezhhs9cnua2thfnknmpj2rysz0rtwpexvjhvylww2ng5h3makwma" + ), + protocol_manager_badge: resource_address!( + "resource_rdx1t5w3cekqxjcphrvtp8x5rqz55s4qk97ralrtldnlvf3t6nfhq9a4en" + ), + }, + }) +} diff --git a/testing/stateful-tests/tests/lib.rs b/testing/stateful-tests/tests/lib.rs index c3bfa2da..e60c675b 100644 --- a/testing/stateful-tests/tests/lib.rs +++ b/testing/stateful-tests/tests/lib.rs @@ -15,9 +15,7 @@ use transaction::prelude::*; #[apply(mainnet_test)] fn all_ignition_entities_are_linked_to_the_dapp_definition_in_accordance_with_the_metadata_standard( _: AccountAndControllingKey, - _: PublishingConfiguration, - receipt: PublishingReceipt, - _: ComponentAddress, + receipt: &PublishingReceipt, test_runner: &mut StatefulTestRunner<'_>, ) { // Collecting all of the entities into an array @@ -165,9 +163,7 @@ macro_rules! define_open_and_close_liquidity_position_tests { account_address: test_account, controlling_key: test_account_private_key, }: AccountAndControllingKey, - _: PublishingConfiguration, - receipt: PublishingReceipt, - _: ComponentAddress, + receipt: &PublishingReceipt, test_runner: &mut StatefulTestRunner<'_>, ) { // Arrange @@ -195,7 +191,7 @@ macro_rules! define_open_and_close_liquidity_position_tests { .manifest( ManifestBuilder::new() .lock_fee(test_account, dec!(10)) - .withdraw_from_account(test_account, user_resource, dec!(1000)) + .withdraw_from_account(test_account, user_resource, dec!(1)) .take_all_from_worktop(user_resource, "bucket") .with_bucket("bucket", |builder, bucket| { builder.call_method( @@ -232,9 +228,7 @@ macro_rules! define_open_and_close_liquidity_position_tests { account_address: test_account, controlling_key: test_account_private_key, }: AccountAndControllingKey, - _: PublishingConfiguration, - receipt: PublishingReceipt, - oracle: ComponentAddress, + receipt: &PublishingReceipt, test_runner: &mut StatefulTestRunner<'_>, ) { // Arrange @@ -261,7 +255,7 @@ macro_rules! define_open_and_close_liquidity_position_tests { .manifest( ManifestBuilder::new() .lock_fee(test_account, dec!(10)) - .withdraw_from_account(test_account, user_resource, dec!(1000)) + .withdraw_from_account(test_account, user_resource, dec!(1)) .take_all_from_worktop(user_resource, "bucket") .with_bucket("bucket", |builder, bucket| { builder.call_method( @@ -325,6 +319,7 @@ macro_rules! define_open_and_close_liquidity_position_tests { } { + let oracle = receipt.components.protocol_entities.simple_oracle; let (price, _) = test_runner .execute_manifest_with_enabled_modules( ManifestBuilder::new() @@ -425,12 +420,48 @@ define_open_and_close_liquidity_position_tests! { usdc, usdt ], - // TODO: Enable once Ociswap v2 is live on mainnet and once they have their - // pools inline with the oracle prices. - // ociswap_v2 => [ - // bitcoin, - // ethereum, - // usdc, - // usdt - // ] + ociswap_v2 => [ + bitcoin, + ethereum, + usdc, + usdt + ] +} + +#[apply(mainnet_test)] +fn log_reported_price_from_defiplaza_pool( + _: AccountAndControllingKey, + receipt: &PublishingReceipt, + test_runner: &mut StatefulTestRunner<'_>, +) { + let mut manifest_builder = ManifestBuilder::new(); + for pool in receipt + .exchange_information + .defiplaza_v2 + .as_ref() + .unwrap() + .pools + .iter() + { + manifest_builder = manifest_builder.call_method( + receipt.components.exchange_adapter_entities.defiplaza_v2, + "price", + (*pool,), + ) + } + let receipt = test_runner.preview_manifest( + manifest_builder.build(), + vec![], + 0, + PreviewFlags { + use_free_credit: true, + assume_all_signature_proofs: true, + skip_epoch_check: true, + }, + ); + receipt.expect_commit_success(); + for i in (0..4) { + let price = receipt.expect_commit_success().output::(i); + println!("{price:#?}"); + } }