diff --git a/Cargo.lock b/Cargo.lock index 073908a5a..344c7ab33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,7 +18,7 @@ version = "0.7.12" dependencies = [ "anyhow", "bs58 0.4.0", - "clap 4.5.23", + "clap 4.5.26", "env_logger 0.10.2", "figment", "flatbuffers", @@ -383,11 +383,12 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", + "once_cell", "windows-sys 0.59.0", ] @@ -684,7 +685,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 5.3.1", + "event-listener 5.4.0", "event-listener-strategy", "pin-project-lite", ] @@ -744,7 +745,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -755,13 +756,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -943,7 +944,7 @@ name = "bgtask_creator" version = "0.7.12" dependencies = [ "anyhow", - "clap 4.5.23", + "clap 4.5.26", "digital_asset_types", "futures", "lazy_static", @@ -975,9 +976,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" dependencies = [ "serde", ] @@ -1114,11 +1115,11 @@ dependencies = [ [[package]] name = "borsh" -version = "1.5.3" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2506947f73ad44e344215ccd6403ac2ae18cd8e046e581a441bf8d199f257f03" +checksum = "9fb65153674e51d3a42c8f27b05b9508cea85edfaade8aa46bc8fc18cecdfef3" dependencies = [ - "borsh-derive 1.5.3", + "borsh-derive 1.5.4", "cfg_aliases", ] @@ -1150,15 +1151,15 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.5.3" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2593a3b8b938bd68373196c9832f516be11fa487ef4ae745eb282e6a56a7244" +checksum = "a396e17ad94059c650db3d253bb6e25927f1eb462eede7e7a153bb6e75dce0a7" dependencies = [ "once_cell", "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -1243,9 +1244,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.11.1" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786a307d683a5bf92e6fd5fd69a7eb613751668d1d8d67d802846dfe367c62c8" +checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" dependencies = [ "memchr", "serde", @@ -1306,7 +1307,7 @@ checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -1363,9 +1364,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.6" +version = "1.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333" +checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b" dependencies = [ "jobserver", "libc", @@ -1442,19 +1443,19 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.23" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" dependencies = [ "clap_builder", - "clap_derive 4.5.18", + "clap_derive 4.5.24", ] [[package]] name = "clap_builder" -version = "4.5.23" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" dependencies = [ "anstream", "anstyle", @@ -1477,14 +1478,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -1761,7 +1762,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -1785,7 +1786,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -1796,7 +1797,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -1808,7 +1809,7 @@ dependencies = [ "blockbuster", "borsh 0.10.4", "bs58 0.4.0", - "clap 4.5.23", + "clap 4.5.26", "das-core", "digital_asset_types", "futures", @@ -1842,7 +1843,7 @@ dependencies = [ "bs58 0.4.0", "cadence", "cadence-macros", - "clap 4.5.23", + "clap 4.5.26", "derive_more", "digital_asset_types", "figment", @@ -1874,7 +1875,7 @@ dependencies = [ "atty", "cargo-lock", "chrono", - "clap 4.5.23", + "clap 4.5.26", "das-bubblegum", "das-core", "digital_asset_types", @@ -1920,7 +1921,7 @@ dependencies = [ "cadence", "cadence-macros", "chrono", - "clap 4.5.23", + "clap 4.5.26", "das-core", "derive_more", "digital_asset_types", @@ -1954,7 +1955,7 @@ dependencies = [ "bs58 0.4.0", "cadence", "cadence-macros", - "clap 4.5.23", + "clap 4.5.26", "das-bubblegum", "das-core", "digital_asset_types", @@ -2031,9 +2032,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" [[package]] name = "der" @@ -2095,7 +2096,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -2150,6 +2151,7 @@ dependencies = [ "jsonpath_lib", "log", "mime_guess", + "mpl-token-metadata", "num-derive 0.3.3", "num-traits", "schemars", @@ -2193,7 +2195,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -2216,7 +2218,7 @@ checksum = "a6cbae11b3de8fce2a456e8ea3dada226b35fe791f0dc1d360c0941f0bb681f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -2313,7 +2315,7 @@ checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -2366,9 +2368,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "5.3.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" dependencies = [ "concurrent-queue", "parking", @@ -2381,7 +2383,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ - "event-listener 5.3.1", + "event-listener 5.4.0", "pin-project-lite", ] @@ -2414,7 +2416,7 @@ dependencies = [ "anyhow", "async-trait", "borsh 0.10.4", - "clap 4.5.23", + "clap 4.5.26", "mpl-bubblegum", "solana-account-decoder", "solana-client", @@ -2609,9 +2611,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" dependencies = [ "fastrand", "futures-core", @@ -2628,7 +2630,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -2737,7 +2739,7 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -3210,7 +3212,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -3299,13 +3301,13 @@ checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" [[package]] name = "insta" -version = "1.41.1" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9ffc4d4892617c50a928c52b2961cb5174b6fc6ebf252b2fac9d21955c48b8" +checksum = "6513e4067e16e69ed1db5ab56048ed65db32d10ba5fc1217f5393f8f17d8b5a5" dependencies = [ "console", - "lazy_static", "linked-hash-map", + "once_cell", "serde", "similar", ] @@ -3417,9 +3419,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -3595,7 +3597,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "libc", ] @@ -3667,9 +3669,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litemap" @@ -3704,9 +3706,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" dependencies = [ "value-bag", ] @@ -3858,9 +3860,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", ] @@ -4001,7 +4003,7 @@ dependencies = [ "cadence", "cadence-macros", "chrono", - "clap 4.5.23", + "clap 4.5.26", "das-core", "das-metadata-json", "digital_asset_types", @@ -4135,7 +4137,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -4216,7 +4218,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -4228,7 +4230,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -4306,7 +4308,7 @@ version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cfg-if", "foreign-types", "libc", @@ -4323,7 +4325,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -4558,7 +4560,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -4592,7 +4594,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror 2.0.9", + "thiserror 2.0.11", "ucd-trie", ] @@ -4616,7 +4618,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -4642,29 +4644,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -4799,12 +4801,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.25" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -4861,9 +4863,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -4876,7 +4878,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", "version_check", "yansi", ] @@ -4902,6 +4904,7 @@ dependencies = [ "solana-transaction-status", "spl-account-compression", "spl-token", + "spl-token-2022", "sqlx", "thiserror 1.0.69", "tokio", @@ -4956,7 +4959,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.93", + "syn 2.0.96", "tempfile", ] @@ -4970,7 +4973,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -5044,7 +5047,7 @@ checksum = "9e2e25ee72f5b24d773cae88422baddefff7714f97aab68d96fe2b6fc4a28fb2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -5331,7 +5334,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -5566,7 +5569,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" dependencies = [ "arrayvec", - "borsh 1.5.3", + "borsh 1.5.4", "bytes", "num-traits", "rand 0.8.5", @@ -5613,11 +5616,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.42" +version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "errno", "libc", "linux-raw-sys", @@ -5721,7 +5724,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -5747,7 +5750,7 @@ checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -5954,7 +5957,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "core-foundation", "core-foundation-sys", "libc", @@ -5963,9 +5966,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -6006,7 +6009,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -6017,14 +6020,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] name = "serde_json" -version = "1.0.134" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" dependencies = [ "indexmap 2.7.0", "itoa", @@ -6091,7 +6094,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -6103,7 +6106,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -6141,7 +6144,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -6352,9 +6355,9 @@ dependencies = [ [[package]] name = "solana-account-info" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8378a48833ee7f828e20a63e93a6cdaef1313a8fd8f71a90b243df83295987b1" +checksum = "e053b991f91fd274df53e070c77a0a6a33681a5102c6421a0ca9ffaa0040368a" dependencies = [ "bincode", "serde", @@ -6365,18 +6368,18 @@ dependencies = [ [[package]] name = "solana-atomic-u64" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6f9da603778bbee6402ee2526a3b219b85497c10e49f9c7c112ed8836f46d3" +checksum = "966dce88672728380c476d5d3e54c02025875100b8246db05669961806c9575e" dependencies = [ "parking_lot 0.12.3", ] [[package]] name = "solana-bincode" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb75b5d25ae05cfddb8b9b53512f17c18c29e3c92b7a848f39598473e6bf6484" +checksum = "c117b9646b1e9e6c4b48f363ad4c5af25c4ab35754ff307714e5fec2c3c4bb6b" dependencies = [ "bincode", "serde", @@ -6385,12 +6388,12 @@ dependencies = [ [[package]] name = "solana-borsh" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "055c45b7aae61c8c7b26a537f7a8387cc8a10036081ddfd7170a7a9b4d924ffd" +checksum = "7c55b83c305eac62095b6f24ea2ae729f17de47e5a4e866ee4ddd0dc501b351e" dependencies = [ "borsh 0.10.4", - "borsh 1.5.3", + "borsh 1.5.4", ] [[package]] @@ -6445,13 +6448,13 @@ dependencies = [ [[package]] name = "solana-clock" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d6af32497acc6bb09d8150ea6f53df25dbad713991741999d46688beaf944e" +checksum = "a2387b936492cab0649c2a3e3fcfb282077029b533fa8454c88c41dff3bc2552" dependencies = [ "serde", "serde_derive", - "solana-sdk-macro 2.1.7", + "solana-sdk-macro 2.1.8", "solana-sysvar-id", ] @@ -6493,9 +6496,9 @@ dependencies = [ [[package]] name = "solana-cpi" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b06ac8e2ebf8a5b8a54645b721f92f5d7a6d116550bbf1817167cc48abeb1" +checksum = "00bae0591481827ac9cfce5573aad2918bb01f91289b811ea531df4fcb73d136" dependencies = [ "solana-account-info", "solana-define-syscall", @@ -6507,36 +6510,36 @@ dependencies = [ [[package]] name = "solana-decode-error" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99f2fe3170045ae27951ae476a0c88a40130b242736f033ba5e353cd6f816b8e" +checksum = "8880dc18fb97c6205214d1f3ce2f1152e997ecc6f6da4bb458fbf6e6207a0693" dependencies = [ "num-traits", ] [[package]] name = "solana-define-syscall" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14a80d323dbe8866aaefb40e3c11564d82f77894d193fa66583e793f3d845723" +checksum = "6452c4a8fc77cc60ad2934b19f2d75691067f17355b34462d52285395c1c99db" [[package]] name = "solana-epoch-schedule" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "276fda33b7de27df41e3043f344e517d5e85ab345d2f2aa2425d1011bac570c4" +checksum = "6e783a735416c534228f24f18f18ade0b189c2c8a93be486c9a26bd314517d93" dependencies = [ "serde", "serde_derive", - "solana-sdk-macro 2.1.7", + "solana-sdk-macro 2.1.8", "solana-sysvar-id", ] [[package]] name = "solana-fee-calculator" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abc0371153eda80024109800051b87bab11f056fae260ca477a50875aaffdfde" +checksum = "e1fa18582732d94369263c42eeee967ff919e99b9b15ba747fb7534aa24fbbc0" dependencies = [ "log", "serde", @@ -6577,7 +6580,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -6594,11 +6597,11 @@ dependencies = [ [[package]] name = "solana-hash" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7067f2bd1e9c3f28153484f03e44bf8dc73cededa6bc132e40de9bfc667b8534" +checksum = "58e35f984e3d60a58184743446250cf724afb34ed65f794da0dc4b462f9c1929" dependencies = [ - "borsh 1.5.3", + "borsh 1.5.4", "bs58 0.5.1", "bytemuck", "bytemuck_derive", @@ -6612,12 +6615,12 @@ dependencies = [ [[package]] name = "solana-instruction" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dbf607aff88e4f17079a33d34517b7f379d752dfa0496a94cea798a91e6f6ee" +checksum = "35fc69f7f75df0b11e99c03393b24a7443aec0430518054de14715c59cfa716d" dependencies = [ "bincode", - "borsh 1.5.3", + "borsh 1.5.4", "getrandom 0.2.15", "js-sys", "num-traits", @@ -6630,13 +6633,13 @@ dependencies = [ [[package]] name = "solana-last-restart-slot" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f2ee9af7a2815137c60d77cb930c26480bc802b55e00d9e230e81fd308062e" +checksum = "fee98cc25000ee8bab1a4f63c7516d9521bc8a9747d8287ebb05e5d9b1d32ee1" dependencies = [ "serde", "serde_derive", - "solana-sdk-macro 2.1.7", + "solana-sdk-macro 2.1.8", "solana-sysvar-id", ] @@ -6678,18 +6681,18 @@ dependencies = [ [[package]] name = "solana-msg" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cd9c6ed9d4d036da00c958f0f98edb248c08eb596975011a30063ac3aa902" +checksum = "fac7a109b0c7a0ed26c1fbf3b0fec8809b5d4c74b5d597f0252d45255fd0d309" dependencies = [ "solana-define-syscall", ] [[package]] name = "solana-native-token" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08ef55ad8dcc8c5f21e2bbbc7298fa59550269e8bdf2c6976c4de626ea8a0069" +checksum = "7246817ae265f5a67be25f32ee52267f1c2fe29767ab601ef03c5086bfc64992" [[package]] name = "solana-net-utils" @@ -6754,11 +6757,11 @@ dependencies = [ "ark-serialize", "base64 0.21.7", "bincode", - "bitflags 2.6.0", + "bitflags 2.8.0", "blake3", "borsh 0.10.4", "borsh 0.9.3", - "borsh 1.5.3", + "borsh 1.5.4", "bs58 0.4.0", "bv", "bytemuck", @@ -6799,16 +6802,16 @@ dependencies = [ [[package]] name = "solana-program" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99c693c5c392bacc44e7a86baa18069eb966d88afbc9040219ca000e873fb02e" +checksum = "fb05f5ffadb039285ee82efd9a593e0873220f840f0eac7069d962f9eb29a407" dependencies = [ "base64 0.22.1", "bincode", - "bitflags 2.6.0", + "bitflags 2.8.0", "blake3", "borsh 0.10.4", - "borsh 1.5.3", + "borsh 1.5.4", "bs58 0.5.1", "bv", "bytemuck", @@ -6855,7 +6858,7 @@ dependencies = [ "solana-pubkey", "solana-rent", "solana-sanitize", - "solana-sdk-macro 2.1.7", + "solana-sdk-macro 2.1.8", "solana-secp256k1-recover", "solana-serde-varint", "solana-serialize-utils", @@ -6872,9 +6875,9 @@ dependencies = [ [[package]] name = "solana-program-entrypoint" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e0696f51a4386f92f4e1807059bd2b649c207688b9249df332d0fd5ecac240c" +checksum = "c5f6148e740c6deed55fe343355f0cb3ec158d221e11aa8bb93a392fa62c4137" dependencies = [ "solana-account-info", "solana-msg", @@ -6884,11 +6887,11 @@ dependencies = [ [[package]] name = "solana-program-error" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd557d10df4380234f5b4a5971a0cae29eedcc42ebe4bbe926a21ff31912e99" +checksum = "d87e99e4299728f450194b6adf946dde512d79d82275b1c73f6faea7e9075cef" dependencies = [ - "borsh 1.5.3", + "borsh 1.5.4", "num-traits", "serde", "serde_derive", @@ -6900,9 +6903,9 @@ dependencies = [ [[package]] name = "solana-program-memory" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef71296a3fa6ebaa38ae522bea71af82b7deeea170e459efa3c539b8f14e0155" +checksum = "3691cdd84c0a4753b484f468aac19e0943fab1e71705b21d00d561ac6eea6449" dependencies = [ "num-traits", "solana-define-syscall", @@ -6910,15 +6913,15 @@ dependencies = [ [[package]] name = "solana-program-option" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "506908720b9b1660f2087d9c6f45ba208fa67a0f34658804517324ef65fd432b" +checksum = "e99a3e016363a95cdbe23aaa2a68578ffa2ce8e37c4a642962201af6376ffc37" [[package]] name = "solana-program-pack" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21820c32999ce24532fb12d6188daeffa27eb88a366505a4a0553857729aae52" +checksum = "4eba980dec9d5403ea299a3cdf27cd794e6b1a188acc8c5e3ae7d067b629eb24" dependencies = [ "solana-program-error", ] @@ -6953,12 +6956,12 @@ dependencies = [ [[package]] name = "solana-pubkey" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c46701a845b731f2ffcf002c4bb08968434b9cf5e576cdd705259421074d1764" +checksum = "6dba2b19db8b73ab96b309b6d2a9f26386e45e2af3618a27b92389da9a3df1f1" dependencies = [ "borsh 0.10.4", - "borsh 1.5.3", + "borsh 1.5.4", "bs58 0.5.1", "bytemuck", "bytemuck_derive", @@ -7060,13 +7063,13 @@ dependencies = [ [[package]] name = "solana-rent" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05a4d15fe63d0e9a9551adfecfec495e3d50ba0bc4705001010c724fe84f65d8" +checksum = "138b60a6683d14d63b4cee532d50afcb54999679b5c53013969fd51977455e14" dependencies = [ "serde", "serde_derive", - "solana-sdk-macro 2.1.7", + "solana-sdk-macro 2.1.8", "solana-sysvar-id", ] @@ -7133,9 +7136,9 @@ dependencies = [ [[package]] name = "solana-sanitize" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b8c536460c84ec7cbcce45d8a68ab1c12c99933cfeb0cb332567e67a27eea2f" +checksum = "58f71b885b953e9157b66eaba9a34507f2f840712ef54f483725ba510ee1bd89" [[package]] name = "solana-sdk" @@ -7146,8 +7149,8 @@ dependencies = [ "assert_matches", "base64 0.21.7", "bincode", - "bitflags 2.6.0", - "borsh 1.5.3", + "bitflags 2.8.0", + "borsh 1.5.4", "bs58 0.4.0", "bytemuck", "byteorder", @@ -7202,26 +7205,26 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] name = "solana-sdk-macro" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24c160cb22c3671fe2e0fd7c3630701cfccae308174d8d31d7fc93386c3cc381" +checksum = "62f0b358f336ceac3827881915e5293f121c023cbd2150115046356c66898cb8" dependencies = [ "bs58 0.5.1", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] name = "solana-secp256k1-recover" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ece1b12237c7755a2070ab1d309a236b506041dd1d7927c7053b04cd3203728" +checksum = "460c2e36586bcce843cdeaaf2364f3db7fbd9f266325e93d5e9af33f2605dd7d" dependencies = [ "libsecp256k1", "solana-define-syscall", @@ -7236,18 +7239,18 @@ checksum = "468aa43b7edb1f9b7b7b686d5c3aeb6630dc1708e86e31343499dd5c4d775183" [[package]] name = "solana-serde-varint" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef862fbe970f90d0e024c6dfca71f25210ec9a52c0e62d94249aae10bda8e97" +checksum = "a98449030e53dcc2c4f160acab99b2bdb3e24ea8bff8ca6e71a6e539a54bf3d7" dependencies = [ "serde", ] [[package]] name = "solana-serialize-utils" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f1cdaa53aea6fa76dbacd1504e307c4d9665edd50d6d2c5154cd1f88caa1d2" +checksum = "d659aac218580fc3fb3e8350669db9bb01bc1bc849c90f0741cbfccb6663eb94" dependencies = [ "solana-instruction", "solana-pubkey", @@ -7256,9 +7259,9 @@ dependencies = [ [[package]] name = "solana-sha256-hasher" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4806e0ffeeb460968219b6a20176b1dc616089e84de1cd37e50cf7f01e295ad9" +checksum = "0db90ad6643d4d626f923159eaa876000c09f8c2e9aa7ff59b803e8328712582" dependencies = [ "sha2 0.10.8", "solana-define-syscall", @@ -7267,18 +7270,18 @@ dependencies = [ [[package]] name = "solana-short-vec" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824dee0886a8504a62a0c06bf95928d43cec0b077992d2b543e0f898775a4931" +checksum = "11f7de721a6c50cb3a41e027a623496be39e45c452fbf897f657cd1f2f67dbbd" dependencies = [ "serde", ] [[package]] name = "solana-slot-hashes" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "409010900f8c357c6cb7bec25014f96e7a0826ca47c915f61f94358b01df9f68" +checksum = "3840867aa6d0fac65d3a4c1f14fff650a8e148732a16c06ebd8a2389d79d4745" dependencies = [ "serde", "serde_derive", @@ -7288,9 +7291,9 @@ dependencies = [ [[package]] name = "solana-slot-history" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82dfd06c84fa3baea6686178b34be9f9485849045ba401c5d0e8eee2eaf485ab" +checksum = "101583a12fcce9b52f845b3c773f4ae6c3f4ca6a46177dadbd83e276baf82326" dependencies = [ "bv", "serde", @@ -7300,9 +7303,9 @@ dependencies = [ [[package]] name = "solana-stable-layout" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1629426668b0c503ce86e2fad9ed6d9d525efa958365e6b890397af491c545" +checksum = "9e1b923e1c9e42b6c98b1786ca003af6a0366932f08d63432e984fcc394b7b5e" dependencies = [ "solana-instruction", "solana-pubkey", @@ -7343,9 +7346,9 @@ dependencies = [ [[package]] name = "solana-sysvar-id" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9ec6dbb5d57e773722824069d1b2283e552f0363b1be39aa177b1d77b51ea6e" +checksum = "59351de877a7cf0cea0e436424ecf4ea0c08c59ff01ef0575436972b920b818c" dependencies = [ "solana-pubkey", ] @@ -7391,9 +7394,9 @@ dependencies = [ [[package]] name = "solana-transaction-error" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "832ead1987e67eb4cd9a742b8e778169966d600955a39efa50b83a6bb3dec6d3" +checksum = "1c3d2147cfaad2a5518b8e15621008699e28d32d6233cd7a6b27a506e01f1515" dependencies = [ "solana-instruction", "solana-sanitize", @@ -7555,7 +7558,7 @@ checksum = "2785042005954aec5d5db7fcb99a78754b222be906a89d10a3d66ebdbc8e9548" dependencies = [ "anchor-lang", "bytemuck", - "solana-program 2.1.7", + "solana-program 2.1.8", "spl-concurrent-merkle-tree", "spl-noop", ] @@ -7606,7 +7609,7 @@ checksum = "07fd7858fc4ff8fb0e34090e41d7eb06a823e1057945c26d480bfc21d2338a93" dependencies = [ "quote", "spl-discriminator-syn", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -7618,7 +7621,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.93", + "syn 2.0.96", "thiserror 1.0.69", ] @@ -7677,7 +7680,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -7960,9 +7963,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.93" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -7995,7 +7998,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -8027,12 +8030,13 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.14.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", "fastrand", + "getrandom 0.2.15", "once_cell", "rustix", "windows-sys 0.59.0", @@ -8073,11 +8077,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.9" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl 2.0.9", + "thiserror-impl 2.0.11", ] [[package]] @@ -8088,18 +8092,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] name = "thiserror-impl" -version = "2.0.9" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -8224,9 +8228,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.42.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -8253,13 +8257,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -8398,7 +8402,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.20", + "winnow 0.6.24", ] [[package]] @@ -8443,7 +8447,7 @@ dependencies = [ "proc-macro2", "prost-build", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -8529,7 +8533,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -8609,7 +8613,7 @@ dependencies = [ "anchor-client", "anyhow", "bs58 0.4.0", - "clap 4.5.23", + "clap 4.5.26", "digital_asset_types", "env_logger 0.10.2", "flatbuffers", @@ -8663,7 +8667,7 @@ name = "txn_forwarder" version = "0.7.12" dependencies = [ "anyhow", - "clap 4.5.23", + "clap 4.5.26", "env_logger 0.10.2", "figment", "flatbuffers", @@ -8857,9 +8861,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "744018581f9a3454a9e15beb8a33b017183f1e7c0cd170232a2d1453b23a51c4" dependencies = [ "getrandom 0.2.15", "serde", @@ -8954,34 +8958,35 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.49" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", @@ -8992,9 +8997,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -9002,28 +9007,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "web-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -9293,9 +9301,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.20" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" dependencies = [ "memchr", ] @@ -9402,7 +9410,7 @@ dependencies = [ "async-trait", "atty", "cargo-lock", - "clap 4.5.23", + "clap 4.5.26", "futures", "git-version", "hyper", @@ -9444,7 +9452,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", "synstructure 0.13.1", ] @@ -9466,7 +9474,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -9486,7 +9494,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", "synstructure 0.13.1", ] @@ -9507,7 +9515,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -9529,7 +9537,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index c0c8708e0..ee4402a7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,8 +80,8 @@ lru = "0.12.3" metrics = "0.20.1" migration = { path = "migration" } mime_guess = "2.0.4" -mpl-bubblegum = "1.2.0" mpl-account-compression = "0.4.2" +mpl-bubblegum = "1.4.0" mpl-core = { version = "0.8.0-beta.1", features = ["serde"] } mpl-noop = "0.2.1" mpl-token-metadata = "4.1.1" diff --git a/blockbuster/src/programs/mod.rs b/blockbuster/src/programs/mod.rs index 8da2feed0..474bce179 100644 --- a/blockbuster/src/programs/mod.rs +++ b/blockbuster/src/programs/mod.rs @@ -2,12 +2,14 @@ use bubblegum::BubblegumInstruction; use mpl_core_program::MplCoreAccountState; use token_account::TokenProgramAccount; use token_extensions::TokenExtensionsProgramAccount; +use token_inscriptions::TokenInscriptionAccount; use token_metadata::TokenMetadataAccountState; pub mod bubblegum; pub mod mpl_core_program; pub mod token_account; pub mod token_extensions; +pub mod token_inscriptions; pub mod token_metadata; // Note: `ProgramParseResult` used to contain the following variants that have been deprecated and @@ -30,5 +32,6 @@ pub enum ProgramParseResult<'a> { TokenMetadata(&'a TokenMetadataAccountState), TokenProgramAccount(&'a TokenProgramAccount), TokenExtensionsProgramAccount(&'a TokenExtensionsProgramAccount), + TokenInscriptionAccount(&'a TokenInscriptionAccount), Unknown, } diff --git a/blockbuster/src/programs/token_extensions/extension.rs b/blockbuster/src/programs/token_extensions/extension.rs index c9a0b9b4a..6dba2b57c 100644 --- a/blockbuster/src/programs/token_extensions/extension.rs +++ b/blockbuster/src/programs/token_extensions/extension.rs @@ -13,7 +13,6 @@ use spl_token_2022::extension::{ default_account_state::DefaultAccountState, group_member_pointer::GroupMemberPointer, group_pointer::GroupPointer, - immutable_owner::ImmutableOwner, interest_bearing_mint::{BasisPoints, InterestBearingConfig}, memo_transfer::MemoTransfer, metadata_pointer::MetadataPointer, @@ -77,9 +76,6 @@ pub struct ShadowDefaultAccountState { pub state: PodAccountState, } -#[derive(Clone, Copy, Debug, Default, PartialEq, Zeroable, Serialize, Deserialize)] -pub struct ShadowImmutableOwner; - #[derive(Clone, Copy, Debug, Default, PartialEq, Zeroable, Serialize, Deserialize)] pub struct ShadowInterestBearingConfig { pub rate_authority: OptionalNonZeroPubkey, @@ -97,20 +93,16 @@ pub struct ShadowMemoTransfer { #[derive(Clone, Copy, Debug, Default, PartialEq, Zeroable, Serialize, Deserialize)] pub struct ShadowMetadataPointer { - pub authority: OptionalNonZeroPubkey, pub metadata_address: OptionalNonZeroPubkey, } #[derive(Clone, Copy, Debug, Default, PartialEq, Zeroable, Serialize, Deserialize)] pub struct ShadowGroupMemberPointer { - pub authority: OptionalNonZeroPubkey, pub member_address: OptionalNonZeroPubkey, } #[derive(Clone, Copy, Debug, Default, PartialEq, Zeroable, Serialize, Deserialize)] pub struct ShadowGroupPointer { - /// Authority that can set the group address - pub authority: OptionalNonZeroPubkey, /// Account address that holds the group pub group_address: OptionalNonZeroPubkey, } @@ -348,12 +340,6 @@ impl From for ShadowDefaultAccountState { } } -impl From for ShadowImmutableOwner { - fn from(_: ImmutableOwner) -> Self { - ShadowImmutableOwner - } -} - impl From for ShadowConfidentialTransferFeeAmount { fn from(original: ConfidentialTransferFeeAmount) -> Self { ShadowConfidentialTransferFeeAmount { @@ -381,7 +367,6 @@ impl From for ShadowMemoTransfer { impl From for ShadowMetadataPointer { fn from(original: MetadataPointer) -> Self { ShadowMetadataPointer { - authority: original.authority, metadata_address: original.metadata_address, } } @@ -390,7 +375,6 @@ impl From for ShadowMetadataPointer { impl From for ShadowGroupPointer { fn from(original: GroupPointer) -> Self { ShadowGroupPointer { - authority: original.authority, group_address: original.group_address, } } @@ -420,7 +404,6 @@ impl From for ShadowTokenGroupMember { impl From for ShadowGroupMemberPointer { fn from(original: GroupMemberPointer) -> Self { ShadowGroupMemberPointer { - authority: original.authority, member_address: original.member_address, } } diff --git a/blockbuster/src/programs/token_extensions/mod.rs b/blockbuster/src/programs/token_extensions/mod.rs index ec1633c9c..6272f96b5 100644 --- a/blockbuster/src/programs/token_extensions/mod.rs +++ b/blockbuster/src/programs/token_extensions/mod.rs @@ -4,6 +4,7 @@ use crate::{ program_handler::{ParseResult, ProgramParser}, programs::ProgramParseResult, }; + use serde::{Deserialize, Serialize}; use solana_sdk::{pubkey::Pubkey, pubkeys}; use spl_token_2022::{ @@ -14,10 +15,12 @@ use spl_token_2022::{ default_account_state::DefaultAccountState, group_member_pointer::GroupMemberPointer, group_pointer::GroupPointer, + immutable_owner::ImmutableOwner, interest_bearing_mint::InterestBearingConfig, memo_transfer::MemoTransfer, metadata_pointer::MetadataPointer, mint_close_authority::MintCloseAuthority, + non_transferable::{NonTransferable, NonTransferableAccount}, permanent_delegate::PermanentDelegate, transfer_fee::{TransferFeeAmount, TransferFeeConfig}, transfer_hook::TransferHook, @@ -41,7 +44,6 @@ use self::extension::{ pub struct MintAccountExtensions { pub default_account_state: Option, pub confidential_transfer_mint: Option, - pub confidential_transfer_account: Option, pub confidential_transfer_fee_config: Option, pub interest_bearing_config: Option, pub transfer_fee_config: Option, @@ -54,6 +56,29 @@ pub struct MintAccountExtensions { pub token_group: Option, pub group_member_pointer: Option, pub token_group_member: Option, + // TODO : add this when spl-token-2022 is updated + // pub scaled_ui_amount: Option, + pub non_transferable: Option, + pub immutable_owner: Option, +} + +impl MintAccountExtensions { + pub fn is_some(&self) -> bool { + self.default_account_state.is_some() + || self.confidential_transfer_mint.is_some() + || self.confidential_transfer_fee_config.is_some() + || self.interest_bearing_config.is_some() + || self.transfer_fee_config.is_some() + || self.mint_close_authority.is_some() + || self.permanent_delegate.is_some() + || self.metadata_pointer.is_some() + || self.metadata.is_some() + || self.transfer_hook.is_some() + || self.group_pointer.is_some() + || self.token_group.is_some() + || self.group_member_pointer.is_some() + || self.token_group_member.is_some() + } } #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] @@ -62,6 +87,17 @@ pub struct TokenAccountExtensions { pub cpi_guard: Option, pub memo_transfer: Option, pub transfer_fee_amount: Option, + pub immutable_owner: Option, + pub non_transferable_account: Option, +} + +impl TokenAccountExtensions { + pub fn is_some(&self) -> bool { + self.confidential_transfer.is_some() + || self.cpi_guard.is_some() + || self.memo_transfer.is_some() + || self.transfer_fee_amount.is_some() + } } #[derive(Debug, PartialEq)] pub struct TokenAccount { @@ -134,6 +170,16 @@ impl ProgramParser for Token2022AccountParser { let cpi_guard = account.get_extension::().ok().copied(); let memo_transfer = account.get_extension::().ok().copied(); let transfer_fee_amount = account.get_extension::().ok().copied(); + let immutable_owner = account + .get_extension::() + .ok() + .copied() + .map(|_| true); + let non_transferable_account = account + .get_extension::() + .ok() + .copied() + .map(|_| true); // Create a structured account with extensions let structured_account = TokenAccount { @@ -144,6 +190,8 @@ impl ProgramParser for Token2022AccountParser { cpi_guard: cpi_guard.map(ShadowCpiGuard::from), memo_transfer: memo_transfer.map(ShadowMemoTransfer::from), transfer_fee_amount: transfer_fee_amount.map(ShadowTransferFeeAmount::from), + immutable_owner, + non_transferable_account, }, }; @@ -153,10 +201,7 @@ impl ProgramParser for Token2022AccountParser { .get_extension::() .ok() .copied(); - let confidential_transfer_account = mint - .get_extension::() - .ok() - .copied(); + let confidential_transfer_fee_config = mint .get_extension::() .ok() @@ -174,14 +219,23 @@ impl ProgramParser for Token2022AccountParser { let group_member_pointer = mint.get_extension::().ok().copied(); let token_group_member = mint.get_extension::().ok().copied(); let transfer_hook = mint.get_extension::().ok().copied(); + let non_transferable = mint + .get_extension::() + .ok() + .copied() + .map(|_| true); + + let immutable_owner = mint + .get_extension::() + .ok() + .copied() + .map(|_| true); let structured_mint = MintAccount { account: mint.base, extensions: MintAccountExtensions { confidential_transfer_mint: confidential_transfer_mint .map(ShadowConfidentialTransferMint::from), - confidential_transfer_account: confidential_transfer_account - .map(ShadowConfidentialTransferAccount::from), confidential_transfer_fee_config: confidential_transfer_fee_config .map(ShadowConfidentialTransferFeeConfig::from), default_account_state: default_account_state @@ -198,6 +252,8 @@ impl ProgramParser for Token2022AccountParser { token_group: token_group.map(ShadowTokenGroup::from), group_member_pointer: group_member_pointer.map(ShadowGroupMemberPointer::from), token_group_member: token_group_member.map(ShadowTokenGroupMember::from), + non_transferable, + immutable_owner, }, }; result = TokenExtensionsProgramAccount::MintAccount(structured_mint); diff --git a/blockbuster/src/programs/token_inscriptions/mod.rs b/blockbuster/src/programs/token_inscriptions/mod.rs new file mode 100644 index 000000000..7e40f5e07 --- /dev/null +++ b/blockbuster/src/programs/token_inscriptions/mod.rs @@ -0,0 +1,141 @@ +use serde::{Deserialize, Serialize}; +use solana_sdk::{pubkey::Pubkey, pubkeys}; + +use crate::{ + error::BlockbusterError, + program_handler::{ParseResult, ProgramParser}, +}; + +use super::ProgramParseResult; + +pubkeys!( + inscription_program_id, + "inscokhJarcjaEs59QbQ7hYjrKz25LEPRfCbP8EmdUp" +); + +pub struct TokenInscriptionParser; + +#[derive(Debug, Serialize, Deserialize)] +pub struct InscriptionData { + pub authority: String, + pub root: String, + pub content: String, + pub encoding: String, + pub inscription_data: String, + pub order: u64, + pub size: u32, + pub validation_hash: Option, +} + +impl InscriptionData { + pub const BASE_SIZE: usize = 121; + pub const INSCRIPTION_ACC_DATA_DISC: [u8; 8] = [232, 120, 205, 47, 153, 239, 229, 224]; + + pub fn try_unpack_data(data: &[u8]) -> Result { + let acc_disc = &data[0..8]; + + if acc_disc != Self::INSCRIPTION_ACC_DATA_DISC { + return Err(BlockbusterError::InvalidAccountType); + } + + if data.len() < Self::BASE_SIZE { + return Err(BlockbusterError::CustomDeserializationError( + "Inscription Data is too short".to_string(), + )); + } + + let authority = Pubkey::try_from(&data[8..40]).unwrap(); + let mint = Pubkey::try_from(&data[40..72]).unwrap(); + let inscription_data = Pubkey::try_from(&data[72..104]).unwrap(); + let order = u64::from_le_bytes(data[104..112].try_into().unwrap()); + let size = u32::from_le_bytes(data[112..116].try_into().unwrap()); + let content_type_len = u32::from_le_bytes(data[116..120].try_into().unwrap()) as usize; + let content = String::from_utf8(data[120..120 + content_type_len].to_vec()).unwrap(); + let encoding_len = u32::from_le_bytes( + data[120 + content_type_len..124 + content_type_len] + .try_into() + .unwrap(), + ) as usize; + + let encoding = String::from_utf8( + data[124 + content_type_len..124 + content_type_len + encoding_len].to_vec(), + ) + .unwrap(); + + let validation_exists = u8::from_le_bytes( + data[124 + content_type_len + encoding_len..124 + content_type_len + encoding_len + 1] + .try_into() + .unwrap(), + ); + + let validation_hash = if validation_exists == 1 { + let validation_hash_len = u32::from_le_bytes( + data[124 + content_type_len + encoding_len + 1 + ..128 + content_type_len + encoding_len + 1] + .try_into() + .unwrap(), + ) as usize; + Some( + String::from_utf8( + data[128 + content_type_len + encoding_len + 1 + ..128 + content_type_len + encoding_len + 1 + validation_hash_len] + .to_vec(), + ) + .unwrap(), + ) + } else { + None + }; + Ok(InscriptionData { + authority: authority.to_string(), + root: mint.to_string(), + content, + encoding, + inscription_data: inscription_data.to_string(), + order, + size, + validation_hash, + }) + } +} + +pub struct TokenInscriptionAccount { + pub data: InscriptionData, +} + +impl ParseResult for TokenInscriptionAccount { + fn result(&self) -> &Self + where + Self: Sized, + { + self + } + fn result_type(&self) -> ProgramParseResult { + ProgramParseResult::TokenInscriptionAccount(self) + } +} + +impl ProgramParser for TokenInscriptionParser { + fn key(&self) -> Pubkey { + inscription_program_id() + } + fn key_match(&self, key: &Pubkey) -> bool { + key == &inscription_program_id() + } + + fn handles_account_updates(&self) -> bool { + true + } + + fn handles_instructions(&self) -> bool { + false + } + + fn handle_account( + &self, + account_data: &[u8], + ) -> Result, BlockbusterError> { + let data = InscriptionData::try_unpack_data(account_data)?; + Ok(Box::new(TokenInscriptionAccount { data })) + } +} diff --git a/das_api/src/api/api_impl.rs b/das_api/src/api/api_impl.rs index 902303be4..7c6a71127 100644 --- a/das_api/src/api/api_impl.rs +++ b/das_api/src/api/api_impl.rs @@ -1,21 +1,21 @@ use digital_asset_types::{ dao::{ - scopes::asset::get_grouping, + scopes::asset::{get_grouping, get_nft_editions}, sea_orm_active_enums::{ OwnerType, RoyaltyTargetType, SpecificationAssetClass, SpecificationVersions, }, Cursor, PageOptions, SearchAssetsQuery, }, dapi::{ - get_asset, get_asset_proofs, get_asset_signatures, get_assets, get_assets_by_authority, - get_assets_by_creator, get_assets_by_group, get_assets_by_owner, get_proof_for_asset, - search_assets, + common::create_pagination, get_asset, get_asset_proofs, get_asset_signatures, get_assets, + get_assets_by_authority, get_assets_by_creator, get_assets_by_group, get_assets_by_owner, + get_proof_for_asset, get_token_accounts, search_assets, }, rpc::{ filter::{AssetSortBy, SearchConditionType}, - response::GetGroupingResponse, + response::{GetGroupingResponse, TokenAccountList}, + OwnershipModel, RoyaltyModel, }, - rpc::{OwnershipModel, RoyaltyModel}, }; use open_rpc_derive::document_rpc; use sea_orm::{sea_query::ConditionType, ConnectionTrait, DbBackend, Statement}; @@ -218,6 +218,10 @@ impl ApiContract for DasApi { ) -> Result>, DasApiError> { let GetAssets { ids, options } = payload; + let mut ids: Vec = ids.into_iter().collect(); + ids.sort(); + ids.dedup(); + let batch_size = ids.len(); if batch_size > 1000 { return Err(DasApiError::BatchSizeExceededError); @@ -284,6 +288,7 @@ impl ApiContract for DasApi { options, cursor, } = payload; + validate_pubkey(group_value.clone())?; let before: Option = before.filter(|before| !before.is_empty()); let after: Option = after.filter(|after| !after.is_empty()); let sort_by = sort_by.unwrap_or_default(); @@ -501,6 +506,7 @@ impl ApiContract for DasApi { .await .map_err(Into::into) } + async fn get_grouping( self: &DasApi, payload: GetGrouping, @@ -516,4 +522,59 @@ impl ApiContract for DasApi { group_size: gs.size, }) } + + async fn get_token_accounts( + self: &DasApi, + payload: GetTokenAccounts, + ) -> Result { + let GetTokenAccounts { + owner_address, + mint_address, + limit, + page, + before, + after, + options, + cursor, + } = payload; + let owner_address = validate_opt_pubkey(&owner_address)?; + let mint_address = validate_opt_pubkey(&mint_address)?; + let options = options.unwrap_or_default(); + let page_options = self.validate_pagination(limit, page, &before, &after, &cursor, None)?; + get_token_accounts( + &self.db_connection, + owner_address, + mint_address, + &page_options, + &options, + ) + .await + .map_err(Into::into) + } + + async fn get_nft_editions( + self: &DasApi, + payload: GetNftEditions, + ) -> Result { + let GetNftEditions { + mint_address, + page, + limit, + before, + after, + cursor, + } = payload; + + let page_options = self.validate_pagination(limit, page, &before, &after, &cursor, None)?; + let mint_address = validate_pubkey(mint_address.clone())?; + let pagination = create_pagination(&page_options)?; + get_nft_editions( + &self.db_connection, + mint_address, + &pagination, + page_options.limit, + ) + .await + .map_err(Into::into) + } } diff --git a/das_api/src/api/mod.rs b/das_api/src/api/mod.rs index 2e1da73ca..b98fe2cbe 100644 --- a/das_api/src/api/mod.rs +++ b/das_api/src/api/mod.rs @@ -2,7 +2,9 @@ use crate::error::DasApiError; use async_trait::async_trait; use digital_asset_types::rpc::filter::{AssetSortDirection, SearchConditionType}; use digital_asset_types::rpc::options::Options; -use digital_asset_types::rpc::response::{AssetList, TransactionSignatureList}; +use digital_asset_types::rpc::response::{ + AssetList, NftEditions, TokenAccountList, TransactionSignatureList, +}; use digital_asset_types::rpc::{filter::AssetSorting, response::GetGroupingResponse}; use digital_asset_types::rpc::{Asset, AssetProof, Interface, OwnershipModel, RoyaltyModel}; use open_rpc_derive::{document_rpc, rpc}; @@ -147,6 +149,18 @@ pub struct GetGrouping { pub group_value: String, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[serde(deny_unknown_fields, rename_all = "camelCase")] +pub struct GetNftEditions { + pub mint_address: String, + pub page: Option, + pub limit: Option, + pub before: Option, + pub after: Option, + #[serde(default)] + pub cursor: Option, +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Default)] #[serde(deny_unknown_fields, rename_all = "camelCase")] pub struct GetAssetSignatures { @@ -163,6 +177,21 @@ pub struct GetAssetSignatures { pub sort_direction: Option, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[serde(deny_unknown_fields, rename_all = "camelCase")] +pub struct GetTokenAccounts { + pub owner_address: Option, + pub mint_address: Option, + pub limit: Option, + pub page: Option, + pub before: Option, + pub after: Option, + #[serde(default, alias = "displayOptions")] + pub options: Option, + #[serde(default)] + pub cursor: Option, +} + #[document_rpc] #[async_trait] pub trait ApiContract: Send + Sync + 'static { @@ -251,4 +280,20 @@ pub trait ApiContract: Send + Sync + 'static { summary = "Get a list of assets grouped by a specific authority" )] async fn get_grouping(&self, payload: GetGrouping) -> Result; + + #[rpc( + name = "getTokenAccounts", + params = "named", + summary = "Get a list of token accounts by owner or mint" + )] + async fn get_token_accounts( + &self, + payload: GetTokenAccounts, + ) -> Result; + #[rpc( + name = "getNftEditions", + params = "named", + summary = "Get all printable editions for a master edition NFT mint" + )] + async fn get_nft_editions(&self, payload: GetNftEditions) -> Result; } diff --git a/das_api/src/builder.rs b/das_api/src/builder.rs index 25edb0aa2..d048dfc5a 100644 --- a/das_api/src/builder.rs +++ b/das_api/src/builder.rs @@ -118,6 +118,27 @@ impl RpcApiBuilder { Ok(rpc_context.schema()) })?; + module.register_async_method( + "get_token_accounts", + |rpc_params, rpc_context| async move { + let payload = rpc_params.parse::()?; + rpc_context + .get_token_accounts(payload) + .await + .map_err(Into::into) + }, + )?; + module.register_alias("getTokenAccounts", "get_token_accounts")?; + + module.register_async_method("get_nft_editions", |rpc_params, rpc_context| async move { + let payload = rpc_params.parse::()?; + rpc_context + .get_nft_editions(payload) + .await + .map_err(Into::into) + })?; + module.register_alias("getNftEditions", "get_nft_editions")?; + Ok(module) } } diff --git a/digital_asset_types/Cargo.toml b/digital_asset_types/Cargo.toml index 449f6e78d..53d7bc0d5 100644 --- a/digital_asset_types/Cargo.toml +++ b/digital_asset_types/Cargo.toml @@ -29,6 +29,7 @@ spl-concurrent-merkle-tree = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true, features = ["macros"] } url = { workspace = true } +mpl-token-metadata = { workspace = true } [features] default = ["json_types", "sql_types"] diff --git a/digital_asset_types/src/dao/extensions/asset.rs b/digital_asset_types/src/dao/extensions/asset.rs index 18525e39e..1d8e7f1d3 100644 --- a/digital_asset_types/src/dao/extensions/asset.rs +++ b/digital_asset_types/src/dao/extensions/asset.rs @@ -4,6 +4,7 @@ use crate::dao::{ asset, asset_authority, asset_creators, asset_data, asset_grouping, asset_v1_account_attachments, sea_orm_active_enums::{OwnerType, RoyaltyTargetType}, + token_accounts, }; #[derive(Copy, Clone, Debug, EnumIter)] @@ -13,6 +14,7 @@ pub enum Relation { AssetAuthority, AssetCreators, AssetGrouping, + TokenAccounts, } impl RelationTrait for Relation { @@ -22,6 +24,10 @@ impl RelationTrait for Relation { .from(asset::Column::AssetData) .to(asset_data::Column::Id) .into(), + Self::TokenAccounts => asset::Entity::belongs_to(token_accounts::Entity) + .from(asset::Column::Id) + .to(token_accounts::Column::Mint) + .into(), Self::AssetV1AccountAttachments => { asset::Entity::has_many(asset_v1_account_attachments::Entity).into() } @@ -62,6 +68,12 @@ impl Related for asset::Entity { } } +impl Related for asset::Entity { + fn to() -> RelationDef { + Relation::TokenAccounts.def() + } +} + impl Default for RoyaltyTargetType { fn default() -> Self { Self::Creators @@ -76,7 +88,7 @@ impl Default for asset::Model { specification_version: None, specification_asset_class: None, owner: None, - owner_type: OwnerType::Single, + owner_type: OwnerType::Unknown, delegate: None, frozen: Default::default(), supply: Default::default(), @@ -103,6 +115,7 @@ impl Default for asset::Model { owner_delegate_seq: None, leaf_seq: None, base_info_seq: None, + mint_extensions: None, mpl_core_plugins: None, mpl_core_unknown_plugins: None, mpl_core_collection_current_size: None, diff --git a/digital_asset_types/src/dao/extensions/asset_grouping.rs b/digital_asset_types/src/dao/extensions/asset_grouping.rs index 49b091efb..d474c63bb 100644 --- a/digital_asset_types/src/dao/extensions/asset_grouping.rs +++ b/digital_asset_types/src/dao/extensions/asset_grouping.rs @@ -1,11 +1,12 @@ use sea_orm::{EntityTrait, EnumIter, Related, RelationDef, RelationTrait}; -use crate::dao::{asset, asset_authority, asset_grouping}; +use crate::dao::{asset, asset_authority, asset_data, asset_grouping}; #[derive(Copy, Clone, Debug, EnumIter)] pub enum Relation { Asset, AssetAuthority, + AssetData, } impl RelationTrait for Relation { @@ -19,6 +20,10 @@ impl RelationTrait for Relation { .from(asset_grouping::Column::AssetId) .to(asset_authority::Column::Id) .into(), + Self::AssetData => asset_grouping::Entity::belongs_to(asset_data::Entity) + .from(asset_grouping::Column::AssetId) + .to(asset_data::Column::Id) + .into(), } } } @@ -34,3 +39,9 @@ impl Related for asset_grouping::Entity { Relation::AssetAuthority.def() } } + +impl Related for asset_grouping::Entity { + fn to() -> RelationDef { + Relation::AssetData.def() + } +} diff --git a/digital_asset_types/src/dao/extensions/mod.rs b/digital_asset_types/src/dao/extensions/mod.rs index bcfc3e130..af3baad66 100644 --- a/digital_asset_types/src/dao/extensions/mod.rs +++ b/digital_asset_types/src/dao/extensions/mod.rs @@ -5,3 +5,4 @@ pub mod asset_data; pub mod asset_grouping; pub mod asset_v1_account_attachment; pub mod instruction; +pub mod token_accounts; diff --git a/digital_asset_types/src/dao/extensions/token_accounts.rs b/digital_asset_types/src/dao/extensions/token_accounts.rs new file mode 100644 index 000000000..e48408f74 --- /dev/null +++ b/digital_asset_types/src/dao/extensions/token_accounts.rs @@ -0,0 +1,25 @@ +use sea_orm::{EntityTrait, EnumIter, Related, RelationDef, RelationTrait}; + +use crate::dao::{asset, token_accounts}; + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation { + Asset, +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + match self { + Self::Asset => token_accounts::Entity::belongs_to(asset::Entity) + .from(token_accounts::Column::Mint) + .to(asset::Column::Id) + .into(), + } + } +} + +impl Related for token_accounts::Entity { + fn to() -> RelationDef { + Relation::Asset.def() + } +} diff --git a/digital_asset_types/src/dao/full_asset.rs b/digital_asset_types/src/dao/full_asset.rs index fda932d21..b80a1f1e6 100644 --- a/digital_asset_types/src/dao/full_asset.rs +++ b/digital_asset_types/src/dao/full_asset.rs @@ -1,13 +1,29 @@ +use super::{asset_v1_account_attachments, tokens}; use crate::dao::{asset, asset_authority, asset_creators, asset_data, asset_grouping}; +#[derive(Clone, Debug, PartialEq)] +pub struct FullAssetGroup { + pub id: i64, + pub asset_id: Vec, + pub group_key: String, + pub group_value: Option, + pub seq: Option, + pub slot_updated: Option, + pub verified: bool, + pub group_info_seq: Option, +} + #[derive(Clone, Debug, PartialEq)] pub struct FullAsset { pub asset: asset::Model, pub data: asset_data::Model, + pub token_info: Option, pub authorities: Vec, pub creators: Vec, - pub groups: Vec, + pub inscription: Option, + pub groups: Vec<(asset_grouping::Model, Option)>, } + #[derive(Clone, Debug, PartialEq)] pub struct AssetRelated { pub authorities: Vec, diff --git a/digital_asset_types/src/dao/generated/asset.rs b/digital_asset_types/src/dao/generated/asset.rs index f70e0b383..f2f74d6a9 100644 --- a/digital_asset_types/src/dao/generated/asset.rs +++ b/digital_asset_types/src/dao/generated/asset.rs @@ -50,6 +50,7 @@ pub struct Model { pub owner_delegate_seq: Option, pub leaf_seq: Option, pub base_info_seq: Option, + pub mint_extensions: Option, pub mpl_core_plugins: Option, pub mpl_core_unknown_plugins: Option, pub mpl_core_collection_num_minted: Option, @@ -93,6 +94,7 @@ pub enum Column { OwnerDelegateSeq, LeafSeq, BaseInfoSeq, + MintExtensions, MplCorePlugins, MplCoreUnknownPlugins, MplCoreCollectionNumMinted, @@ -153,6 +155,7 @@ impl ColumnTrait for Column { Self::OwnerDelegateSeq => ColumnType::BigInteger.def().null(), Self::LeafSeq => ColumnType::BigInteger.def().null(), Self::BaseInfoSeq => ColumnType::BigInteger.def().null(), + Self::MintExtensions => ColumnType::JsonBinary.def().null(), Self::MplCorePlugins => ColumnType::JsonBinary.def().null(), Self::MplCoreUnknownPlugins => ColumnType::JsonBinary.def().null(), Self::MplCoreCollectionNumMinted => ColumnType::Integer.def().null(), diff --git a/digital_asset_types/src/dao/generated/sea_orm_active_enums.rs b/digital_asset_types/src/dao/generated/sea_orm_active_enums.rs index e4d0e012d..cf7470c6f 100644 --- a/digital_asset_types/src/dao/generated/sea_orm_active_enums.rs +++ b/digital_asset_types/src/dao/generated/sea_orm_active_enums.rs @@ -66,6 +66,8 @@ pub enum V1AccountAttachments { MasterEditionV1, #[sea_orm(string_value = "master_edition_v2")] MasterEditionV2, + #[sea_orm(string_value = "token_inscription")] + TokenInscription, #[sea_orm(string_value = "unknown")] Unknown, } diff --git a/digital_asset_types/src/dao/generated/token_accounts.rs b/digital_asset_types/src/dao/generated/token_accounts.rs index 380e29b42..d758403b8 100644 --- a/digital_asset_types/src/dao/generated/token_accounts.rs +++ b/digital_asset_types/src/dao/generated/token_accounts.rs @@ -24,6 +24,7 @@ pub struct Model { pub delegated_amount: i64, pub slot_updated: i64, pub token_program: Vec, + pub extensions: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] @@ -38,6 +39,7 @@ pub enum Column { DelegatedAmount, SlotUpdated, TokenProgram, + Extensions, } #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] @@ -69,6 +71,7 @@ impl ColumnTrait for Column { Self::DelegatedAmount => ColumnType::BigInteger.def(), Self::SlotUpdated => ColumnType::BigInteger.def(), Self::TokenProgram => ColumnType::Binary.def(), + Self::Extensions => ColumnType::Json.def().null(), } } } diff --git a/digital_asset_types/src/dao/generated/tokens.rs b/digital_asset_types/src/dao/generated/tokens.rs index 326b8d968..ed1e5598d 100644 --- a/digital_asset_types/src/dao/generated/tokens.rs +++ b/digital_asset_types/src/dao/generated/tokens.rs @@ -23,6 +23,7 @@ pub struct Model { pub close_authority: Option>, pub extension_data: Option>, pub slot_updated: i64, + pub extensions: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] @@ -36,6 +37,7 @@ pub enum Column { CloseAuthority, ExtensionData, SlotUpdated, + Extensions, } #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] @@ -66,6 +68,7 @@ impl ColumnTrait for Column { Self::CloseAuthority => ColumnType::Binary.def().null(), Self::ExtensionData => ColumnType::Binary.def().null(), Self::SlotUpdated => ColumnType::BigInteger.def(), + Self::Extensions => ColumnType::JsonBinary.def().null(), } } } @@ -77,3 +80,13 @@ impl RelationTrait for Relation { } impl ActiveModelBehavior for ActiveModel {} + +pub trait IsNonFungible { + fn is_non_fungible(&self) -> bool; +} + +impl IsNonFungible for Model { + fn is_non_fungible(&self) -> bool { + self.decimals == 0 && self.supply == 1.into() + } +} diff --git a/digital_asset_types/src/dao/scopes/asset.rs b/digital_asset_types/src/dao/scopes/asset.rs index 58e4cbbaf..c241c4c4e 100644 --- a/digital_asset_types/src/dao/scopes/asset.rs +++ b/digital_asset_types/src/dao/scopes/asset.rs @@ -1,15 +1,24 @@ use crate::{ dao::{ asset::{self}, - asset_authority, asset_creators, asset_data, asset_grouping, cl_audits_v2, + asset_authority, asset_creators, asset_data, asset_grouping, asset_v1_account_attachments, + cl_audits_v2, extensions::{self, instruction::PascalCase}, - sea_orm_active_enums::Instruction, - Cursor, FullAsset, GroupingSize, Pagination, + sea_orm_active_enums::{Instruction, V1AccountAttachments}, + token_accounts, tokens, Cursor, FullAsset, GroupingSize, Pagination, + }, + rpc::{ + filter::AssetSortDirection, + options::Options, + response::{NftEdition, NftEditions}, }, - rpc::filter::AssetSortDirection, }; use indexmap::IndexMap; -use sea_orm::{entity::*, query::*, ConnectionTrait, DbErr, Order}; +use mpl_token_metadata::accounts::{Edition, MasterEdition}; +use sea_orm::{entity::*, query::*, sea_query::Expr, ConnectionTrait, DbErr, Order}; +use serde::de::DeserializeOwned; +use serde_json::Value; +use solana_sdk::pubkey::Pubkey; use std::collections::HashMap; pub fn paginate( @@ -60,7 +69,7 @@ pub async fn get_by_creator( sort_direction: Order, pagination: &Pagination, limit: u64, - show_unverified_collections: bool, + options: &Options, ) -> Result, DbErr> { let mut condition = Condition::all() .add(asset_creators::Column::Creator.eq(creator.clone())) @@ -76,7 +85,7 @@ pub async fn get_by_creator( sort_direction, pagination, limit, - show_unverified_collections, + options, Some(creator), ) .await @@ -112,13 +121,13 @@ pub async fn get_by_grouping( sort_direction: Order, pagination: &Pagination, limit: u64, - show_unverified_collections: bool, + options: &Options, ) -> Result, DbErr> { let mut condition = asset_grouping::Column::GroupKey .eq(group_key) .and(asset_grouping::Column::GroupValue.eq(group_value)); - if !show_unverified_collections { + if !options.show_unverified_collections { condition = condition.and( asset_grouping::Column::Verified .eq(true) @@ -136,7 +145,7 @@ pub async fn get_by_grouping( sort_direction, pagination, limit, - show_unverified_collections, + options, None, ) .await @@ -149,11 +158,12 @@ pub async fn get_assets_by_owner( sort_direction: Order, pagination: &Pagination, limit: u64, - show_unverified_collections: bool, + options: &Options, ) -> Result, DbErr> { let cond = Condition::all() - .add(asset::Column::Owner.eq(owner)) + .add(asset::Column::Owner.eq(owner.clone())) .add(asset::Column::Supply.gt(0)); + get_assets_by_condition( conn, cond, @@ -162,7 +172,7 @@ pub async fn get_assets_by_owner( sort_direction, pagination, limit, - show_unverified_collections, + options, ) .await } @@ -172,10 +182,12 @@ pub async fn get_assets( asset_ids: Vec>, pagination: &Pagination, limit: u64, + options: &Options, ) -> Result, DbErr> { let cond = Condition::all() .add(asset::Column::Id.is_in(asset_ids)) .add(asset::Column::Supply.gt(0)); + get_assets_by_condition( conn, cond, @@ -185,7 +197,7 @@ pub async fn get_assets( Order::Asc, pagination, limit, - false, + options, ) .await } @@ -197,7 +209,7 @@ pub async fn get_by_authority( sort_direction: Order, pagination: &Pagination, limit: u64, - show_unverified_collections: bool, + options: &Options, ) -> Result, DbErr> { let cond = Condition::all() .add(asset_authority::Column::Authority.eq(authority)) @@ -210,7 +222,7 @@ pub async fn get_by_authority( sort_direction, pagination, limit, - show_unverified_collections, + options, None, ) .await @@ -225,7 +237,7 @@ async fn get_by_related_condition( sort_direction: Order, pagination: &Pagination, limit: u64, - show_unverified_collections: bool, + options: &Options, required_creator: Option>, ) -> Result, DbErr> where @@ -244,19 +256,19 @@ where let assets = paginate(pagination, limit, stmt, sort_direction, asset::Column::Id) .all(conn) .await?; - get_related_for_assets(conn, assets, show_unverified_collections, required_creator).await + get_related_for_assets(conn, assets, options, required_creator).await } pub async fn get_related_for_assets( conn: &impl ConnectionTrait, assets: Vec, - show_unverified_collections: bool, + options: &Options, required_creator: Option>, ) -> Result, DbErr> { let asset_ids = assets.iter().map(|a| a.id.clone()).collect::>(); let asset_data: Vec = asset_data::Entity::find() - .filter(asset_data::Column::Id.is_in(asset_ids)) + .filter(asset_data::Column::Id.is_in(asset_ids.clone())) .all(conn) .await?; let asset_data_map = asset_data.into_iter().fold(HashMap::new(), |mut acc, ad| { @@ -278,6 +290,8 @@ pub async fn get_related_for_assets( authorities: vec![], creators: vec![], groups: vec![], + inscription: None, + token_info: None, }; acc.insert(id, fa); }; @@ -287,7 +301,7 @@ pub async fn get_related_for_assets( // Get all creators for all assets in `assets_map``. let creators = asset_creators::Entity::find() - .filter(asset_creators::Column::AssetId.is_in(ids.clone())) + .filter(asset_creators::Column::AssetId.is_in(ids)) .order_by_asc(asset_creators::Column::AssetId) .order_by_asc(asset_creators::Column::Position) .all(conn) @@ -325,7 +339,18 @@ pub async fn get_related_for_assets( } } - let cond = if show_unverified_collections { + if options.show_fungible { + find_tokens(conn, ids.clone()) + .await? + .into_iter() + .for_each(|t| { + if let Some(asset) = assets_map.get_mut(&t.mint.clone()) { + asset.token_info = Some(t); + } + }); + } + + let cond = if options.show_unverified_collections { Condition::all() } else { Condition::any() @@ -335,19 +360,44 @@ pub async fn get_related_for_assets( .add(asset_grouping::Column::Verified.is_null()) }; - let grouping = asset_grouping::Entity::find() + let grouping_base_query = asset_grouping::Entity::find() .filter(asset_grouping::Column::AssetId.is_in(ids.clone())) .filter(asset_grouping::Column::GroupValue.is_not_null()) .filter(cond) - .order_by_asc(asset_grouping::Column::AssetId) - .all(conn) - .await?; - for g in grouping.into_iter() { - if let Some(asset) = assets_map.get_mut(&g.asset_id) { - asset.groups.push(g); + .order_by_asc(asset_grouping::Column::AssetId); + + if options.show_inscription { + let attachments = asset_v1_account_attachments::Entity::find() + .filter(asset_v1_account_attachments::Column::AssetId.is_in(asset_ids)) + .all(conn) + .await?; + + for a in attachments.into_iter() { + if let Some(asset) = assets_map.get_mut(&a.id) { + asset.inscription = Some(a); + } } } + if options.show_collection_metadata { + let combined_group_query = grouping_base_query + .find_also_related(asset_data::Entity) + .all(conn) + .await?; + for (g, a) in combined_group_query.into_iter() { + if let Some(asset) = assets_map.get_mut(&g.asset_id) { + asset.groups.push((g, a)); + } + } + } else { + let single_group_query = grouping_base_query.all(conn).await?; + for g in single_group_query.into_iter() { + if let Some(asset) = assets_map.get_mut(&g.asset_id) { + asset.groups.push((g, None)); + } + } + }; + Ok(assets_map.into_iter().map(|(_, v)| v).collect()) } @@ -360,7 +410,7 @@ pub async fn get_assets_by_condition( sort_direction: Order, pagination: &Pagination, limit: u64, - show_unverified_collections: bool, + options: &Options, ) -> Result, DbErr> { let mut stmt = asset::Entity::find(); for def in joins { @@ -376,8 +426,7 @@ pub async fn get_assets_by_condition( let assets = paginate(pagination, limit, stmt, sort_direction, asset::Column::Id) .all(conn) .await?; - let full_assets = - get_related_for_assets(conn, assets, show_unverified_collections, None).await?; + let full_assets = get_related_for_assets(conn, assets, options, None).await?; Ok(full_assets) } @@ -385,19 +434,35 @@ pub async fn get_by_id( conn: &impl ConnectionTrait, asset_id: Vec, include_no_supply: bool, + options: &Options, ) -> Result { let mut asset_data = asset::Entity::find_by_id(asset_id.clone()).find_also_related(asset_data::Entity); if !include_no_supply { asset_data = asset_data.filter(Condition::all().add(asset::Column::Supply.gt(0))); } - let asset_data: (asset::Model, asset_data::Model) = + + let inscription = if options.show_inscription { + get_inscription_by_mint(conn, asset_id.clone()).await.ok() + } else { + None + }; + + let token_info = if options.show_fungible { + find_tokens(conn, vec![asset_id.clone()]) + .await? + .into_iter() + .next() + } else { + None + }; + + let (asset, data): (asset::Model, asset_data::Model) = asset_data.one(conn).await.and_then(|o| match o { Some((a, Some(d))) => Ok((a, d)), _ => Err(DbErr::RecordNotFound("Asset Not Found".to_string())), })?; - let (asset, data) = asset_data; let authorities: Vec = asset_authority::Entity::find() .filter(asset_authority::Column::AssetId.eq(asset.id.clone())) .order_by_asc(asset_authority::Column::AssetId) @@ -411,7 +476,7 @@ pub async fn get_by_id( filter_out_stale_creators(&mut creators); - let grouping: Vec = asset_grouping::Entity::find() + let grouping_query = asset_grouping::Entity::find() .filter(asset_grouping::Column::AssetId.eq(asset.id.clone())) .filter(asset_grouping::Column::GroupValue.is_not_null()) .filter( @@ -421,15 +486,30 @@ pub async fn get_by_id( // Therefore if verified is null, we can assume that the group is verified. .add(asset_grouping::Column::Verified.is_null()), ) - .order_by_asc(asset_grouping::Column::AssetId) - .all(conn) - .await?; + .order_by_asc(asset_grouping::Column::AssetId); + + let groups = if options.show_collection_metadata { + grouping_query + .find_also_related(asset_data::Entity) + .all(conn) + .await? + } else { + grouping_query + .all(conn) + .await? + .into_iter() + .map(|g| (g, None)) + .collect::>() + }; + Ok(FullAsset { asset, data, authorities, creators, - groups: grouping, + inscription, + groups, + token_info, }) } @@ -553,3 +633,184 @@ fn filter_out_stale_creators(creators: &mut Vec) { } } } + +pub async fn get_token_accounts( + conn: &impl ConnectionTrait, + owner_address: Option>, + mint_address: Option>, + pagination: &Pagination, + limit: u64, + options: &Options, +) -> Result, DbErr> { + let mut condition = Condition::all(); + + if options.show_zero_balance { + condition = condition.add(token_accounts::Column::Amount.gte(0)); + } else { + condition = condition.add(token_accounts::Column::Amount.gt(0)); + } + + if owner_address.is_none() && mint_address.is_none() { + return Err(DbErr::Custom( + "Either 'owner_address' or 'mint_address' must be provided".to_string(), + )); + } + + if let Some(owner) = owner_address { + condition = condition.add(token_accounts::Column::Owner.eq(owner)); + } + if let Some(mint) = mint_address { + condition = condition.add(token_accounts::Column::Mint.eq(mint)); + } + + let token_accounts = paginate( + pagination, + limit, + token_accounts::Entity::find().filter(condition), + Order::Asc, + token_accounts::Column::Pubkey, + ) + .all(conn) + .await?; + + Ok(token_accounts) +} + +fn get_edition_data_from_json(data: Value) -> Result { + serde_json::from_value(data).map_err(|e| DbErr::Custom(e.to_string())) +} + +fn attachment_to_nft_edition( + attachment: asset_v1_account_attachments::Model, +) -> Result { + let data: Edition = attachment + .data + .clone() + .ok_or(DbErr::RecordNotFound("Edition data not found".to_string())) + .map(get_edition_data_from_json)??; + + Ok(NftEdition { + mint_address: attachment + .asset_id + .clone() + .map(|id| bs58::encode(id).into_string()) + .unwrap_or("".to_string()), + edition_number: data.edition, + edition_address: bs58::encode(attachment.id.clone()).into_string(), + }) +} + +pub async fn get_nft_editions( + conn: &impl ConnectionTrait, + mint_address: Pubkey, + pagination: &Pagination, + limit: u64, +) -> Result { + let master_edition_pubkey = MasterEdition::find_pda(&mint_address).0; + + // to fetch nft editions associated with a mint we need to fetch the master edition first + let master_edition = + asset_v1_account_attachments::Entity::find_by_id(master_edition_pubkey.to_bytes().to_vec()) + .one(conn) + .await? + .ok_or(DbErr::RecordNotFound( + "Master Edition not found".to_string(), + ))?; + + let master_edition_data: MasterEdition = master_edition + .data + .clone() + .ok_or(DbErr::RecordNotFound( + "Master Edition data not found".to_string(), + )) + .map(get_edition_data_from_json)??; + + let mut stmt = asset_v1_account_attachments::Entity::find(); + + stmt = stmt.filter( + asset_v1_account_attachments::Column::AttachmentType + .eq(V1AccountAttachments::Edition) + // The data field is a JSON field that contains the edition data. + .and(asset_v1_account_attachments::Column::Data.is_not_null()) + // The parent field is a string field that contains the master edition pubkey ( mapping edition to master edition ) + .and(Expr::cust(&format!( + "data->>'parent' = '{}'", + master_edition_pubkey + ))), + ); + + let nft_editions = paginate( + pagination, + limit, + stmt, + Order::Asc, + asset_v1_account_attachments::Column::Id, + ) + .all(conn) + .await? + .into_iter() + .map(attachment_to_nft_edition) + .collect::, _>>()?; + + let (page, before, after, cursor) = match pagination { + Pagination::Keyset { before, after } => { + let bef = before.clone().and_then(|x| String::from_utf8(x).ok()); + let aft = after.clone().and_then(|x| String::from_utf8(x).ok()); + (None, bef, aft, None) + } + Pagination::Page { page } => (Some(*page as u32), None, None, None), + Pagination::Cursor(_) => { + if let Some(last_asset) = nft_editions.last() { + let cursor_str = bs58::encode(last_asset.edition_address.clone()).into_string(); + (None, None, None, Some(cursor_str)) + } else { + (None, None, None, None) + } + } + }; + + Ok(NftEditions { + total: nft_editions.len() as u32, + master_edition_address: master_edition_pubkey.to_string(), + supply: master_edition_data.supply, + max_supply: master_edition_data.max_supply, + editions: nft_editions, + limit: limit as u32, + page, + before, + after, + cursor, + }) +} + +async fn get_inscription_by_mint( + conn: &impl ConnectionTrait, + mint: Vec, +) -> Result { + asset_v1_account_attachments::Entity::find() + .filter( + asset_v1_account_attachments::Column::Data + .is_not_null() + .and(Expr::cust(&format!( + "data->>'root' = '{}'", + bs58::encode(mint).into_string() + ))), + ) + .one(conn) + .await + .and_then(|o| match o { + Some(t) => Ok(t), + _ => Err(DbErr::RecordNotFound("Inscription Not Found".to_string())), + }) +} + +async fn find_tokens( + conn: &impl ConnectionTrait, + ids: Vec>, +) -> Result, DbErr> { + tokens::Entity::find() + .filter(tokens::Column::Mint.is_in(ids)) + .all(conn) + .await + .map_err(|_| DbErr::RecordNotFound("Token (s) Not Found".to_string())) +} diff --git a/digital_asset_types/src/dapi/assets_by_authority.rs b/digital_asset_types/src/dapi/assets_by_authority.rs index 59404f3e0..b52891062 100644 --- a/digital_asset_types/src/dapi/assets_by_authority.rs +++ b/digital_asset_types/src/dapi/assets_by_authority.rs @@ -24,7 +24,7 @@ pub async fn get_assets_by_authority( sort_direction, &pagination, page_options.limit, - options.show_unverified_collections, + options, ) .await?; Ok(build_asset_response( diff --git a/digital_asset_types/src/dapi/assets_by_creator.rs b/digital_asset_types/src/dapi/assets_by_creator.rs index 9ce5de591..3a2b1e53e 100644 --- a/digital_asset_types/src/dapi/assets_by_creator.rs +++ b/digital_asset_types/src/dapi/assets_by_creator.rs @@ -27,7 +27,7 @@ pub async fn get_assets_by_creator( sort_direction, &pagination, page_options.limit, - options.show_unverified_collections, + options, ) .await?; Ok(build_asset_response( diff --git a/digital_asset_types/src/dapi/assets_by_group.rs b/digital_asset_types/src/dapi/assets_by_group.rs index 68784b9f4..36f4ae534 100644 --- a/digital_asset_types/src/dapi/assets_by_group.rs +++ b/digital_asset_types/src/dapi/assets_by_group.rs @@ -27,7 +27,7 @@ pub async fn get_assets_by_group( sort_direction, &pagination, page_options.limit, - options.show_unverified_collections, + options, ) .await?; Ok(build_asset_response( diff --git a/digital_asset_types/src/dapi/assets_by_owner.rs b/digital_asset_types/src/dapi/assets_by_owner.rs index c3c4da3a5..5f342fe9a 100644 --- a/digital_asset_types/src/dapi/assets_by_owner.rs +++ b/digital_asset_types/src/dapi/assets_by_owner.rs @@ -24,7 +24,7 @@ pub async fn get_assets_by_owner( sort_direction, &pagination, page_options.limit, - options.show_unverified_collections, + options, ) .await?; Ok(build_asset_response( diff --git a/digital_asset_types/src/dapi/change_logs.rs b/digital_asset_types/src/dapi/change_logs.rs index fdd19ddf9..4742696f0 100644 --- a/digital_asset_types/src/dapi/change_logs.rs +++ b/digital_asset_types/src/dapi/change_logs.rs @@ -206,7 +206,7 @@ fn build_asset_proof( let mut final_node_list = vec![SimpleChangeLog::default(); req_indexes.len()]; for node in required_nodes.iter() { if node.level < final_node_list.len().try_into().unwrap() { - final_node_list[node.level as usize] = node.to_owned(); + node.clone_into(&mut final_node_list[node.level as usize]) } } for (i, (n, nin)) in final_node_list diff --git a/digital_asset_types/src/dapi/common/asset.rs b/digital_asset_types/src/dapi/common/asset.rs index c9a9dd486..328970492 100644 --- a/digital_asset_types/src/dapi/common/asset.rs +++ b/digital_asset_types/src/dapi/common/asset.rs @@ -1,16 +1,21 @@ -use crate::dao::sea_orm_active_enums::SpecificationVersions; +use crate::dao::token_accounts; use crate::dao::FullAsset; use crate::dao::PageOptions; use crate::dao::Pagination; use crate::dao::{asset, asset_authority, asset_creators, asset_data, asset_grouping}; use crate::rpc::filter::{AssetSortBy, AssetSortDirection, AssetSorting}; use crate::rpc::options::Options; +use crate::rpc::response::TokenAccountList; use crate::rpc::response::TransactionSignatureList; -use crate::rpc::response::{AssetError, AssetList}; +use crate::rpc::response::{AssetList, DasError}; +use crate::rpc::TokenInfo; +use crate::rpc::TokenInscriptionInfo; use crate::rpc::{ Asset as RpcAsset, Authority, Compression, Content, Creator, File, Group, Interface, - MetadataMap, MplCoreInfo, Ownership, Royalty, Scope, Supply, Uses, + MetadataMap, MplCoreInfo, Ownership, Royalty, Scope, Supply, TokenAccount as RpcTokenAccount, + Uses, }; +use blockbuster::programs::token_inscriptions::InscriptionData; use jsonpath_lib::JsonPathError; use log::warn; use mime_guess::Mime; @@ -278,14 +283,8 @@ pub fn v1_content_from_json(asset_data: &asset_data::Model) -> Result Result { - match asset.specification_version { - Some(SpecificationVersions::V1) | Some(SpecificationVersions::V0) => { - v1_content_from_json(data) - } - Some(_) => Err(DbErr::Custom("Version Not Implemented".to_string())), - None => Err(DbErr::Custom("Specification version not found".to_string())), - } +pub fn get_content(data: &asset_data::Model) -> Option { + v1_content_from_json(data).ok() } pub fn to_authority(authority: Vec) -> Vec { @@ -310,34 +309,56 @@ pub fn to_creators(creators: Vec) -> Vec { } pub fn to_grouping( - groups: Vec, + groups: Vec<(asset_grouping::Model, Option)>, options: &Options, ) -> Result, DbErr> { let result: Vec = groups .iter() - .filter_map(|model| { + .filter_map(|(asset_group, asset_data)| { let verified = match options.show_unverified_collections { // Null verified indicates legacy data, meaning it is verified. - true => Some(model.verified), + true => Some(asset_group.verified), false => None, }; // Filter out items where group_value is None. - model.group_value.clone().map(|group_value| Group { - group_key: model.group_key.clone(), - group_value: Some(group_value), - verified, + asset_group.group_value.clone().map(|group_value| { + let collection_metadata = asset_data.as_ref().map(|data| { + let mut metadata_selector_fn = jsonpath_lib::selector(&data.metadata); + let metadata_selector = &mut metadata_selector_fn; + let mut meta: MetadataMap = MetadataMap::new(); + + if let Some(name) = safe_select(metadata_selector, "$.name") { + meta.set_item("name", name.clone()); + } + if let Some(symbol) = safe_select(metadata_selector, "$.symbol") { + meta.set_item("symbol", symbol.clone()); + } + if let Some(image) = safe_select(metadata_selector, "$.image") { + meta.set_item("image", image.clone()); + } + if let Some(external_url) = safe_select(metadata_selector, "$.external_url") { + meta.set_item("external_url", external_url.clone()); + } + + meta + }); + + Group { + group_key: asset_group.group_key.clone(), + group_value: Some(group_value), + verified, + collection_metadata, + } }) }) .collect(); + Ok(result) } pub fn get_interface(asset: &asset::Model) -> Result { Ok(Interface::from(( - asset - .specification_version - .as_ref() - .ok_or(DbErr::Custom("Specification version not found".to_string()))?, + asset.specification_version.as_ref(), asset .specification_asset_class .as_ref() @@ -355,19 +376,34 @@ pub fn asset_to_rpc(asset: FullAsset, options: &Options) -> Result Some(MplCoreInfo { num_minted: asset.mpl_core_collection_num_minted, @@ -377,12 +413,53 @@ pub fn asset_to_rpc(asset: FullAsset, options: &Options) -> Result None, }; + let inscription = if options.show_inscription { + inscription + .and_then(|i| { + i.data.map(|d| -> Result { + let deserialized_data: InscriptionData = + serde_json::from_value(d).map_err(|e| { + DbErr::Custom(format!("Failed to deserialize inscription data: {}", e)) + })?; + Ok(TokenInscriptionInfo { + authority: deserialized_data.authority, + root: deserialized_data.root, + content: deserialized_data.content, + encoding: deserialized_data.encoding, + inscription_data: deserialized_data.inscription_data, + order: deserialized_data.order, + size: deserialized_data.size, + validation_hash: deserialized_data.validation_hash, + }) + }) + }) + .and_then(|i| i.ok()) + } else { + None + }; + + let token_info = if options.show_fungible { + token_info.map(|token_info| TokenInfo { + supply: token_info.supply.try_into().unwrap_or(0), + decimals: token_info.decimals as u8, + mint_authority: token_info + .mint_authority + .map(|s| bs58::encode(s).into_string()), + freeze_authority: token_info + .freeze_authority + .map(|s| bs58::encode(s).into_string()), + token_program: bs58::encode(token_info.token_program).into_string(), + }) + } else { + None + }; + Ok(RpcAsset { interface: interface.clone(), id: bs58::encode(asset.id).into_string(), - content: Some(content), + content, authorities: Some(rpc_authorities), - mutable: data.chain_data_mutability.into(), + mutable, compression: Some(Compression { eligible: asset.compressible, compressed: asset.compressed, @@ -415,7 +492,7 @@ pub fn asset_to_rpc(asset: FullAsset, options: &Options) -> Result Result Some(Supply { edition_nonce, @@ -433,17 +510,11 @@ pub fn asset_to_rpc(asset: FullAsset, options: &Options) -> Result None, }, - uses: data.chain_data.get("uses").map(|u| Uses { - use_method: u - .get("use_method") - .and_then(|s| s.as_str()) - .unwrap_or("Single") - .to_string() - .into(), - total: u.get("total").and_then(|t| t.as_u64()).unwrap_or(0), - remaining: u.get("remaining").and_then(|t| t.as_u64()).unwrap_or(0), - }), + uses, burnt: asset.burnt, + token_info, + mint_extensions: asset.mint_extensions, + inscription, plugins: asset.mpl_core_plugins, unknown_plugins: asset.mpl_core_unknown_plugins, mpl_core_info, @@ -455,14 +526,14 @@ pub fn asset_to_rpc(asset: FullAsset, options: &Options) -> Result, options: &Options, -) -> (Vec, Vec) { +) -> (Vec, Vec) { asset_list .into_iter() .fold((vec![], vec![]), |(mut assets, mut errors), asset| { let id = bs58::encode(asset.asset.id.clone()).into_string(); match asset_to_rpc(asset, options) { Ok(rpc_asset) => assets.push(rpc_asset), - Err(e) => errors.push(AssetError { + Err(e) => errors.push(DasError { id, error: e.to_string(), }), @@ -470,3 +541,87 @@ pub fn asset_list_to_rpc( (assets, errors) }) } + +pub fn token_account_to_rpc( + token_account: token_accounts::Model, + _options: &Options, +) -> Result { + let address = bs58::encode(token_account.pubkey.clone()).into_string(); + let mint = bs58::encode(token_account.mint.clone()).into_string(); + let owner = bs58::encode(token_account.owner.clone()).into_string(); + let delegate = token_account + .delegate + .map(|d| bs58::encode(d).into_string()); + let close_authority = token_account + .close_authority + .map(|d| bs58::encode(d).into_string()); + + Ok(RpcTokenAccount { + address, + mint, + amount: token_account.amount as u64, + owner, + frozen: token_account.frozen, + delegate, + delegated_amount: token_account.delegated_amount as u64, + close_authority, + extensions: None, + }) +} + +pub fn token_account_list_to_rpc( + token_accounts: Vec, + options: &Options, +) -> (Vec, Vec) { + token_accounts.into_iter().fold( + (vec![], vec![]), + |(mut accounts, mut errors), token_account| { + let id = bs58::encode(token_account.pubkey.clone()).into_string(); + match token_account_to_rpc(token_account, options) { + Ok(rpc_token_account) => accounts.push(rpc_token_account), + Err(e) => errors.push(DasError { + id, + error: e.to_string(), + }), + } + (accounts, errors) + }, + ) +} + +pub fn build_token_list_response( + token_accounts: Vec, + limit: u64, + pagination: &Pagination, + options: &Options, +) -> TokenAccountList { + let total = token_accounts.len() as u32; + let (page, before, after, cursor) = match pagination { + Pagination::Keyset { before, after } => { + let bef = before.clone().and_then(|x| String::from_utf8(x).ok()); + let aft = after.clone().and_then(|x| String::from_utf8(x).ok()); + (None, bef, aft, None) + } + Pagination::Page { page } => (Some(*page as u32), None, None, None), + Pagination::Cursor(_) => { + if let Some(last_token_account) = token_accounts.last() { + let cursor_str = bs58::encode(&last_token_account.pubkey.clone()).into_string(); + (None, None, None, Some(cursor_str)) + } else { + (None, None, None, None) + } + } + }; + + let (items, errors) = token_account_list_to_rpc(token_accounts, options); + TokenAccountList { + total, + limit: limit as u32, + page, + before, + after, + token_accounts: items, + cursor, + errors, + } +} diff --git a/digital_asset_types/src/dapi/get_asset.rs b/digital_asset_types/src/dapi/get_asset.rs index 3740562c3..2ec7d9006 100644 --- a/digital_asset_types/src/dapi/get_asset.rs +++ b/digital_asset_types/src/dapi/get_asset.rs @@ -11,7 +11,7 @@ pub async fn get_asset( id: Vec, options: &Options, ) -> Result { - let asset = scopes::asset::get_by_id(db, id, false).await?; + let asset = scopes::asset::get_by_id(db, id, false, options).await?; asset_to_rpc(asset, options) } @@ -22,7 +22,7 @@ pub async fn get_assets( options: &Options, ) -> Result, DbErr> { let pagination = Pagination::Page { page: 1 }; - let assets = scopes::asset::get_assets(db, ids, &pagination, limit).await?; + let assets = scopes::asset::get_assets(db, ids, &pagination, limit, options).await?; let asset_list = build_asset_response(assets, limit, &pagination, options); let asset_map = asset_list .items diff --git a/digital_asset_types/src/dapi/get_token_accounts.rs b/digital_asset_types/src/dapi/get_token_accounts.rs new file mode 100644 index 000000000..28dcfc503 --- /dev/null +++ b/digital_asset_types/src/dapi/get_token_accounts.rs @@ -0,0 +1,35 @@ +use sea_orm::{DatabaseConnection, DbErr}; + +use crate::{ + dao::PageOptions, + rpc::{options::Options, response::TokenAccountList}, +}; + +use super::common::{build_token_list_response, create_pagination}; + +pub async fn get_token_accounts( + db: &DatabaseConnection, + owner_address: Option>, + mint_address: Option>, + page_options: &PageOptions, + options: &Options, +) -> Result { + let pagination = create_pagination(page_options)?; + + let token_accounts = crate::dao::scopes::asset::get_token_accounts( + db, + owner_address, + mint_address, + &pagination, + page_options.limit, + options, + ) + .await?; + + Ok(build_token_list_response( + token_accounts, + page_options.limit, + &pagination, + options, + )) +} diff --git a/digital_asset_types/src/dapi/mod.rs b/digital_asset_types/src/dapi/mod.rs index e9481169a..488a474bd 100644 --- a/digital_asset_types/src/dapi/mod.rs +++ b/digital_asset_types/src/dapi/mod.rs @@ -5,6 +5,7 @@ mod assets_by_owner; mod change_logs; mod get_asset; mod get_asset_signatures; +mod get_token_accounts; mod search_assets; pub mod common; @@ -16,4 +17,5 @@ pub use assets_by_owner::*; pub use change_logs::*; pub use get_asset::*; pub use get_asset_signatures::*; +pub use get_token_accounts::*; pub use search_assets::*; diff --git a/digital_asset_types/src/dapi/search_assets.rs b/digital_asset_types/src/dapi/search_assets.rs index a7ee65509..85a0f190d 100644 --- a/digital_asset_types/src/dapi/search_assets.rs +++ b/digital_asset_types/src/dapi/search_assets.rs @@ -23,7 +23,7 @@ pub async fn search_assets( sort_direction, &pagination, page_options.limit, - options.show_unverified_collections, + options, ) .await?; Ok(build_asset_response( diff --git a/digital_asset_types/src/rpc/asset.rs b/digital_asset_types/src/rpc/asset.rs index 751ef3841..7467e5fab 100644 --- a/digital_asset_types/src/rpc/asset.rs +++ b/digital_asset_types/src/rpc/asset.rs @@ -21,22 +21,22 @@ pub struct AssetProof { pub tree_id: String, } -#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema, Default)] pub enum Interface { #[serde(rename = "V1_NFT")] V1NFT, #[serde(rename = "V1_PRINT")] V1PRINT, - #[serde(rename = "LEGACY_NFT")] + #[serde(rename = "V2_NFT")] + Nft, // TODO: change on version bump + #[serde(rename = "LEGACY_NFT")] #[allow(non_camel_case_types)] LEGACY_NFT, - #[serde(rename = "V2_NFT")] - Nft, #[serde(rename = "FungibleAsset")] FungibleAsset, - #[serde(rename = "Custom")] - Custom, + #[serde(rename = "FungibleToken")] + FungibleToken, #[serde(rename = "Identity")] Identity, #[serde(rename = "Executable")] @@ -47,19 +47,28 @@ pub enum Interface { MplCoreAsset, #[serde(rename = "MplCoreCollection")] MplCoreCollection, + #[default] + #[serde(rename = "Custom")] + Custom, } -impl From<(&SpecificationVersions, &SpecificationAssetClass)> for Interface { - fn from(i: (&SpecificationVersions, &SpecificationAssetClass)) -> Self { +impl From<(Option<&SpecificationVersions>, &SpecificationAssetClass)> for Interface { + fn from(i: (Option<&SpecificationVersions>, &SpecificationAssetClass)) -> Self { match i { - (SpecificationVersions::V1, SpecificationAssetClass::Nft) => Interface::V1NFT, - (SpecificationVersions::V1, SpecificationAssetClass::PrintableNft) => Interface::V1NFT, - (SpecificationVersions::V0, SpecificationAssetClass::Nft) => Interface::LEGACY_NFT, - (SpecificationVersions::V1, SpecificationAssetClass::ProgrammableNft) => { + (Some(SpecificationVersions::V1), SpecificationAssetClass::Nft) => Interface::V1NFT, + (Some(SpecificationVersions::V1), SpecificationAssetClass::PrintableNft) => { + Interface::V1NFT + } + (Some(SpecificationVersions::V0), SpecificationAssetClass::Nft) => { + Interface::LEGACY_NFT + } + (Some(SpecificationVersions::V1), SpecificationAssetClass::ProgrammableNft) => { Interface::ProgrammableNFT } (_, SpecificationAssetClass::MplCoreAsset) => Interface::MplCoreAsset, (_, SpecificationAssetClass::MplCoreCollection) => Interface::MplCoreCollection, + (_, SpecificationAssetClass::FungibleAsset) => Interface::FungibleAsset, + (_, SpecificationAssetClass::FungibleToken) => Interface::FungibleToken, _ => Interface::Custom, } } @@ -87,6 +96,10 @@ impl From for (SpecificationVersions, SpecificationAssetClass) { SpecificationVersions::V1, SpecificationAssetClass::MplCoreCollection, ), + Interface::FungibleToken => ( + SpecificationVersions::V1, + SpecificationAssetClass::FungibleToken, + ), _ => (SpecificationVersions::V1, SpecificationAssetClass::Unknown), } } @@ -219,6 +232,8 @@ pub struct Group { pub group_value: Option, #[serde(skip_serializing_if = "Option::is_none")] pub verified: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub collection_metadata: Option, } #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] @@ -367,6 +382,29 @@ pub struct MplCoreInfo { } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct TokenInscriptionInfo { + pub authority: String, + pub root: String, + pub inscription_data: String, + pub content: String, + pub encoding: String, + pub order: u64, + pub size: u32, + pub validation_hash: Option, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct TokenInfo { + pub supply: u64, + pub decimals: u8, + pub token_program: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub mint_authority: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub freeze_authority: Option, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, Default)] pub struct Asset { pub interface: Interface, pub id: String, @@ -382,13 +420,21 @@ pub struct Asset { pub royalty: Option, #[serde(skip_serializing_if = "Option::is_none")] pub creators: Option>, - pub ownership: Ownership, + #[serde(skip_serializing_if = "Option::is_none")] + pub ownership: Option, #[serde(skip_serializing_if = "Option::is_none")] pub uses: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub supply: Option, pub mutable: bool, pub burnt: bool, #[serde(skip_serializing_if = "Option::is_none")] + pub mint_extensions: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub inscription: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub token_info: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub plugins: Option, #[serde(skip_serializing_if = "Option::is_none")] pub unknown_plugins: Option, @@ -399,3 +445,17 @@ pub struct Asset { #[serde(skip_serializing_if = "Option::is_none")] pub unknown_external_plugins: Option, } + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default, JsonSchema)] +#[serde(default)] +pub struct TokenAccount { + pub address: String, + pub mint: String, + pub amount: u64, + pub owner: String, + pub frozen: bool, + pub delegate: Option, + pub delegated_amount: u64, + pub close_authority: Option, + pub extensions: Option, +} diff --git a/digital_asset_types/src/rpc/options.rs b/digital_asset_types/src/rpc/options.rs index a1fafa2c4..e1a57f9ce 100644 --- a/digital_asset_types/src/rpc/options.rs +++ b/digital_asset_types/src/rpc/options.rs @@ -6,4 +6,12 @@ use serde::{Deserialize, Serialize}; pub struct Options { #[serde(default)] pub show_unverified_collections: bool, + #[serde(default)] + pub show_collection_metadata: bool, + #[serde(default)] + pub show_zero_balance: bool, + #[serde(default)] + pub show_inscription: bool, + #[serde(default)] + pub show_fungible: bool, } diff --git a/digital_asset_types/src/rpc/response.rs b/digital_asset_types/src/rpc/response.rs index 53076c8b2..641f39640 100644 --- a/digital_asset_types/src/rpc/response.rs +++ b/digital_asset_types/src/rpc/response.rs @@ -1,12 +1,13 @@ use schemars::JsonSchema; + use { - crate::rpc::Asset, + crate::rpc::{Asset, TokenAccount}, serde::{Deserialize, Serialize}, }; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default, JsonSchema)] #[serde(default)] -pub struct AssetError { +pub struct DasError { pub id: String, pub error: String, } @@ -34,7 +35,7 @@ pub struct AssetList { pub cursor: Option, pub items: Vec, #[serde(skip_serializing_if = "Vec::is_empty")] - pub errors: Vec, + pub errors: Vec, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default, JsonSchema)] @@ -50,3 +51,48 @@ pub struct TransactionSignatureList { pub after: Option, pub items: Vec<(String, String)>, } + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default, JsonSchema)] +#[serde(default)] +pub struct TokenAccountList { + pub total: u32, + pub limit: u32, + #[serde(skip_serializing_if = "Option::is_none")] + pub page: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub before: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub after: Option, + pub token_accounts: Vec, + pub cursor: Option, + pub errors: Vec, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default, JsonSchema)] +#[serde(default)] + +pub struct NftEdition { + pub mint_address: String, + pub edition_address: String, + pub edition_number: u64, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default, JsonSchema)] +#[serde(default)] +pub struct NftEditions { + pub total: u32, + pub limit: u32, + pub master_edition_address: String, + pub supply: u64, + pub max_supply: Option, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub editions: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub page: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub before: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub after: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub cursor: Option, +} diff --git a/digital_asset_types/tests/common.rs b/digital_asset_types/tests/common.rs index 0dcad71ef..6f40f610e 100644 --- a/digital_asset_types/tests/common.rs +++ b/digital_asset_types/tests/common.rs @@ -169,6 +169,7 @@ pub fn create_asset( mpl_core_plugins_json_version: None, mpl_core_external_plugins: None, mpl_core_unknown_external_plugins: None, + mint_extensions: None, }, ) } diff --git a/integration_tests/tests/data/accounts/fungible_token_get_asset/5x38Kp4hvdomTCnCrAny4UtMUt5rQBdB6px2K1Ui45Wq b/integration_tests/tests/data/accounts/fungible_token_get_asset/5x38Kp4hvdomTCnCrAny4UtMUt5rQBdB6px2K1Ui45Wq new file mode 100644 index 000000000..a6006eeb7 Binary files /dev/null and b/integration_tests/tests/data/accounts/fungible_token_get_asset/5x38Kp4hvdomTCnCrAny4UtMUt5rQBdB6px2K1Ui45Wq differ diff --git a/integration_tests/tests/data/accounts/fungible_token_get_asset/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v b/integration_tests/tests/data/accounts/fungible_token_get_asset/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v new file mode 100644 index 000000000..182b80a9a Binary files /dev/null and b/integration_tests/tests/data/accounts/fungible_token_get_asset/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v differ diff --git a/integration_tests/tests/data/accounts/fungible_token_get_asset_scenario_1/5x38Kp4hvdomTCnCrAny4UtMUt5rQBdB6px2K1Ui45Wq b/integration_tests/tests/data/accounts/fungible_token_get_asset_scenario_1/5x38Kp4hvdomTCnCrAny4UtMUt5rQBdB6px2K1Ui45Wq new file mode 100644 index 000000000..1326753e3 Binary files /dev/null and b/integration_tests/tests/data/accounts/fungible_token_get_asset_scenario_1/5x38Kp4hvdomTCnCrAny4UtMUt5rQBdB6px2K1Ui45Wq differ diff --git a/integration_tests/tests/data/accounts/fungible_token_get_asset_scenario_1/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v b/integration_tests/tests/data/accounts/fungible_token_get_asset_scenario_1/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v new file mode 100644 index 000000000..3ffad1cc0 Binary files /dev/null and b/integration_tests/tests/data/accounts/fungible_token_get_asset_scenario_1/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v differ diff --git a/integration_tests/tests/data/accounts/fungible_token_get_asset_scenario_2/wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 b/integration_tests/tests/data/accounts/fungible_token_get_asset_scenario_2/wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 new file mode 100644 index 000000000..49c0c5373 Binary files /dev/null and b/integration_tests/tests/data/accounts/fungible_token_get_asset_scenario_2/wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK b/integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK new file mode 100644 index 000000000..a4776424f Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti b/integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti new file mode 100644 index 000000000..1470bf74a Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM b/integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM new file mode 100644 index 000000000..14163db13 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK b/integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK new file mode 100644 index 000000000..eee1c6f44 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti b/integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti new file mode 100644 index 000000000..2b0af9ec3 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM b/integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM new file mode 100644 index 000000000..200463c35 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK new file mode 100644 index 000000000..607ad146f Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti new file mode 100644 index 000000000..fdc448537 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM new file mode 100644 index 000000000..a0da0ece2 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/6BRNfDfdq1nKyU1TQiCEQLWyPtD8EwUH9Kt2ahsbidUx b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/6BRNfDfdq1nKyU1TQiCEQLWyPtD8EwUH9Kt2ahsbidUx new file mode 100644 index 000000000..035a8fc7e Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/6BRNfDfdq1nKyU1TQiCEQLWyPtD8EwUH9Kt2ahsbidUx differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/7BajpcYgnxmWK91RhrfsdB3Tm83PcDwPvMC8ZinvtTY6 b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/7BajpcYgnxmWK91RhrfsdB3Tm83PcDwPvMC8ZinvtTY6 new file mode 100644 index 000000000..fb22589cd Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/7BajpcYgnxmWK91RhrfsdB3Tm83PcDwPvMC8ZinvtTY6 differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/7EYnhQoR9YM3N7UoaKRoA44Uy8JeaZV3qyouov87awMs b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/7EYnhQoR9YM3N7UoaKRoA44Uy8JeaZV3qyouov87awMs new file mode 100644 index 000000000..07a90f4b0 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/7EYnhQoR9YM3N7UoaKRoA44Uy8JeaZV3qyouov87awMs differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_collection_metadata_option/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK b/integration_tests/tests/data/accounts/get_asset_with_show_collection_metadata_option/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK new file mode 100644 index 000000000..3d17826b4 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_collection_metadata_option/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_collection_metadata_option/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti b/integration_tests/tests/data/accounts/get_asset_with_show_collection_metadata_option/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti new file mode 100644 index 000000000..5079e4397 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_collection_metadata_option/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_collection_metadata_option/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM b/integration_tests/tests/data/accounts/get_asset_with_show_collection_metadata_option/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM new file mode 100644 index 000000000..313f7a164 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_collection_metadata_option/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/7z6b5TE4WX4mgcQjuNBTDxK4SE75sbgEg5WWJwoUeie8 b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/7z6b5TE4WX4mgcQjuNBTDxK4SE75sbgEg5WWJwoUeie8 new file mode 100644 index 000000000..7670328be Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/7z6b5TE4WX4mgcQjuNBTDxK4SE75sbgEg5WWJwoUeie8 differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/8myaCN6KcKVkMqroXuLJq6QsqRcPbvme4wV5Ubfr5mDC b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/8myaCN6KcKVkMqroXuLJq6QsqRcPbvme4wV5Ubfr5mDC new file mode 100644 index 000000000..69f99d0d5 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/8myaCN6KcKVkMqroXuLJq6QsqRcPbvme4wV5Ubfr5mDC differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/Ca84nWhQu41DMRnjdhRrLZty1i9txepMhAhz5qLLGcBw b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/Ca84nWhQu41DMRnjdhRrLZty1i9txepMhAhz5qLLGcBw new file mode 100644 index 000000000..f273bd7c1 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/Ca84nWhQu41DMRnjdhRrLZty1i9txepMhAhz5qLLGcBw differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK new file mode 100644 index 000000000..de4e24d36 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti new file mode 100644 index 000000000..3ffe5ac43 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM new file mode 100644 index 000000000..4922084e3 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_3/2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_3/2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo new file mode 100644 index 000000000..824ca37b0 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_3/2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_4/8yn5oqFMwYA8SgGqWwKq1Hia8aM5gh1DWmHEL34hMqBX b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_4/8yn5oqFMwYA8SgGqWwKq1Hia8aM5gh1DWmHEL34hMqBX new file mode 100644 index 000000000..4e6e98e0e Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_4/8yn5oqFMwYA8SgGqWwKq1Hia8aM5gh1DWmHEL34hMqBX differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_4/J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_4/J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn new file mode 100644 index 000000000..4a427f28f Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_4/J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/4Q18N6XrfJHgDbRTaHJR328jN9dixCLQAQhDsTsRzg3v b/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/4Q18N6XrfJHgDbRTaHJR328jN9dixCLQAQhDsTsRzg3v new file mode 100644 index 000000000..7443f1f46 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/4Q18N6XrfJHgDbRTaHJR328jN9dixCLQAQhDsTsRzg3v differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/9FkS3kZV4MoGps14tUSp7iVnizGbxcK4bDEhSoF5oYAZ b/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/9FkS3kZV4MoGps14tUSp7iVnizGbxcK4bDEhSoF5oYAZ new file mode 100644 index 000000000..1478546cd Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/9FkS3kZV4MoGps14tUSp7iVnizGbxcK4bDEhSoF5oYAZ differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/AKo9P7S8FE9NYeAcrtZEpimwQAXJMp8Lrt8p4dMkHkY2 b/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/AKo9P7S8FE9NYeAcrtZEpimwQAXJMp8Lrt8p4dMkHkY2 new file mode 100644 index 000000000..b6b5149df Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/AKo9P7S8FE9NYeAcrtZEpimwQAXJMp8Lrt8p4dMkHkY2 differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/DarH4z6SmdVzPrt8krAygpLodhdjvNAstP3taj2tysN2 b/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/DarH4z6SmdVzPrt8krAygpLodhdjvNAstP3taj2tysN2 new file mode 100644 index 000000000..cc574d2ce Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/DarH4z6SmdVzPrt8krAygpLodhdjvNAstP3taj2tysN2 differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/HMixBLSkuhiGgVbcGhqJar476xzu1bC8wM7yHsc1iXwP b/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/HMixBLSkuhiGgVbcGhqJar476xzu1bC8wM7yHsc1iXwP new file mode 100644 index 000000000..ee9399855 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/HMixBLSkuhiGgVbcGhqJar476xzu1bC8wM7yHsc1iXwP differ diff --git a/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/2ecGsTKbj7FecLwxTHaodZRFwza7m7LamqDG4YjczZMj b/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/2ecGsTKbj7FecLwxTHaodZRFwza7m7LamqDG4YjczZMj new file mode 100644 index 000000000..1ae41d2d6 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/2ecGsTKbj7FecLwxTHaodZRFwza7m7LamqDG4YjczZMj differ diff --git a/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/DZAZ3mGuq7nCYGzUyw4MiA74ysr15EfqLpzCzX2cRVng b/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/DZAZ3mGuq7nCYGzUyw4MiA74ysr15EfqLpzCzX2cRVng new file mode 100644 index 000000000..3e817c9dc Binary files /dev/null and b/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/DZAZ3mGuq7nCYGzUyw4MiA74ysr15EfqLpzCzX2cRVng differ diff --git a/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/F9Lw3ki3hJ7PF9HQXsBzoY8GyE6sPoEZZdXJBsTTD2rk b/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/F9Lw3ki3hJ7PF9HQXsBzoY8GyE6sPoEZZdXJBsTTD2rk new file mode 100644 index 000000000..4908e565b Binary files /dev/null and b/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/F9Lw3ki3hJ7PF9HQXsBzoY8GyE6sPoEZZdXJBsTTD2rk differ diff --git a/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/JEKKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X b/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/JEKKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X new file mode 100644 index 000000000..eee1c3953 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/JEKKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X differ diff --git a/integration_tests/tests/data/accounts/get_nft_editions/4V9QuYLpiMu4ZQmhdEHmgATdgiHkDeJfvZi84BfkYcez b/integration_tests/tests/data/accounts/get_nft_editions/4V9QuYLpiMu4ZQmhdEHmgATdgiHkDeJfvZi84BfkYcez new file mode 100644 index 000000000..558488282 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_nft_editions/4V9QuYLpiMu4ZQmhdEHmgATdgiHkDeJfvZi84BfkYcez differ diff --git a/integration_tests/tests/data/accounts/get_nft_editions/8SHfqzJYABeGfiG1apwiEYt6TvfGQiL1pdwEjvTKsyiZ b/integration_tests/tests/data/accounts/get_nft_editions/8SHfqzJYABeGfiG1apwiEYt6TvfGQiL1pdwEjvTKsyiZ new file mode 100644 index 000000000..10eae717a Binary files /dev/null and b/integration_tests/tests/data/accounts/get_nft_editions/8SHfqzJYABeGfiG1apwiEYt6TvfGQiL1pdwEjvTKsyiZ differ diff --git a/integration_tests/tests/data/accounts/get_nft_editions/9ZmY7qCaq7WbrR7RZdHWCNS9FrFRPwRqU84wzWfmqLDz b/integration_tests/tests/data/accounts/get_nft_editions/9ZmY7qCaq7WbrR7RZdHWCNS9FrFRPwRqU84wzWfmqLDz new file mode 100644 index 000000000..c8244da85 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_nft_editions/9ZmY7qCaq7WbrR7RZdHWCNS9FrFRPwRqU84wzWfmqLDz differ diff --git a/integration_tests/tests/data/accounts/get_nft_editions/9yQecKKYSHxez7fFjJkUvkz42TLmkoXzhyZxEf2pw8pz b/integration_tests/tests/data/accounts/get_nft_editions/9yQecKKYSHxez7fFjJkUvkz42TLmkoXzhyZxEf2pw8pz new file mode 100644 index 000000000..75d190842 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_nft_editions/9yQecKKYSHxez7fFjJkUvkz42TLmkoXzhyZxEf2pw8pz differ diff --git a/integration_tests/tests/data/accounts/get_nft_editions/AoxgzXKEsJmUyF5pBb3djn9cJFA26zh2SQHvd9EYijZV b/integration_tests/tests/data/accounts/get_nft_editions/AoxgzXKEsJmUyF5pBb3djn9cJFA26zh2SQHvd9EYijZV new file mode 100644 index 000000000..31abb9fb6 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_nft_editions/AoxgzXKEsJmUyF5pBb3djn9cJFA26zh2SQHvd9EYijZV differ diff --git a/integration_tests/tests/data/accounts/get_nft_editions/Ey2Qb8kLctbchQsMnhZs5DjY32To2QtPuXNwWvk4NosL b/integration_tests/tests/data/accounts/get_nft_editions/Ey2Qb8kLctbchQsMnhZs5DjY32To2QtPuXNwWvk4NosL new file mode 100644 index 000000000..cf8f217c8 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_nft_editions/Ey2Qb8kLctbchQsMnhZs5DjY32To2QtPuXNwWvk4NosL differ diff --git a/integration_tests/tests/data/accounts/get_nft_editions/GJvFDcBWf6aDncd1TBzx2ou1rgLFYaMBdbYLBa9oTAEw b/integration_tests/tests/data/accounts/get_nft_editions/GJvFDcBWf6aDncd1TBzx2ou1rgLFYaMBdbYLBa9oTAEw new file mode 100644 index 000000000..80ea336fb Binary files /dev/null and b/integration_tests/tests/data/accounts/get_nft_editions/GJvFDcBWf6aDncd1TBzx2ou1rgLFYaMBdbYLBa9oTAEw differ diff --git a/integration_tests/tests/data/accounts/get_nft_editions/giWoA4jqHFkodPJgtbRYRcYtiXbsVytnxnEao3QT2gg b/integration_tests/tests/data/accounts/get_nft_editions/giWoA4jqHFkodPJgtbRYRcYtiXbsVytnxnEao3QT2gg new file mode 100644 index 000000000..a63f177f0 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_nft_editions/giWoA4jqHFkodPJgtbRYRcYtiXbsVytnxnEao3QT2gg differ diff --git a/integration_tests/tests/data/accounts/get_token_accounts/jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2 b/integration_tests/tests/data/accounts/get_token_accounts/jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2 new file mode 100644 index 000000000..b4c5bef73 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_token_accounts/jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2 differ diff --git a/integration_tests/tests/data/accounts/get_token_accounts_by_mint/jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2 b/integration_tests/tests/data/accounts/get_token_accounts_by_mint/jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2 new file mode 100644 index 000000000..71c23c91d Binary files /dev/null and b/integration_tests/tests/data/accounts/get_token_accounts_by_mint/jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2 differ diff --git a/integration_tests/tests/data/accounts/get_token_accounts_by_owner/3Pv9H5UzU8T9BwgutXrcn2wLohS1JUZuk3x8paiRyzui b/integration_tests/tests/data/accounts/get_token_accounts_by_owner/3Pv9H5UzU8T9BwgutXrcn2wLohS1JUZuk3x8paiRyzui new file mode 100644 index 000000000..20e262776 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_token_accounts_by_owner/3Pv9H5UzU8T9BwgutXrcn2wLohS1JUZuk3x8paiRyzui differ diff --git a/integration_tests/tests/data/accounts/get_token_accounts_by_owner/jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2 b/integration_tests/tests/data/accounts/get_token_accounts_by_owner/jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2 new file mode 100644 index 000000000..868b38969 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_token_accounts_by_owner/jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2 differ diff --git a/integration_tests/tests/data/accounts/show_zero_balance_filter_being_disabled/BE1CkzRjLTXAWcSVCaqzycwXsZ18Yuk3jMDMnPUoHjjS b/integration_tests/tests/data/accounts/show_zero_balance_filter_being_disabled/BE1CkzRjLTXAWcSVCaqzycwXsZ18Yuk3jMDMnPUoHjjS new file mode 100644 index 000000000..5cf4586cc Binary files /dev/null and b/integration_tests/tests/data/accounts/show_zero_balance_filter_being_disabled/BE1CkzRjLTXAWcSVCaqzycwXsZ18Yuk3jMDMnPUoHjjS differ diff --git a/integration_tests/tests/data/accounts/show_zero_balance_filter_being_disabled/CyqarC6hyNYvb3EDueyeYrnGeAUjCDtMvWrbtdAnA53a b/integration_tests/tests/data/accounts/show_zero_balance_filter_being_disabled/CyqarC6hyNYvb3EDueyeYrnGeAUjCDtMvWrbtdAnA53a new file mode 100644 index 000000000..0f73124a7 Binary files /dev/null and b/integration_tests/tests/data/accounts/show_zero_balance_filter_being_disabled/CyqarC6hyNYvb3EDueyeYrnGeAUjCDtMvWrbtdAnA53a differ diff --git a/integration_tests/tests/data/accounts/show_zero_balance_filter_being_enabled/BE1CkzRjLTXAWcSVCaqzycwXsZ18Yuk3jMDMnPUoHjjS b/integration_tests/tests/data/accounts/show_zero_balance_filter_being_enabled/BE1CkzRjLTXAWcSVCaqzycwXsZ18Yuk3jMDMnPUoHjjS new file mode 100644 index 000000000..a25676b25 Binary files /dev/null and b/integration_tests/tests/data/accounts/show_zero_balance_filter_being_enabled/BE1CkzRjLTXAWcSVCaqzycwXsZ18Yuk3jMDMnPUoHjjS differ diff --git a/integration_tests/tests/data/accounts/show_zero_balance_filter_being_enabled/CyqarC6hyNYvb3EDueyeYrnGeAUjCDtMvWrbtdAnA53a b/integration_tests/tests/data/accounts/show_zero_balance_filter_being_enabled/CyqarC6hyNYvb3EDueyeYrnGeAUjCDtMvWrbtdAnA53a new file mode 100644 index 000000000..6f5616699 Binary files /dev/null and b/integration_tests/tests/data/accounts/show_zero_balance_filter_being_enabled/CyqarC6hyNYvb3EDueyeYrnGeAUjCDtMvWrbtdAnA53a differ diff --git a/integration_tests/tests/data/accounts/token_extension_with_no_metadata/wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 b/integration_tests/tests/data/accounts/token_extension_with_no_metadata/wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 new file mode 100644 index 000000000..a05a88603 Binary files /dev/null and b/integration_tests/tests/data/accounts/token_extension_with_no_metadata/wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 differ diff --git a/integration_tests/tests/data/accounts/token_extensions_get_asset/BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y b/integration_tests/tests/data/accounts/token_extensions_get_asset/BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y new file mode 100644 index 000000000..357411c17 Binary files /dev/null and b/integration_tests/tests/data/accounts/token_extensions_get_asset/BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y differ diff --git a/integration_tests/tests/data/accounts/token_extensions_get_asset_scenario1/BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y b/integration_tests/tests/data/accounts/token_extensions_get_asset_scenario1/BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y new file mode 100644 index 000000000..c864c0089 Binary files /dev/null and b/integration_tests/tests/data/accounts/token_extensions_get_asset_scenario1/BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y differ diff --git a/integration_tests/tests/data/accounts/token_extensions_get_asset_scenario2/HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM b/integration_tests/tests/data/accounts/token_extensions_get_asset_scenario2/HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM new file mode 100644 index 000000000..a4c739eb4 Binary files /dev/null and b/integration_tests/tests/data/accounts/token_extensions_get_asset_scenario2/HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM differ diff --git a/integration_tests/tests/data/accounts/token_extensions_get_asset_scenario_1/BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y b/integration_tests/tests/data/accounts/token_extensions_get_asset_scenario_1/BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y new file mode 100644 index 000000000..805898376 Binary files /dev/null and b/integration_tests/tests/data/accounts/token_extensions_get_asset_scenario_1/BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y differ diff --git a/integration_tests/tests/data/accounts/token_extensions_get_asset_scenario_2/HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM b/integration_tests/tests/data/accounts/token_extensions_get_asset_scenario_2/HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM new file mode 100644 index 000000000..951ce886c Binary files /dev/null and b/integration_tests/tests/data/accounts/token_extensions_get_asset_scenario_2/HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM differ diff --git a/integration_tests/tests/data/accounts/token_extensions_get_asset_scenario_3/2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo b/integration_tests/tests/data/accounts/token_extensions_get_asset_scenario_3/2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo new file mode 100644 index 000000000..2f1cdbc62 Binary files /dev/null and b/integration_tests/tests/data/accounts/token_extensions_get_asset_scenario_3/2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo differ diff --git a/integration_tests/tests/data/accounts/token_keg_fungible_with_no_metadata/wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 b/integration_tests/tests/data/accounts/token_keg_fungible_with_no_metadata/wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 new file mode 100644 index 000000000..3a695164b Binary files /dev/null and b/integration_tests/tests/data/accounts/token_keg_fungible_with_no_metadata/wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 differ diff --git a/integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs b/integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs new file mode 100644 index 000000000..b320cd479 --- /dev/null +++ b/integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs @@ -0,0 +1,161 @@ +use super::common::*; +use das_api::{ + api::{self, ApiContract}, + error::DasApiError, +}; +use function_name::named; +use itertools::Itertools; +use serial_test::serial; + +#[tokio::test] +#[serial] +#[named] +async fn test_token_extensions_get_asset_scenario_1() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Devnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts(["BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y"]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "id": "BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y" + } + "#; + + let request: api::GetAsset = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_asset(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} + +#[tokio::test] +#[serial] +#[named] +async fn test_token_extensions_get_asset_scenario_2() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts(["HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM"]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "id": "HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM" + } + "#; + + let request: api::GetAsset = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_asset(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} + +#[tokio::test] +#[serial] +#[named] +async fn test_token_extensions_get_asset_scenario_3() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts(["2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo"]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "id": "2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo" + } + "#; + + let request: api::GetAsset = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_asset(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} + +#[tokio::test] +#[serial] +#[named] +async fn test_fungible_token_get_asset_scenario_1() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + "5x38Kp4hvdomTCnCrAny4UtMUt5rQBdB6px2K1Ui45Wq", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "id": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" + } + "#; + + let request: api::GetAsset = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_asset(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} + +#[tokio::test] +#[serial] +#[named] +async fn test_token_keg_fungible_with_no_metadata() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Devnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts(["wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1"]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "id": "wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1" + } + "#; + + let request: api::GetAsset = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_asset(request).await; + + assert!(matches!(response, Err(DasApiError::DatabaseError(_)))); +} diff --git a/integration_tests/tests/integration_tests/main.rs b/integration_tests/tests/integration_tests/main.rs index 7220ca96c..2e66f7e9e 100644 --- a/integration_tests/tests/integration_tests/main.rs +++ b/integration_tests/tests/integration_tests/main.rs @@ -1,6 +1,14 @@ mod account_update_tests; mod cnft_tests; mod common; +mod fungibles_and_token_extensions_tests; mod general_scenario_tests; mod mpl_core_tests; +mod nft_editions_tests; mod regular_nft_tests; +mod show_collection_metadata_option_tests; +mod show_fungible_flag_tests; +mod show_inscription_flag_tests; +mod test_get_assets_with_multiple_same_ids; +mod test_show_zero_balance_filter; +mod token_accounts_tests; diff --git a/integration_tests/tests/integration_tests/nft_editions_tests.rs b/integration_tests/tests/integration_tests/nft_editions_tests.rs new file mode 100644 index 000000000..61929f2f5 --- /dev/null +++ b/integration_tests/tests/integration_tests/nft_editions_tests.rs @@ -0,0 +1,50 @@ +use function_name::named; + +use das_api::api::{self, ApiContract}; + +use itertools::Itertools; + +use serial_test::serial; + +use super::common::*; + +#[tokio::test] +#[serial] +#[named] +async fn test_get_nft_editions() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "Ey2Qb8kLctbchQsMnhZs5DjY32To2QtPuXNwWvk4NosL", + "9ZmY7qCaq7WbrR7RZdHWCNS9FrFRPwRqU84wzWfmqLDz", + "8SHfqzJYABeGfiG1apwiEYt6TvfGQiL1pdwEjvTKsyiZ", + "GJvFDcBWf6aDncd1TBzx2ou1rgLFYaMBdbYLBa9oTAEw", + "9ZmY7qCaq7WbrR7RZdHWCNS9FrFRPwRqU84wzWfmqLDz", + "AoxgzXKEsJmUyF5pBb3djn9cJFA26zh2SQHvd9EYijZV", + "9yQecKKYSHxez7fFjJkUvkz42TLmkoXzhyZxEf2pw8pz", + "4V9QuYLpiMu4ZQmhdEHmgATdgiHkDeJfvZi84BfkYcez", + "giWoA4jqHFkodPJgtbRYRcYtiXbsVytnxnEao3QT2gg", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "mintAddress": "Ey2Qb8kLctbchQsMnhZs5DjY32To2QtPuXNwWvk4NosL", + "limit":10 + } + "#; + + let request: api::GetNftEditions = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_nft_editions(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} diff --git a/integration_tests/tests/integration_tests/show_collection_metadata_option_tests.rs b/integration_tests/tests/integration_tests/show_collection_metadata_option_tests.rs new file mode 100644 index 000000000..688448f51 --- /dev/null +++ b/integration_tests/tests/integration_tests/show_collection_metadata_option_tests.rs @@ -0,0 +1,50 @@ +use function_name::named; + +use das_api::api::{self, ApiContract}; + +use itertools::Itertools; + +use serial_test::serial; + +use super::common::*; + +#[tokio::test] +#[serial] +#[named] + +async fn test_get_asset_with_show_collection_metadata_option() { + let name = trim_test_name(function_name!()); + + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK", + "8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "id": "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "displayOptions" : { + "showCollectionMetadata": true + } + } + "#; + + let request: api::GetAsset = serde_json::from_str(request).unwrap(); + + let response = setup.das_api.get_asset(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} diff --git a/integration_tests/tests/integration_tests/show_fungible_flag_tests.rs b/integration_tests/tests/integration_tests/show_fungible_flag_tests.rs new file mode 100644 index 000000000..3a7626106 --- /dev/null +++ b/integration_tests/tests/integration_tests/show_fungible_flag_tests.rs @@ -0,0 +1,269 @@ +use function_name::named; + +use das_api::api::{self, ApiContract}; + +use itertools::Itertools; + +use serial_test::serial; + +use super::common::*; + +#[tokio::test] +#[serial] +#[named] +async fn test_get_asset_with_show_fungible_scenario_1() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "Ca84nWhQu41DMRnjdhRrLZty1i9txepMhAhz5qLLGcBw", + "7z6b5TE4WX4mgcQjuNBTDxK4SE75sbgEg5WWJwoUeie8", + "8myaCN6KcKVkMqroXuLJq6QsqRcPbvme4wV5Ubfr5mDC", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "id": "Ca84nWhQu41DMRnjdhRrLZty1i9txepMhAhz5qLLGcBw", + "displayOptions": { + "showFungible": true + } + + } + "#; + + let request: api::GetAsset = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_asset(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} + +#[tokio::test] +#[serial] +#[named] +async fn test_get_asset_with_show_fungible_scenario_2() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK", + "8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "id": "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "displayOptions": { + "showFungible": true + } + } + "#; + + let request: api::GetAsset = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_asset(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} + +#[tokio::test] +#[serial] +#[named] +async fn test_get_asset_with_show_fungible_scenario_3() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts(["2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo"]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "id": "2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo", + "displayOptions": { + "showFungible": true + } + + } + "#; + + let request: api::GetAsset = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_asset(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} + +#[tokio::test] +#[serial] +#[named] +async fn test_get_asset_with_show_fungible_scenario_4() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn", + "8yn5oqFMwYA8SgGqWwKq1Hia8aM5gh1DWmHEL34hMqBX", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "id": "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn", + "displayOptions": { + "showFungible": true + } + } + "#; + + let request: api::GetAsset = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_asset(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} + +#[tokio::test] +#[serial] +#[named] +async fn test_get_asset_by_owner_with_show_fungible() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK", + "8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "ownerAddress": "GqPnSDXwp4JFtKS7YZ2HERgBbYLKpKVYy9TpVunzLRa9", + "displayOptions": { + "showFungible": true + } + } + "#; + + let request: api::GetAssetsByOwner = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_assets_by_owner(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} + +#[tokio::test] +#[serial] +#[named] +async fn test_get_asset_by_authority_with_show_fungible() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK", + "8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "authorityAddress": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "displayOptions": { + "showFungible": true + } + } + "#; + + let request: api::GetAssetsByAuthority = serde_json::from_str(request).unwrap(); + let response = setup + .das_api + .get_assets_by_authority(request) + .await + .unwrap(); + + insta::assert_json_snapshot!(name, response); +} + +#[tokio::test] +#[serial] +#[named] +async fn test_get_asset_by_creator_with_show_fungible() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK", + "8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "creatorAddress": "5XvhfmRjwXkGp3jHGmaKpqeerNYjkuZZBYLVQYdeVcRv", + "displayOptions": { + "showFungible": true + } + } + "#; + + let request: api::GetAssetsByCreator = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_assets_by_creator(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} diff --git a/integration_tests/tests/integration_tests/show_inscription_flag_tests.rs b/integration_tests/tests/integration_tests/show_inscription_flag_tests.rs new file mode 100644 index 000000000..7c5dffdff --- /dev/null +++ b/integration_tests/tests/integration_tests/show_inscription_flag_tests.rs @@ -0,0 +1,46 @@ +use function_name::named; + +use das_api::api::{self, ApiContract}; + +use itertools::Itertools; + +use serial_test::serial; + +use super::common::*; + +#[tokio::test] +#[serial] +#[named] +async fn test_get_asset_with_show_inscription_scenario_1() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "9FkS3kZV4MoGps14tUSp7iVnizGbxcK4bDEhSoF5oYAZ", + "HMixBLSkuhiGgVbcGhqJar476xzu1bC8wM7yHsc1iXwP", + "DarH4z6SmdVzPrt8krAygpLodhdjvNAstP3taj2tysN2", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "id": "9FkS3kZV4MoGps14tUSp7iVnizGbxcK4bDEhSoF5oYAZ", + "displayOptions": { + "showInscription": true + } + } + "#; + + let request: api::GetAsset = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_asset(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-metadata-updated.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-metadata-updated.snap index fe306fab3..7ede46288 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-metadata-updated.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-metadata-updated.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/account_update_tests.rs expression: response_new_slot +snapshot_kind: text --- { "interface": "ProgrammableNFT", @@ -62,7 +63,6 @@ expression: response_new_slot "ownership_model": "single", "owner": "BzbdvwEkQKeghTY53aZxTYjUienhdbkNVkgrLV6cErke" }, - "supply": null, "mutable": false, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-token-updated.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-token-updated.snap index 5dac7346d..90985a04a 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-token-updated.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-token-updated.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/account_update_tests.rs expression: response_new_slot +snapshot_kind: text --- { "interface": "ProgrammableNFT", @@ -62,7 +63,6 @@ expression: response_new_slot "ownership_model": "single", "owner": "1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM" }, - "supply": null, "mutable": true, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-with-all-updates.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-with-all-updates.snap index d0978550e..4a314f738 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-with-all-updates.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-with-all-updates.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/account_update_tests.rs expression: setup.das_api.get_asset(request.clone()).await.unwrap() +snapshot_kind: text --- { "interface": "ProgrammableNFT", @@ -62,7 +63,6 @@ expression: setup.das_api.get_asset(request.clone()).await.unwrap() "ownership_model": "single", "owner": "1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM" }, - "supply": null, "mutable": false, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates.snap index 2deeb3b43..e37b12286 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/account_update_tests.rs expression: response +snapshot_kind: text --- { "interface": "ProgrammableNFT", @@ -62,7 +63,6 @@ expression: response "ownership_model": "single", "owner": "BzbdvwEkQKeghTY53aZxTYjUienhdbkNVkgrLV6cErke" }, - "supply": null, "mutable": true, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__fungible_token_get_asset_scenario_1.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__fungible_token_get_asset_scenario_1.snap new file mode 100644 index 000000000..8da886066 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__fungible_token_get_asset_scenario_1.snap @@ -0,0 +1,55 @@ +--- +source: integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs +expression: response +--- +{ + "interface": "Custom", + "id": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "", + "files": [], + "metadata": { + "name": "USD Coin", + "symbol": "USDC" + }, + "links": {} + }, + "authorities": [ + { + "address": "2wmVCSfPxGPjrnMMn7rchp4uaeoTqN39mXFC2zhPdri9", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.0, + "basis_points": 0, + "primary_sale_happened": false, + "locked": false + }, + "creators": [], + "ownership": { + "frozen": false, + "delegated": false, + "delegate": null, + "ownership_model": "token", + "owner": "" + }, + "mutable": true, + "burnt": false +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__fungible_token_get_asset_scenario_2.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__fungible_token_get_asset_scenario_2.snap new file mode 100644 index 000000000..2357ddf57 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__fungible_token_get_asset_scenario_2.snap @@ -0,0 +1,39 @@ +--- +source: integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs +expression: response +snapshot_kind: text +--- +{ + "interface": "FungibleToken", + "id": "wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1", + "authorities": [], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.0, + "basis_points": 0, + "primary_sale_happened": false, + "locked": false + }, + "creators": [], + "ownership": { + "frozen": false, + "delegated": false, + "delegate": null, + "ownership_model": "token", + "owner": "" + }, + "mutable": false, + "burnt": false +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_1.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_1.snap new file mode 100644 index 000000000..52b0da861 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_1.snap @@ -0,0 +1,61 @@ +--- +source: integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs +expression: response +--- +{ + "interface": "FungibleToken", + "id": "BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://acme.com/demo.json", + "files": [], + "metadata": { + "name": "DAS Dev", + "symbol": "DAS" + }, + "links": {} + }, + "authorities": [], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.0, + "basis_points": 0, + "primary_sale_happened": false, + "locked": false + }, + "creators": [], + "ownership": { + "frozen": false, + "delegated": false, + "delegate": null, + "ownership_model": "token", + "owner": "" + }, + "mutable": true, + "burnt": false, + "mint_extensions": { + "metadata": { + "uri": "https://acme.com/demo.json", + "mint": "BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y", + "name": "DAS Dev", + "symbol": "DAS", + "update_authority": "Em34oqDQYQZ9b6ycPHD28K47mttrRsdNu1S1pgK6NtPL", + "additional_metadata": [] + }, + "metadata_pointer": { + "metadata_address": "BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y" + } + } +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_1.snap.new b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_1.snap.new new file mode 100644 index 000000000..ecc62cc90 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_1.snap.new @@ -0,0 +1,62 @@ +--- +source: integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs +assertion_line: 37 +expression: response +--- +{ + "interface": "Custom", + "id": "BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://acme.com/demo.json", + "files": [], + "metadata": { + "name": "DAS Dev", + "symbol": "DAS" + }, + "links": {} + }, + "authorities": [], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.0, + "basis_points": 0, + "primary_sale_happened": false, + "locked": false + }, + "creators": [], + "ownership": { + "frozen": false, + "delegated": false, + "delegate": null, + "ownership_model": "single", + "owner": "" + }, + "mutable": true, + "burnt": false, + "mint_extensions": { + "metadata": { + "uri": "https://acme.com/demo.json", + "mint": "BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y", + "name": "DAS Dev", + "symbol": "DAS", + "update_authority": "Em34oqDQYQZ9b6ycPHD28K47mttrRsdNu1S1pgK6NtPL", + "additional_metadata": [] + }, + "metadata_pointer": { + "metadata_address": "BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y" + } + } +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_2.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_2.snap new file mode 100644 index 000000000..31d33d4d2 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_2.snap @@ -0,0 +1,76 @@ +--- +source: integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs +expression: response +--- +{ + "interface": "FungibleToken", + "id": "HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://token-metadata.paxos.com/usdp_metadata/prod/solana/usdp_metadata.json", + "files": [], + "metadata": { + "name": "Pax Dollar", + "symbol": "USDP" + }, + "links": {} + }, + "authorities": [], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.0, + "basis_points": 0, + "primary_sale_happened": false, + "locked": false + }, + "creators": [], + "ownership": { + "frozen": false, + "delegated": false, + "delegate": null, + "ownership_model": "token", + "owner": "" + }, + "mutable": true, + "burnt": false, + "mint_extensions": { + "metadata": { + "uri": "https://token-metadata.paxos.com/usdp_metadata/prod/solana/usdp_metadata.json", + "mint": "HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM", + "name": "Pax Dollar", + "symbol": "USDP", + "update_authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "additional_metadata": [] + }, + "transfer_hook": { + "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "program_id": null + }, + "metadata_pointer": { + "metadata_address": "HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM" + }, + "permanent_delegate": { + "delegate": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk" + }, + "mint_close_authority": { + "close_authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk" + }, + "confidential_transfer_mint": { + "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "auditor_elgamal_pubkey": null, + "auto_approve_new_accounts": false + } + } +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_3.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_3.snap new file mode 100644 index 000000000..75c59b24b --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_3.snap @@ -0,0 +1,195 @@ +--- +source: integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs +expression: response +--- +{ + "interface": "FungibleToken", + "id": "2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://token-metadata.paxos.com/pyusd_metadata/prod/solana/pyusd_metadata.json", + "files": [], + "metadata": { + "name": "PayPal USD", + "symbol": "PYUSD" + }, + "links": {} + }, + "authorities": [], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.0, + "basis_points": 0, + "primary_sale_happened": false, + "locked": false + }, + "creators": [], + "ownership": { + "frozen": false, + "delegated": false, + "delegate": null, + "ownership_model": "token", + "owner": "" + }, + "mutable": true, + "burnt": false, + "mint_extensions": { + "metadata": { + "uri": "https://token-metadata.paxos.com/pyusd_metadata/prod/solana/pyusd_metadata.json", + "mint": "2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo", + "name": "PayPal USD", + "symbol": "PYUSD", + "update_authority": "9nEfZqzTP3dfVWmzQy54TzsZqSQqDFVW4PhXdG9vYCVD", + "additional_metadata": [] + }, + "transfer_hook": { + "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "program_id": null + }, + "metadata_pointer": { + "metadata_address": "2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo" + }, + "permanent_delegate": { + "delegate": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk" + }, + "transfer_fee_config": { + "withheld_amount": 0, + "newer_transfer_fee": { + "epoch": 605, + "maximum_fee": 0, + "transfer_fee_basis_points": 0 + }, + "older_transfer_fee": { + "epoch": 605, + "maximum_fee": 0, + "transfer_fee_basis_points": 0 + }, + "withdraw_withheld_authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "transfer_fee_config_authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk" + }, + "mint_close_authority": { + "close_authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk" + }, + "confidential_transfer_mint": { + "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "auditor_elgamal_pubkey": null, + "auto_approve_new_accounts": false + }, + "confidential_transfer_fee_config": { + "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "withheld_amount": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "harvest_to_mint_enabled": true, + "withdraw_withheld_authority_elgamal_pubkey": [ + 28, + 55, + 230, + 67, + 59, + 115, + 4, + 221, + 130, + 115, + 122, + 228, + 13, + 155, + 139, + 243, + 196, + 159, + 91, + 14, + 108, + 73, + 168, + 213, + 51, + 40, + 179, + 229, + 6, + 144, + 28, + 87 + ] + } + } +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__general_scenario_tests__asset_parsing.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__general_scenario_tests__asset_parsing.snap index 27d495e3a..9843eb6b3 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__general_scenario_tests__asset_parsing.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__general_scenario_tests__asset_parsing.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/general_scenario_tests.rs expression: response +snapshot_kind: text --- { "interface": "ProgrammableNFT", @@ -62,7 +63,6 @@ expression: response "ownership_model": "single", "owner": "BzbdvwEkQKeghTY53aZxTYjUienhdbkNVkgrLV6cErke" }, - "supply": null, "mutable": true, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__general_scenario_tests__creators_reordering.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__general_scenario_tests__creators_reordering.snap index 2ed4bf732..7b50b3a93 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__general_scenario_tests__creators_reordering.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__general_scenario_tests__creators_reordering.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/general_scenario_tests.rs expression: response +snapshot_kind: text --- { "interface": "ProgrammableNFT", @@ -77,7 +78,6 @@ expression: response "ownership_model": "single", "owner": "AZgXpkRSetUJHy6C3NBvG6jNe49MpgrkZ2RkdMkjCjkW" }, - "supply": null, "mutable": true, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_autograph_plugin.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_autograph_plugin.snap index 1a8cfd344..552549a56 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_autograph_plugin.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_autograph_plugin.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 487 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": { diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_autograph_plugin_with_signature.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_autograph_plugin_with_signature.snap index 4277369f1..2753ea8e2 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_autograph_plugin_with_signature.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_autograph_plugin_with_signature.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 516 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "ACxrDWeCPic6voU6a8vyadpL8nSW15Un5vT76LDpxD4N" }, - "supply": null, "mutable": true, "burnt": false, "plugins": { diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset.snap index 0b9a6039f..a72d1bfc8 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 37 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -62,7 +62,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": { diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_binary_data_and_owner_is_data_authority.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_binary_data_and_owner_is_data_authority.snap index b0d613582..e48244c90 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_binary_data_and_owner_is_data_authority.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_binary_data_and_owner_is_data_authority.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 603 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "HKwwF4sPVYFPgqEgxth4GZRZjJ9o4E3wA8eu2QM5Vt3H" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_json_data_and_update_authority_is_data_authority.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_json_data_and_update_authority_is_data_authority.snap index ba7f6bca3..d7f66a390 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_json_data_and_update_authority_is_data_authority.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_json_data_and_update_authority_is_data_authority.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 633 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "HKwwF4sPVYFPgqEgxth4GZRZjJ9o4E3wA8eu2QM5Vt3H" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_msg_pack_data_and_address_is_data_authority.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_msg_pack_data_and_address_is_data_authority.snap index 2031b1797..38619bc7f 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_msg_pack_data_and_address_is_data_authority.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_msg_pack_data_and_address_is_data_authority.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 662 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "HKwwF4sPVYFPgqEgxth4GZRZjJ9o4E3wA8eu2QM5Vt3H" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_binary_data.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_binary_data.snap index 85e72340b..3fe19c33d 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_binary_data.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_binary_data.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 721 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -56,7 +56,6 @@ expression: response "ownership_model": "single", "owner": "HKwwF4sPVYFPgqEgxth4GZRZjJ9o4E3wA8eu2QM5Vt3H" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_json_data.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_json_data.snap index 9524d637f..410235155 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_json_data.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_json_data.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 780 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -56,7 +56,6 @@ expression: response "ownership_model": "single", "owner": "HKwwF4sPVYFPgqEgxth4GZRZjJ9o4E3wA8eu2QM5Vt3H" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_msg_pack_data.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_msg_pack_data.snap index 840e75768..0294d65bf 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_msg_pack_data.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_msg_pack_data.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 839 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -56,7 +56,6 @@ expression: response "ownership_model": "single", "owner": "HKwwF4sPVYFPgqEgxth4GZRZjJ9o4E3wA8eu2QM5Vt3H" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_edition.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_edition.snap index 6333a1260..f86780213 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_edition.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_edition.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 216 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": { diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_multiple_internal_and_external_plugins.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_multiple_internal_and_external_plugins.snap index 60e77e446..a941b9087 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_multiple_internal_and_external_plugins.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_multiple_internal_and_external_plugins.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 458 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -57,7 +57,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": { diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_custom_offset_and_base_address_config.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_custom_offset_and_base_address_config.snap index ac19d3d3a..08ad3c6dd 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_custom_offset_and_base_address_config.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_custom_offset_and_base_address_config.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 361 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_external_plugin_on_collection.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_external_plugin_on_collection.snap index 40802d2a6..520d97d9b 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_external_plugin_on_collection.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_external_plugin_on_collection.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 274 expression: response +snapshot_kind: text --- { "interface": "MplCoreCollection", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_multiple_lifecycle_events.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_multiple_lifecycle_events.snap index f42fdf792..9bf617915 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_multiple_lifecycle_events.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_multiple_lifecycle_events.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 332 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_no_offset.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_no_offset.snap index 51d44c25c..fcbfab1d2 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_no_offset.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_no_offset.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 390 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_pubkey_in_rule_set.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_pubkey_in_rule_set.snap index bfbe6538e..05acfc8ac 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_pubkey_in_rule_set.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_pubkey_in_rule_set.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 245 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -62,7 +62,6 @@ expression: response "ownership_model": "single", "owner": "8LsUNkb7bLaAcZ7NjRKPuvcyRGTWbm9BxzUpVKjqdV8o" }, - "supply": null, "mutable": true, "burnt": false, "plugins": { diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_two_oracle_external_plugins.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_two_oracle_external_plugins.snap index b9d26a436..1da1d1281 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_two_oracle_external_plugins.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_two_oracle_external_plugins.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 274 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_authority.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_authority.snap index 5acbdeb5d..53017c8fe 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_authority.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_authority.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 108 expression: response +snapshot_kind: text --- { "total": 2, @@ -61,7 +61,6 @@ expression: response "ownership_model": "single", "owner": "GzYvuu9aUYXmnardj4svbAcCNmefiaGu2E3knGw9NJQQ" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, @@ -118,7 +117,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_group.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_group.snap index 431a64ec4..f338c6fb7 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_group.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_group.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 149 expression: response +snapshot_kind: text --- { "total": 3, @@ -61,7 +61,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, @@ -123,7 +122,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, @@ -185,7 +183,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_group_with_oracle_and_custom_pda_all_seeds.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_group_with_oracle_and_custom_pda_all_seeds.snap index f9cea5683..a4236f53d 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_group_with_oracle_and_custom_pda_all_seeds.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_group_with_oracle_and_custom_pda_all_seeds.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 429 expression: response +snapshot_kind: text --- { "total": 1, @@ -61,7 +61,6 @@ expression: response "ownership_model": "single", "owner": "FAe4nM85BQ8b1nWEc5TTeogQGnNDWsuGYU84vuiPc7kE" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_owner.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_owner.snap index 7b190b8a9..946d8a9af 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_owner.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_owner.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 187 expression: response +snapshot_kind: text --- { "total": 1, @@ -61,7 +61,6 @@ expression: response "ownership_model": "single", "owner": "7uScVQiT4vArB88dHrZoeVKWbtsRJmNp9r5Gce5VQpXS" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection.snap index 1d854ee47..14a95dbb2 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 66 expression: response +snapshot_kind: text --- { "interface": "MplCoreCollection", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": { diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_binary_data_and_address_is_data_authority.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_binary_data_and_address_is_data_authority.snap index 38d0e3771..4e3b7c0c2 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_binary_data_and_address_is_data_authority.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_binary_data_and_address_is_data_authority.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 692 expression: response +snapshot_kind: text --- { "interface": "MplCoreCollection", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_json_data_and_owner_is_data_authority.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_json_data_and_owner_is_data_authority.snap index b7ef57f77..2ed4afdc1 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_json_data_and_owner_is_data_authority.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_json_data_and_owner_is_data_authority.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 751 expression: response +snapshot_kind: text --- { "interface": "MplCoreCollection", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_msg_pack_data_and_update_authority_is_data_authority.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_msg_pack_data_and_update_authority_is_data_authority.snap index ff0cfbdbf..526a7a545 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_msg_pack_data_and_update_authority_is_data_authority.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_msg_pack_data_and_update_authority_is_data_authority.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 810 expression: response +snapshot_kind: text --- { "interface": "MplCoreCollection", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_verified_creators_plugin.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_verified_creators_plugin.snap index 6387d404c..cbd495062 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_verified_creators_plugin.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_verified_creators_plugin.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 545 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": { diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_verified_creators_plugin_with_signature.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_verified_creators_plugin_with_signature.snap index 96428d6a1..36b1cd541 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_verified_creators_plugin_with_signature.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_verified_creators_plugin_with_signature.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 574 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "D9SSaw4tz5AGpfWynYJ66jDCVNTsbLBkqT8rxQFenqj4" }, - "supply": null, "mutable": true, "burnt": false, "plugins": { diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__nft_editions_tests__get_nft_editions.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__nft_editions_tests__get_nft_editions.snap new file mode 100644 index 000000000..d67779b90 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__nft_editions_tests__get_nft_editions.snap @@ -0,0 +1,24 @@ +--- +source: integration_tests/tests/integration_tests/nft_editions_tests.rs +expression: response +snapshot_kind: text +--- +{ + "total": 2, + "limit": 10, + "master_edition_address": "8SHfqzJYABeGfiG1apwiEYt6TvfGQiL1pdwEjvTKsyiZ", + "supply": 60, + "max_supply": 69, + "editions": [ + { + "mint_address": "GJvFDcBWf6aDncd1TBzx2ou1rgLFYaMBdbYLBa9oTAEw", + "edition_address": "AoxgzXKEsJmUyF5pBb3djn9cJFA26zh2SQHvd9EYijZV", + "edition_number": 1 + }, + { + "mint_address": "9yQecKKYSHxez7fFjJkUvkz42TLmkoXzhyZxEf2pw8pz", + "edition_address": "giWoA4jqHFkodPJgtbRYRcYtiXbsVytnxnEao3QT2gg", + "edition_number": 2 + } + ] +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset.snap index c62b3de1c..befc56b19 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/regular_nft_tests.rs expression: response +snapshot_kind: text --- { "interface": "ProgrammableNFT", @@ -67,7 +68,6 @@ expression: response "ownership_model": "single", "owner": "A59E2tNJEqNN9TDnzgGnmLmnTsdRDoPocGx3n1w2dqZw" }, - "supply": null, "mutable": true, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-2-and-a-missing-1.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-2-and-a-missing-1.snap index 0825ec1b2..2f859cbb6 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-2-and-a-missing-1.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-2-and-a-missing-1.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/regular_nft_tests.rs expression: response +snapshot_kind: text --- [ { @@ -68,7 +69,6 @@ expression: response "ownership_model": "single", "owner": "9PacVenjPyQYiWBha89UYRM1nn6mf9bGY7vi32zY6DLn" }, - "supply": null, "mutable": true, "burnt": false }, @@ -138,7 +138,6 @@ expression: response "ownership_model": "single", "owner": "3H3d3hfpZVVdVwuFAxDtDSFN2AdR7kwiDA3ynbnbkhc9" }, - "supply": null, "mutable": true, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-only-2-different-2.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-only-2-different-2.snap index 36c9961f9..565f843d4 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-only-2-different-2.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-only-2-different-2.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/regular_nft_tests.rs expression: response +snapshot_kind: text --- [ { @@ -68,7 +69,6 @@ expression: response "ownership_model": "single", "owner": "9PacVenjPyQYiWBha89UYRM1nn6mf9bGY7vi32zY6DLn" }, - "supply": null, "mutable": true, "burnt": false }, @@ -137,7 +137,6 @@ expression: response "ownership_model": "single", "owner": "3H3d3hfpZVVdVwuFAxDtDSFN2AdR7kwiDA3ynbnbkhc9" }, - "supply": null, "mutable": true, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-only-2.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-only-2.snap index f07052af1..7647ef26c 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-only-2.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-only-2.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/regular_nft_tests.rs expression: response +snapshot_kind: text --- [ { @@ -68,7 +69,6 @@ expression: response "ownership_model": "single", "owner": "BaBQKh34KrqZzd4ifSHQYMf86HiBGASN6TWUi1ZwfyKv" }, - "supply": null, "mutable": true, "burnt": false }, @@ -137,7 +137,6 @@ expression: response "ownership_model": "single", "owner": "9PacVenjPyQYiWBha89UYRM1nn6mf9bGY7vi32zY6DLn" }, - "supply": null, "mutable": true, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-only-2.snap.new b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-only-2.snap.new new file mode 100644 index 000000000..a2ac23d28 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-only-2.snap.new @@ -0,0 +1,143 @@ +--- +source: integration_tests/tests/integration_tests/regular_nft_tests.rs +assertion_line: 82 +expression: response +--- +[ + { + "interface": "ProgrammableNFT", + "id": "2NqdYX6kJmMUoChnDXU2UrP9BsoPZivRw3uJG8iDhRRd", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://madlads.s3.us-west-2.amazonaws.com/json/9959.json", + "files": [], + "metadata": { + "name": "Mad Lads #9959", + "symbol": "MAD", + "token_standard": "ProgrammableNonFungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [ + { + "group_key": "collection", + "group_value": "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w" + } + ], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.042, + "basis_points": 420, + "primary_sale_happened": true, + "locked": false + }, + "creators": [ + { + "address": "5XvhfmRjwXkGp3jHGmaKpqeerNYjkuZZBYLVQYdeVcRv", + "share": 0, + "verified": true + }, + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "share": 100, + "verified": true + } + ], + "ownership": { + "frozen": true, + "delegated": true, + "delegate": "CimD28VvDSHcGdErmLbbPimnkfwrKHvstNiQUutZDxWS", + "ownership_model": "single", + "owner": "9PacVenjPyQYiWBha89UYRM1nn6mf9bGY7vi32zY6DLn" + }, + "mutable": true, + "burnt": false + }, + { + "interface": "ProgrammableNFT", + "id": "HTKAVZZrDdyecCxzm3WEkCsG1GUmiqKm73PvngfuYRNK", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://madlads.s3.us-west-2.amazonaws.com/json/9965.json", + "files": [], + "metadata": { + "name": "Mad Lads #9965", + "symbol": "MAD", + "token_standard": "ProgrammableNonFungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [ + { + "group_key": "collection", + "group_value": "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w" + } + ], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.042, + "basis_points": 420, + "primary_sale_happened": true, + "locked": false + }, + "creators": [ + { + "address": "5XvhfmRjwXkGp3jHGmaKpqeerNYjkuZZBYLVQYdeVcRv", + "share": 0, + "verified": true + }, + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "share": 100, + "verified": true + } + ], + "ownership": { + "frozen": true, + "delegated": true, + "delegate": "Hys4KoEw32y4sLHb96K2PqYT5jUHqUWdh6qS9EACEB4Y", + "ownership_model": "single", + "owner": "BaBQKh34KrqZzd4ifSHQYMf86HiBGASN6TWUi1ZwfyKv" + }, + "mutable": true, + "burnt": false + } +] diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_by_group.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_by_group.snap index 87920f33b..ce44d4bc4 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_by_group.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_by_group.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/regular_nft_tests.rs expression: response +snapshot_kind: text --- { "total": 1, @@ -77,7 +78,6 @@ expression: response "ownership_model": "single", "owner": "9qUcfdADyrrTSetFjNjF9Ro7LKAqzJkzZV6WKLHfv5MU" }, - "supply": null, "mutable": true, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_search_assets.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_search_assets.snap index bd321f698..415fdbb43 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_search_assets.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_search_assets.snap @@ -72,7 +72,6 @@ expression: response "ownership_model": "single", "owner": "6Cr66AabRYymhZgYQSfTCo6FVpH18wXrMZswAbcErpyX" }, - "supply": null, "mutable": true, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__show_collection_metadata_option_tests__get_asset_with_show_collection_metadata_option.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_collection_metadata_option_tests__get_asset_with_show_collection_metadata_option.snap new file mode 100644 index 000000000..4fd990230 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_collection_metadata_option_tests__get_asset_with_show_collection_metadata_option.snap @@ -0,0 +1,73 @@ +--- +source: integration_tests/tests/integration_tests/show_collection_metadata_option_tests.rs +expression: response +--- +{ + "interface": "ProgrammableNFT", + "id": "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://madlads.s3.us-west-2.amazonaws.com/json/1983.json", + "files": [], + "metadata": { + "name": "Mad Lads #1983", + "symbol": "MAD", + "token_standard": "ProgrammableNonFungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [ + { + "group_key": "collection", + "group_value": "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w", + "collection_metadata": {} + } + ], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.042, + "basis_points": 420, + "primary_sale_happened": true, + "locked": false + }, + "creators": [ + { + "address": "5XvhfmRjwXkGp3jHGmaKpqeerNYjkuZZBYLVQYdeVcRv", + "share": 0, + "verified": true + }, + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "share": 100, + "verified": true + } + ], + "ownership": { + "frozen": false, + "delegated": false, + "delegate": null, + "ownership_model": "single", + "owner": "GqPnSDXwp4JFtKS7YZ2HERgBbYLKpKVYy9TpVunzLRa9" + }, + "mutable": true, + "burnt": false +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_authority_with_show_fungible.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_authority_with_show_fungible.snap new file mode 100644 index 000000000..21f2b10c5 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_authority_with_show_fungible.snap @@ -0,0 +1,86 @@ +--- +source: integration_tests/tests/integration_tests/show_fungible_flag_tests.rs +expression: response +--- +{ + "total": 1, + "limit": 1000, + "cursor": "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "items": [ + { + "interface": "ProgrammableNFT", + "id": "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://madlads.s3.us-west-2.amazonaws.com/json/1983.json", + "files": [], + "metadata": { + "name": "Mad Lads #1983", + "symbol": "MAD", + "token_standard": "ProgrammableNonFungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [ + { + "group_key": "collection", + "group_value": "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w" + } + ], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.042, + "basis_points": 420, + "primary_sale_happened": true, + "locked": false + }, + "creators": [ + { + "address": "5XvhfmRjwXkGp3jHGmaKpqeerNYjkuZZBYLVQYdeVcRv", + "share": 0, + "verified": true + }, + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "share": 100, + "verified": true + } + ], + "ownership": { + "frozen": true, + "delegated": true, + "delegate": "BPB5idZgbA1DG4XEmnKs62AADRZFf3jY7Kr9mpMGyKPi", + "ownership_model": "single", + "owner": "GqPnSDXwp4JFtKS7YZ2HERgBbYLKpKVYy9TpVunzLRa9" + }, + "mutable": true, + "burnt": false, + "token_info": { + "supply": 1, + "decimals": 0, + "token_program": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "mint_authority": "FAzbjKo66M3tKhkKqegmWFaYr93FB74B1ChEBdFyKcip", + "freeze_authority": "FAzbjKo66M3tKhkKqegmWFaYr93FB74B1ChEBdFyKcip" + } + } + ] +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_creator_with_show_fungible.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_creator_with_show_fungible.snap new file mode 100644 index 000000000..21f2b10c5 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_creator_with_show_fungible.snap @@ -0,0 +1,86 @@ +--- +source: integration_tests/tests/integration_tests/show_fungible_flag_tests.rs +expression: response +--- +{ + "total": 1, + "limit": 1000, + "cursor": "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "items": [ + { + "interface": "ProgrammableNFT", + "id": "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://madlads.s3.us-west-2.amazonaws.com/json/1983.json", + "files": [], + "metadata": { + "name": "Mad Lads #1983", + "symbol": "MAD", + "token_standard": "ProgrammableNonFungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [ + { + "group_key": "collection", + "group_value": "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w" + } + ], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.042, + "basis_points": 420, + "primary_sale_happened": true, + "locked": false + }, + "creators": [ + { + "address": "5XvhfmRjwXkGp3jHGmaKpqeerNYjkuZZBYLVQYdeVcRv", + "share": 0, + "verified": true + }, + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "share": 100, + "verified": true + } + ], + "ownership": { + "frozen": true, + "delegated": true, + "delegate": "BPB5idZgbA1DG4XEmnKs62AADRZFf3jY7Kr9mpMGyKPi", + "ownership_model": "single", + "owner": "GqPnSDXwp4JFtKS7YZ2HERgBbYLKpKVYy9TpVunzLRa9" + }, + "mutable": true, + "burnt": false, + "token_info": { + "supply": 1, + "decimals": 0, + "token_program": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "mint_authority": "FAzbjKo66M3tKhkKqegmWFaYr93FB74B1ChEBdFyKcip", + "freeze_authority": "FAzbjKo66M3tKhkKqegmWFaYr93FB74B1ChEBdFyKcip" + } + } + ] +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_owner_with_show_fungible.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_owner_with_show_fungible.snap new file mode 100644 index 000000000..21f2b10c5 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_owner_with_show_fungible.snap @@ -0,0 +1,86 @@ +--- +source: integration_tests/tests/integration_tests/show_fungible_flag_tests.rs +expression: response +--- +{ + "total": 1, + "limit": 1000, + "cursor": "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "items": [ + { + "interface": "ProgrammableNFT", + "id": "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://madlads.s3.us-west-2.amazonaws.com/json/1983.json", + "files": [], + "metadata": { + "name": "Mad Lads #1983", + "symbol": "MAD", + "token_standard": "ProgrammableNonFungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [ + { + "group_key": "collection", + "group_value": "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w" + } + ], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.042, + "basis_points": 420, + "primary_sale_happened": true, + "locked": false + }, + "creators": [ + { + "address": "5XvhfmRjwXkGp3jHGmaKpqeerNYjkuZZBYLVQYdeVcRv", + "share": 0, + "verified": true + }, + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "share": 100, + "verified": true + } + ], + "ownership": { + "frozen": true, + "delegated": true, + "delegate": "BPB5idZgbA1DG4XEmnKs62AADRZFf3jY7Kr9mpMGyKPi", + "ownership_model": "single", + "owner": "GqPnSDXwp4JFtKS7YZ2HERgBbYLKpKVYy9TpVunzLRa9" + }, + "mutable": true, + "burnt": false, + "token_info": { + "supply": 1, + "decimals": 0, + "token_program": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "mint_authority": "FAzbjKo66M3tKhkKqegmWFaYr93FB74B1ChEBdFyKcip", + "freeze_authority": "FAzbjKo66M3tKhkKqegmWFaYr93FB74B1ChEBdFyKcip" + } + } + ] +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_1.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_1.snap new file mode 100644 index 000000000..fc4e0a991 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_1.snap @@ -0,0 +1,79 @@ +--- +source: integration_tests/tests/integration_tests/show_fungible_flag_tests.rs +expression: response +--- +{ + "interface": "ProgrammableNFT", + "id": "Ca84nWhQu41DMRnjdhRrLZty1i9txepMhAhz5qLLGcBw", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://nftstorage.link/ipfs/bafkreibmdapcawep5fb77lvcuvoivft3w3wsnc4qworrntjbg6abc5vwti", + "files": [], + "metadata": { + "name": "Claynosaurz #1096", + "symbol": "DINO", + "token_standard": "ProgrammableNonFungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "B7B2g3WbdZMDV3YcDGRGhEt5KyWqDJZFwRR8zpWVEkUF", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [ + { + "group_key": "collection", + "group_value": "6mszaj17KSfVqADrQj3o4W3zoLMTykgmV37W4QadCczK" + } + ], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.05, + "basis_points": 500, + "primary_sale_happened": true, + "locked": false + }, + "creators": [ + { + "address": "AoebZtN5iKpVyUBc82aouWhugVknLzjUmEEUezxviYNo", + "share": 0, + "verified": true + }, + { + "address": "36tfiBtaDGjAMKd6smPacHQhe4MXycLL6f9ww9CD1naT", + "share": 100, + "verified": false + } + ], + "ownership": { + "frozen": true, + "delegated": false, + "delegate": null, + "ownership_model": "single", + "owner": "J1ep1LizHMU3Bf1GKkWePGHU3Qwwzw6FvwW5ySFWdCkn" + }, + "mutable": true, + "burnt": false, + "token_info": { + "supply": 1, + "decimals": 0, + "token_program": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "mint_authority": "EwSfdqoZPBW6JJN1SBkM2pPgpihDVbByuxKdmiXurxYF", + "freeze_authority": "EwSfdqoZPBW6JJN1SBkM2pPgpihDVbByuxKdmiXurxYF" + } +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_2.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_2.snap new file mode 100644 index 000000000..9f40a6f5c --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_2.snap @@ -0,0 +1,79 @@ +--- +source: integration_tests/tests/integration_tests/show_fungible_flag_tests.rs +expression: response +--- +{ + "interface": "ProgrammableNFT", + "id": "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://madlads.s3.us-west-2.amazonaws.com/json/1983.json", + "files": [], + "metadata": { + "name": "Mad Lads #1983", + "symbol": "MAD", + "token_standard": "ProgrammableNonFungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [ + { + "group_key": "collection", + "group_value": "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w" + } + ], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.042, + "basis_points": 420, + "primary_sale_happened": true, + "locked": false + }, + "creators": [ + { + "address": "5XvhfmRjwXkGp3jHGmaKpqeerNYjkuZZBYLVQYdeVcRv", + "share": 0, + "verified": true + }, + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "share": 100, + "verified": true + } + ], + "ownership": { + "frozen": true, + "delegated": true, + "delegate": "BPB5idZgbA1DG4XEmnKs62AADRZFf3jY7Kr9mpMGyKPi", + "ownership_model": "single", + "owner": "GqPnSDXwp4JFtKS7YZ2HERgBbYLKpKVYy9TpVunzLRa9" + }, + "mutable": true, + "burnt": false, + "token_info": { + "supply": 1, + "decimals": 0, + "token_program": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "mint_authority": "FAzbjKo66M3tKhkKqegmWFaYr93FB74B1ChEBdFyKcip", + "freeze_authority": "FAzbjKo66M3tKhkKqegmWFaYr93FB74B1ChEBdFyKcip" + } +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_3.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_3.snap new file mode 100644 index 000000000..12b41c50d --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_3.snap @@ -0,0 +1,202 @@ +--- +source: integration_tests/tests/integration_tests/show_fungible_flag_tests.rs +expression: response +--- +{ + "interface": "FungibleToken", + "id": "2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://token-metadata.paxos.com/pyusd_metadata/prod/solana/pyusd_metadata.json", + "files": [], + "metadata": { + "name": "PayPal USD", + "symbol": "PYUSD" + }, + "links": {} + }, + "authorities": [], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.0, + "basis_points": 0, + "primary_sale_happened": false, + "locked": false + }, + "creators": [], + "ownership": { + "frozen": false, + "delegated": false, + "delegate": null, + "ownership_model": "token", + "owner": "" + }, + "mutable": true, + "burnt": false, + "mint_extensions": { + "metadata": { + "uri": "https://token-metadata.paxos.com/pyusd_metadata/prod/solana/pyusd_metadata.json", + "mint": "2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo", + "name": "PayPal USD", + "symbol": "PYUSD", + "update_authority": "9nEfZqzTP3dfVWmzQy54TzsZqSQqDFVW4PhXdG9vYCVD", + "additional_metadata": [] + }, + "transfer_hook": { + "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "program_id": null + }, + "metadata_pointer": { + "metadata_address": "2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo" + }, + "permanent_delegate": { + "delegate": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk" + }, + "transfer_fee_config": { + "withheld_amount": 0, + "newer_transfer_fee": { + "epoch": 605, + "maximum_fee": 0, + "transfer_fee_basis_points": 0 + }, + "older_transfer_fee": { + "epoch": 605, + "maximum_fee": 0, + "transfer_fee_basis_points": 0 + }, + "withdraw_withheld_authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "transfer_fee_config_authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk" + }, + "mint_close_authority": { + "close_authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk" + }, + "confidential_transfer_mint": { + "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "auditor_elgamal_pubkey": null, + "auto_approve_new_accounts": false + }, + "confidential_transfer_fee_config": { + "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "withheld_amount": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "harvest_to_mint_enabled": true, + "withdraw_withheld_authority_elgamal_pubkey": [ + 28, + 55, + 230, + 67, + 59, + 115, + 4, + 221, + 130, + 115, + 122, + 228, + 13, + 155, + 139, + 243, + 196, + 159, + 91, + 14, + 108, + 73, + 168, + 213, + 51, + 40, + 179, + 229, + 6, + 144, + 28, + 87 + ] + } + }, + "token_info": { + "supply": 168620476682610, + "decimals": 6, + "token_program": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "mint_authority": "22mKJkKjGEQ3rampp5YKaSsaYZ52BUkcnUN6evXGsXzz", + "freeze_authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk" + } +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_4.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_4.snap new file mode 100644 index 000000000..f4b41d558 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_4.snap @@ -0,0 +1,62 @@ +--- +source: integration_tests/tests/integration_tests/show_fungible_flag_tests.rs +expression: response +--- +{ + "interface": "FungibleToken", + "id": "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://storage.googleapis.com/token-metadata/JitoSOL.json", + "files": [], + "metadata": { + "name": "Jito Staked SOL", + "symbol": "JitoSOL", + "token_standard": "Fungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "EDGARWktv3nDxRYjufjdbZmryqGXceaFPoPpbUzdpqED", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.0, + "basis_points": 0, + "primary_sale_happened": false, + "locked": false + }, + "creators": [], + "ownership": { + "frozen": false, + "delegated": false, + "delegate": null, + "ownership_model": "token", + "owner": "" + }, + "mutable": true, + "burnt": false, + "token_info": { + "supply": 12585937222711072, + "decimals": 9, + "token_program": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "mint_authority": "6iQKfEyhr3bZMotVkW6beNZz5CPAkiwvgV2CTje9pVSS" + } +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__show_inscription_flag_tests__get_asset_with_show_inscription_scenario_1.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_inscription_flag_tests__get_asset_with_show_inscription_scenario_1.snap new file mode 100644 index 000000000..62373341d --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_inscription_flag_tests__get_asset_with_show_inscription_scenario_1.snap @@ -0,0 +1,78 @@ +--- +source: integration_tests/tests/integration_tests/show_inscription_flag_tests.rs +expression: response +snapshot_kind: text +--- +{ + "interface": "V1_NFT", + "id": "9FkS3kZV4MoGps14tUSp7iVnizGbxcK4bDEhSoF5oYAZ", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://arweave.net/qJdjeP8XFfYIG6z5pJ-3RsZR2EcbgILX_ot-b2fEC0g", + "files": [], + "metadata": { + "name": "punk2491", + "symbol": "Symbol", + "token_standard": "NonFungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "BVk6Bvxa9v6Y32o7KGPhYV4CU9pmG2K7nAYc7mDejsGM", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.08, + "basis_points": 800, + "primary_sale_happened": false, + "locked": false + }, + "creators": [ + { + "address": "BVk6Bvxa9v6Y32o7KGPhYV4CU9pmG2K7nAYc7mDejsGM", + "share": 100, + "verified": true + } + ], + "ownership": { + "frozen": false, + "delegated": false, + "delegate": null, + "ownership_model": "single", + "owner": "" + }, + "supply": { + "print_max_supply": 0, + "print_current_supply": 0, + "edition_nonce": 252 + }, + "mutable": true, + "burnt": false, + "inscription": { + "authority": "11111111111111111111111111111111", + "root": "9FkS3kZV4MoGps14tUSp7iVnizGbxcK4bDEhSoF5oYAZ", + "inscription_data": "4Q18N6XrfJHgDbRTaHJR328jN9dixCLQAQhDsTsRzg3v", + "content": "image/net/riupjyro3lsvkb_listajh0jdsrsjmnyhusxvznycqw", + "encoding": "base64", + "order": 1733, + "size": 202, + "validation_hash": "7fa0041483b92f5a0448067ecef9beca2192b13bfe86fbd53a0024e84fcea652" + } +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__test_get_assets_with_multiple_same_ids__get_assets_with_multiple_same_ids.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__test_get_assets_with_multiple_same_ids__get_assets_with_multiple_same_ids.snap new file mode 100644 index 000000000..e856e4c02 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__test_get_assets_with_multiple_same_ids__get_assets_with_multiple_same_ids.snap @@ -0,0 +1,138 @@ +--- +source: integration_tests/tests/integration_tests/test_get_assets_with_multiple_same_ids.rs +expression: response +snapshot_kind: text +--- +[ + { + "interface": "ProgrammableNFT", + "id": "F9Lw3ki3hJ7PF9HQXsBzoY8GyE6sPoEZZdXJBsTTD2rk", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://madlads.s3.us-west-2.amazonaws.com/json/8420.json", + "files": [], + "metadata": { + "name": "Mad Lads #8420", + "symbol": "MAD", + "token_standard": "ProgrammableNonFungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [ + { + "group_key": "collection", + "group_value": "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w" + } + ], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.042, + "basis_points": 420, + "primary_sale_happened": true, + "locked": false + }, + "creators": [ + { + "address": "5XvhfmRjwXkGp3jHGmaKpqeerNYjkuZZBYLVQYdeVcRv", + "share": 0, + "verified": true + }, + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "share": 100, + "verified": true + } + ], + "ownership": { + "frozen": false, + "delegated": false, + "delegate": null, + "ownership_model": "single", + "owner": "" + }, + "mutable": true, + "burnt": false + }, + { + "interface": "V1_NFT", + "id": "JEKKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://nftstorage.link/ipfs/bafkreidgvjw2atmkw2jzkkfi56arfrzaicrebzw5xwfkz3b67fq5gbvlre", + "files": [], + "metadata": { + "name": "TURTLES", + "symbol": "TURTLES", + "token_standard": "NonFungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "J4tNLYMTegHE9nVjpRM17tf1EYwJnA9Crfn3KytRNcGv", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.0, + "basis_points": 0, + "primary_sale_happened": true, + "locked": false + }, + "creators": [ + { + "address": "J4tNLYMTegHE9nVjpRM17tf1EYwJnA9Crfn3KytRNcGv", + "share": 100, + "verified": true + } + ], + "ownership": { + "frozen": false, + "delegated": false, + "delegate": null, + "ownership_model": "single", + "owner": "" + }, + "supply": { + "print_max_supply": 0, + "print_current_supply": 0, + "edition_nonce": 253 + }, + "mutable": false, + "burnt": false + } +] diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__test_show_zero_balance_filter__show_zero_balance_filter_being_disabled.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__test_show_zero_balance_filter__show_zero_balance_filter_being_disabled.snap new file mode 100644 index 000000000..f2a5d1a10 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__test_show_zero_balance_filter__show_zero_balance_filter_being_disabled.snap @@ -0,0 +1,24 @@ +--- +source: integration_tests/tests/integration_tests/test_show_zero_balance_filter.rs +expression: response +snapshot_kind: text +--- +{ + "total": 1, + "limit": 1000, + "token_accounts": [ + { + "address": "CyqarC6hyNYvb3EDueyeYrnGeAUjCDtMvWrbtdAnA53a", + "mint": "7Y5WQ2e3FummR2DebrqP8caC64QvXzpnhSTNWjNabxWn", + "amount": 1, + "owner": "2oerfxddTpK5hWAmCMYB6fr9WvNrjEH54CHCWK8sAq7g", + "frozen": true, + "delegate": "D98f1ebFe6kfZTcztLo1iPeKAwogbWHAgXzgSpdRDiu7", + "delegated_amount": 1, + "close_authority": null, + "extensions": null + } + ], + "cursor": null, + "errors": [] +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__test_show_zero_balance_filter__show_zero_balance_filter_being_enabled.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__test_show_zero_balance_filter__show_zero_balance_filter_being_enabled.snap new file mode 100644 index 000000000..c628cdebb --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__test_show_zero_balance_filter__show_zero_balance_filter_being_enabled.snap @@ -0,0 +1,35 @@ +--- +source: integration_tests/tests/integration_tests/test_show_zero_balance_filter.rs +expression: response +snapshot_kind: text +--- +{ + "total": 2, + "limit": 1000, + "token_accounts": [ + { + "address": "BE1CkzRjLTXAWcSVCaqzycwXsZ18Yuk3jMDMnPUoHjjS", + "mint": "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN", + "amount": 0, + "owner": "2oerfxddTpK5hWAmCMYB6fr9WvNrjEH54CHCWK8sAq7g", + "frozen": false, + "delegate": null, + "delegated_amount": 0, + "close_authority": null, + "extensions": null + }, + { + "address": "CyqarC6hyNYvb3EDueyeYrnGeAUjCDtMvWrbtdAnA53a", + "mint": "7Y5WQ2e3FummR2DebrqP8caC64QvXzpnhSTNWjNabxWn", + "amount": 1, + "owner": "2oerfxddTpK5hWAmCMYB6fr9WvNrjEH54CHCWK8sAq7g", + "frozen": true, + "delegate": "D98f1ebFe6kfZTcztLo1iPeKAwogbWHAgXzgSpdRDiu7", + "delegated_amount": 1, + "close_authority": null, + "extensions": null + } + ], + "cursor": null, + "errors": [] +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__token_accounts_tests__get_token_accounts_by_mint.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__token_accounts_tests__get_token_accounts_by_mint.snap new file mode 100644 index 000000000..461483ad2 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__token_accounts_tests__get_token_accounts_by_mint.snap @@ -0,0 +1,24 @@ +--- +source: integration_tests/tests/integration_tests/token_accounts_tests.rs +expression: response +snapshot_kind: text +--- +{ + "total": 1, + "limit": 1000, + "token_accounts": [ + { + "address": "jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2", + "mint": "wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1", + "amount": 1000000000000, + "owner": "CeviT1DTQLuicEB7yLeFkkAGmam5GnJssbGb7CML4Tgx", + "frozen": false, + "delegate": null, + "delegated_amount": 0, + "close_authority": null, + "extensions": null + } + ], + "cursor": null, + "errors": [] +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__token_accounts_tests__get_token_accounts_by_owner.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__token_accounts_tests__get_token_accounts_by_owner.snap new file mode 100644 index 000000000..e99e05b5e --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__token_accounts_tests__get_token_accounts_by_owner.snap @@ -0,0 +1,35 @@ +--- +source: integration_tests/tests/integration_tests/token_accounts_tests.rs +expression: response +snapshot_kind: text +--- +{ + "total": 2, + "limit": 1000, + "token_accounts": [ + { + "address": "jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2", + "mint": "wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1", + "amount": 1000000000000, + "owner": "CeviT1DTQLuicEB7yLeFkkAGmam5GnJssbGb7CML4Tgx", + "frozen": false, + "delegate": null, + "delegated_amount": 0, + "close_authority": null, + "extensions": null + }, + { + "address": "3Pv9H5UzU8T9BwgutXrcn2wLohS1JUZuk3x8paiRyzui", + "mint": "F3D8Priw3BRecH36BuMubQHrTUn1QxmupLHEmmbZ4LXW", + "amount": 10000, + "owner": "CeviT1DTQLuicEB7yLeFkkAGmam5GnJssbGb7CML4Tgx", + "frozen": false, + "delegate": null, + "delegated_amount": 0, + "close_authority": null, + "extensions": null + } + ], + "cursor": null, + "errors": [] +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__token_extensions_tests__token_extensions_get_asset_scenario1.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__token_extensions_tests__token_extensions_get_asset_scenario1.snap new file mode 100644 index 000000000..3e6f1d5a1 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__token_extensions_tests__token_extensions_get_asset_scenario1.snap @@ -0,0 +1,53 @@ +--- +source: integration_tests/tests/integration_tests/token_extensions_tests.rs +expression: response +snapshot_kind: text +--- +{ + "interface": "FungibleToken", + "id": "BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y", + "authorities": [], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.0, + "basis_points": 0, + "primary_sale_happened": false, + "locked": false + }, + "creators": [], + "ownership": { + "frozen": false, + "delegated": false, + "delegate": null, + "ownership_model": "token", + "owner": "" + }, + "mutable": true, + "burnt": false, + "mint_extensions": { + "metadata": { + "uri": "https://acme.com/demo.json", + "mint": "BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y", + "name": "DAS Dev", + "symbol": "DAS", + "update_authority": "Em34oqDQYQZ9b6ycPHD28K47mttrRsdNu1S1pgK6NtPL", + "additional_metadata": [] + }, + "metadata_pointer": { + "authority": "Em34oqDQYQZ9b6ycPHD28K47mttrRsdNu1S1pgK6NtPL", + "metadata_address": "BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y" + } + } +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__token_extensions_tests__token_extensions_get_asset_scenario2.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__token_extensions_tests__token_extensions_get_asset_scenario2.snap new file mode 100644 index 000000000..d56a5b03f --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__token_extensions_tests__token_extensions_get_asset_scenario2.snap @@ -0,0 +1,68 @@ +--- +source: integration_tests/tests/integration_tests/token_extensions_tests.rs +expression: response +snapshot_kind: text +--- +{ + "interface": "FungibleToken", + "id": "HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM", + "authorities": [], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.0, + "basis_points": 0, + "primary_sale_happened": false, + "locked": false + }, + "creators": [], + "ownership": { + "frozen": false, + "delegated": false, + "delegate": null, + "ownership_model": "token", + "owner": "" + }, + "mutable": true, + "burnt": false, + "mint_extensions": { + "metadata": { + "uri": "https://token-metadata.paxos.com/usdp_metadata/prod/solana/usdp_metadata.json", + "mint": "HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM", + "name": "Pax Dollar", + "symbol": "USDP", + "update_authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "additional_metadata": [] + }, + "transfer_hook": { + "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "program_id": null + }, + "metadata_pointer": { + "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "metadata_address": "HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM" + }, + "permanent_delegate": { + "delegate": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk" + }, + "mint_close_authority": { + "close_authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk" + }, + "confidential_transfer_mint": { + "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "auditor_elgamal_pubkey": null, + "auto_approve_new_accounts": false + } + } +} diff --git a/integration_tests/tests/integration_tests/test_get_assets_with_multiple_same_ids.rs b/integration_tests/tests/integration_tests/test_get_assets_with_multiple_same_ids.rs new file mode 100644 index 000000000..aebb1c8c2 --- /dev/null +++ b/integration_tests/tests/integration_tests/test_get_assets_with_multiple_same_ids.rs @@ -0,0 +1,42 @@ +use function_name::named; + +use das_api::api::{self, ApiContract}; + +use itertools::Itertools; + +use serial_test::serial; + +use super::common::*; + +#[tokio::test] +#[serial] +#[named] +async fn test_get_assets_with_multiple_same_ids() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new(name.clone()).await; + + let seeds: Vec = seed_accounts([ + "F9Lw3ki3hJ7PF9HQXsBzoY8GyE6sPoEZZdXJBsTTD2rk", + "DZAZ3mGuq7nCYGzUyw4MiA74ysr15EfqLpzCzX2cRVng", + "JEKKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X", + "2ecGsTKbj7FecLwxTHaodZRFwza7m7LamqDG4YjczZMj", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "ids": [ + "F9Lw3ki3hJ7PF9HQXsBzoY8GyE6sPoEZZdXJBsTTD2rk", + "F9Lw3ki3hJ7PF9HQXsBzoY8GyE6sPoEZZdXJBsTTD2rk", + "JEKKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X", + "JEKKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X" + ] + } + "#; + + let request: api::GetAssets = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_assets(request).await.unwrap(); + insta::assert_json_snapshot!(name, response); +} diff --git a/integration_tests/tests/integration_tests/test_show_zero_balance_filter.rs b/integration_tests/tests/integration_tests/test_show_zero_balance_filter.rs new file mode 100644 index 000000000..e19f18d71 --- /dev/null +++ b/integration_tests/tests/integration_tests/test_show_zero_balance_filter.rs @@ -0,0 +1,83 @@ +use function_name::named; + +use das_api::api::{self, ApiContract}; + +use itertools::Itertools; + +use serial_test::serial; + +use super::common::*; + +#[tokio::test] +#[serial] +#[named] +async fn test_show_zero_balance_filter_being_enabled() { + let name = trim_test_name(function_name!()); + + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "BE1CkzRjLTXAWcSVCaqzycwXsZ18Yuk3jMDMnPUoHjjS", + "CyqarC6hyNYvb3EDueyeYrnGeAUjCDtMvWrbtdAnA53a", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "ownerAddress":"2oerfxddTpK5hWAmCMYB6fr9WvNrjEH54CHCWK8sAq7g", + "displayOptions": { + "showZeroBalance": true + } + } + "#; + + let request: api::GetTokenAccounts = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_token_accounts(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} + +#[tokio::test] +#[serial] +#[named] +async fn test_show_zero_balance_filter_being_disabled() { + let name = trim_test_name(function_name!()); + + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "BE1CkzRjLTXAWcSVCaqzycwXsZ18Yuk3jMDMnPUoHjjS", + "CyqarC6hyNYvb3EDueyeYrnGeAUjCDtMvWrbtdAnA53a", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "ownerAddress":"2oerfxddTpK5hWAmCMYB6fr9WvNrjEH54CHCWK8sAq7g", + "displayOptions": { + "showZeroBalance": false + } + } + "#; + + let request: api::GetTokenAccounts = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_token_accounts(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} diff --git a/integration_tests/tests/integration_tests/token_accounts_tests.rs b/integration_tests/tests/integration_tests/token_accounts_tests.rs new file mode 100644 index 000000000..8a465e914 --- /dev/null +++ b/integration_tests/tests/integration_tests/token_accounts_tests.rs @@ -0,0 +1,74 @@ +use function_name::named; + +use das_api::api::{self, ApiContract}; + +use itertools::Itertools; + +use serial_test::serial; + +use super::common::*; + +#[tokio::test] +#[serial] +#[named] +async fn test_get_token_accounts_by_mint() { + let name = trim_test_name(function_name!()); + + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Devnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts(["jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2"]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "mintAddress":"wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1" + } + "#; + + let request: api::GetTokenAccounts = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_token_accounts(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} + +#[tokio::test] +#[serial] +#[named] +async fn test_get_token_accounts_by_owner() { + let name = trim_test_name(function_name!()); + + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Devnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2", + "3Pv9H5UzU8T9BwgutXrcn2wLohS1JUZuk3x8paiRyzui", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "ownerAddress":"CeviT1DTQLuicEB7yLeFkkAGmam5GnJssbGb7CML4Tgx" + } + "#; + + let request: api::GetTokenAccounts = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_token_accounts(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} diff --git a/migration/src/lib.rs b/migration/src/lib.rs index 7cee77f5e..211501cd3 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -38,11 +38,13 @@ mod m20240116_130744_add_update_metadata_ix; mod m20240117_120101_alter_creator_indices; mod m20240124_173104_add_tree_seq_index_to_cl_audits_v2; mod m20240124_181900_add_slot_updated_column_per_update_type; +mod m20240219_115532_add_extensions_column; mod m20240313_120101_add_mpl_core_plugins_columns; mod m20240319_120101_add_mpl_core_enum_vals; mod m20240320_120101_add_mpl_core_info_items; mod m20240520_120101_add_mpl_core_external_plugins_columns; mod m20240718_161232_change_supply_columns_to_numeric; +mod m20241119_060310_add_token_inscription_enum_variant; pub mod model; @@ -90,11 +92,13 @@ impl MigratorTrait for Migrator { Box::new(m20240117_120101_alter_creator_indices::Migration), Box::new(m20240124_173104_add_tree_seq_index_to_cl_audits_v2::Migration), Box::new(m20240124_181900_add_slot_updated_column_per_update_type::Migration), + Box::new(m20240219_115532_add_extensions_column::Migration), Box::new(m20240313_120101_add_mpl_core_plugins_columns::Migration), Box::new(m20240319_120101_add_mpl_core_enum_vals::Migration), Box::new(m20240320_120101_add_mpl_core_info_items::Migration), Box::new(m20240520_120101_add_mpl_core_external_plugins_columns::Migration), Box::new(m20240718_161232_change_supply_columns_to_numeric::Migration), + Box::new(m20241119_060310_add_token_inscription_enum_variant::Migration), ] } } diff --git a/migration/src/m20240219_115532_add_extensions_column.rs b/migration/src/m20240219_115532_add_extensions_column.rs new file mode 100644 index 000000000..d0414ea18 --- /dev/null +++ b/migration/src/m20240219_115532_add_extensions_column.rs @@ -0,0 +1,60 @@ +use sea_orm_migration::{ + prelude::*, + sea_orm::{ConnectionTrait, DatabaseBackend, Statement}, +}; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let connection = manager.get_connection(); + + connection + .execute(Statement::from_string( + DatabaseBackend::Postgres, + "ALTER TABLE asset ADD COLUMN mint_extensions jsonb;".to_string(), + )) + .await?; + connection + .execute(Statement::from_string( + DatabaseBackend::Postgres, + "ALTER TABLE tokens ADD COLUMN extensions jsonb;".to_string(), + )) + .await?; + connection + .execute(Statement::from_string( + DatabaseBackend::Postgres, + "ALTER TABLE token_accounts ADD COLUMN extensions jsonb;".to_string(), + )) + .await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let connection = manager.get_connection(); + + connection + .execute(Statement::from_string( + DatabaseBackend::Postgres, + "ALTER TABLE asset DROP COLUMN mint_extensions;".to_string(), + )) + .await?; + connection + .execute(Statement::from_string( + DatabaseBackend::Postgres, + "ALTER TABLE tokens DROP COLUMN extensions;".to_string(), + )) + .await?; + connection + .execute(Statement::from_string( + DatabaseBackend::Postgres, + "ALTER TABLE token_accounts DROP COLUMN extensions;".to_string(), + )) + .await?; + + Ok(()) + } +} diff --git a/migration/src/m20241119_060310_add_token_inscription_enum_variant.rs b/migration/src/m20241119_060310_add_token_inscription_enum_variant.rs new file mode 100644 index 000000000..1bdd6f8c3 --- /dev/null +++ b/migration/src/m20241119_060310_add_token_inscription_enum_variant.rs @@ -0,0 +1,25 @@ +use sea_orm::{ConnectionTrait, DatabaseBackend, Statement}; +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .get_connection() + .execute(Statement::from_string( + DatabaseBackend::Postgres, + "ALTER TYPE v1_account_attachments ADD VALUE IF NOT EXISTS 'token_inscription';" + .to_string(), + )) + .await?; + + Ok(()) + } + + async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> { + Ok(()) + } +} diff --git a/nft_ingester/src/backfiller.rs b/nft_ingester/src/backfiller.rs new file mode 100644 index 000000000..7fbb1c214 --- /dev/null +++ b/nft_ingester/src/backfiller.rs @@ -0,0 +1,1078 @@ +//! Backfiller that fills gaps in trees by detecting gaps in sequence numbers +//! in the `backfill_items` table. Inspired by backfiller.ts/backfill.ts. + +use borsh::BorshDeserialize; +use cadence_macros::{is_global_default_set, statsd_count, statsd_gauge}; +use chrono::Utc; +use digital_asset_types::dao::backfill_items; +use flatbuffers::FlatBufferBuilder; +use futures::{stream::FuturesUnordered, StreamExt}; +use log::{debug, error, info}; +use plerkle_messenger::{Messenger, TRANSACTION_BACKFILL_STREAM}; +use plerkle_serialization::serializer::seralize_encoded_transaction_with_status; + +use sea_orm::{ + entity::*, query::*, sea_query::Expr, DatabaseConnection, DbBackend, DbErr, FromQueryResult, + SqlxPostgresConnector, +}; +use solana_account_decoder::UiAccountEncoding; +use solana_client::{ + nonblocking::rpc_client::RpcClient, + rpc_client::GetConfirmedSignaturesForAddress2Config, + rpc_config::{RpcAccountInfoConfig, RpcBlockConfig, RpcProgramAccountsConfig}, + rpc_filter::{Memcmp, RpcFilterType}, +}; +use solana_sdk::{ + account::Account, + commitment_config::{CommitmentConfig, CommitmentLevel}, + pubkey::Pubkey, + signature::Signature, + slot_history::Slot, +}; +use solana_transaction_status::{ + option_serializer::OptionSerializer, EncodedConfirmedBlock, + EncodedConfirmedTransactionWithStatusMeta, UiTransactionEncoding, +}; +use spl_account_compression::state::{ + merkle_tree_get_size, ConcurrentMerkleTreeHeader, CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1, +}; +use sqlx::{self, Pool, Postgres}; +use std::{ + cmp, + collections::{HashMap, HashSet}, + str::FromStr, + sync::Arc, +}; +use stretto::{AsyncCache, AsyncCacheBuilder}; +use tokio::{ + sync::Semaphore, + task::JoinSet, + time::{self, sleep, Duration}, +}; + +use crate::{ + config::{IngesterConfig, DATABASE_LISTENER_CHANNEL_KEY, RPC_COMMITMENT_KEY, RPC_URL_KEY}, + error::IngesterError, + metric, +}; +// Number of tries to backfill a single tree before marking as "failed". +const NUM_TRIES: i32 = 5; +const TREE_SYNC_INTERVAL: u64 = 60; +const MAX_BACKFILL_CHECK_WAIT: u64 = 1000; +// Constants used for varying delays when failures occur. +const INITIAL_FAILURE_DELAY: u64 = 100; +const MAX_FAILURE_DELAY_MS: u64 = 10_000; +const BLOCK_CACHE_SIZE: usize = 300_000; +const MAX_CACHE_COST: i64 = 32; +const BLOCK_CACHE_DURATION: u64 = 172800; + +struct SlotSeq(u64, u64); +/// Main public entry point for backfiller task. +pub fn setup_backfiller( + pool: Pool, + config: IngesterConfig, +) -> tokio::task::JoinHandle<()> { + tokio::spawn(async move { + loop { + let pool_cloned = pool.clone(); + let config_cloned = config.clone(); + let block_cache = Arc::new( + AsyncCacheBuilder::new(BLOCK_CACHE_SIZE, MAX_CACHE_COST) + .set_ignore_internal_cost(true) + .finalize(tokio::spawn) + .expect("failed to create cache"), + ); + let mut tasks = JoinSet::new(); + let bc = Arc::clone(&block_cache); + tasks.spawn(async move { + info!("Backfiller filler running"); + let mut backfiller = Backfiller::::new(pool_cloned, config_cloned, &bc).await; + backfiller.run_filler().await; + }); + + let pool_cloned = pool.clone(); + let config_cloned = config.clone(); + let bc = Arc::clone(&block_cache); + tasks.spawn(async move { + info!("Backfiller finder running"); + let mut backfiller = Backfiller::::new(pool_cloned, config_cloned, &bc).await; + backfiller.run_finder().await; + }); + + while let Some(task) = tasks.join_next().await { + match task { + Ok(_) => break, + Err(err) if err.is_panic() => { + metric! { + statsd_count!("ingester.backfiller.task_panic", 1); + } + } + Err(err) => { + let err = err.to_string(); + metric! { + statsd_count!("ingester.backfiller.task_error", 1, "error" => &err); + } + } + } + } + } + }) +} + +/// Struct used when querying for unique trees. +#[derive(Debug, FromQueryResult)] +struct UniqueTree { + tree: Vec, +} + +/// Struct used when querying for unique trees. +#[derive(Debug, FromQueryResult)] +struct TreeWithSlot { + tree: Vec, + slot: i64, +} + +#[derive(Debug, Default, Clone)] +struct MissingTree { + tree: Pubkey, + slot: u64, +} + +/// Struct used when storing trees to backfill. +struct BackfillTree { + unique_tree: UniqueTree, + backfill_from_seq_1: bool, + #[allow(dead_code)] + slot: u64, +} + +impl BackfillTree { + const fn new(unique_tree: UniqueTree, backfill_from_seq_1: bool, slot: u64) -> Self { + Self { + unique_tree, + backfill_from_seq_1, + slot, + } + } +} + +/// Struct used when querying the max sequence number of a tree. +#[derive(Debug, FromQueryResult, Clone)] +struct MaxSeqItem { + seq: i64, +} + +/// Struct used when querying for items to backfill. +#[derive(Debug, FromQueryResult, Clone)] +struct SimpleBackfillItem { + seq: i64, + slot: i64, +} + +/// Struct used to store sequence number gap info for a given tree. +#[derive(Debug)] +struct GapInfo { + prev: SimpleBackfillItem, + curr: SimpleBackfillItem, +} + +impl GapInfo { + const fn new(prev: SimpleBackfillItem, curr: SimpleBackfillItem) -> Self { + Self { prev, curr } + } +} + +/// Main struct used for backfiller task. +struct Backfiller<'a, T: Messenger> { + config: IngesterConfig, + db: DatabaseConnection, + rpc_client: RpcClient, + rpc_block_config: RpcBlockConfig, + messenger: T, + failure_delay: u64, + cache: &'a AsyncCache, +} + +impl<'a, T: Messenger> Backfiller<'a, T> { + /// Create a new `Backfiller` struct. + async fn new( + pool: Pool, + config: IngesterConfig, + cache: &'a AsyncCache, + ) -> Backfiller<'a, T> { + // Create Sea ORM database connection used later for queries. + let db = SqlxPostgresConnector::from_sqlx_postgres_pool(pool.clone()); + + // Get database listener channel. + let _channel = config + .database_config + .get(DATABASE_LISTENER_CHANNEL_KEY) + .and_then(|u| u.clone().into_string()) + .ok_or(IngesterError::ConfigurationError { + msg: format!( + "Database listener channel missing: {}", + DATABASE_LISTENER_CHANNEL_KEY + ), + }) + .unwrap(); + + // Get RPC URL. + let rpc_url = config + .rpc_config + .get(RPC_URL_KEY) + .and_then(|u| u.clone().into_string()) + .ok_or(IngesterError::ConfigurationError { + msg: format!("RPC URL missing: {}", RPC_URL_KEY), + }) + .unwrap(); + + // Get RPC commitment level. + let rpc_commitment_level = config + .rpc_config + .get(RPC_COMMITMENT_KEY) + .and_then(|v| v.as_str()) + .ok_or(IngesterError::ConfigurationError { + msg: format!("RPC commitment level missing: {}", RPC_COMMITMENT_KEY), + }) + .unwrap(); + + // Check if commitment level is valid and create `CommitmentConfig`. + let rpc_commitment = CommitmentConfig { + commitment: CommitmentLevel::from_str(rpc_commitment_level) + .map_err(|_| IngesterError::ConfigurationError { + msg: format!("Invalid RPC commitment level: {}", rpc_commitment_level), + }) + .unwrap(), + }; + + // Create `RpcBlockConfig` used when getting blocks from RPC provider. + let rpc_block_config = RpcBlockConfig { + encoding: Some(UiTransactionEncoding::Base64), + commitment: Some(rpc_commitment), + max_supported_transaction_version: Some(0), + ..RpcBlockConfig::default() + }; + + // Instantiate RPC client. + let rpc_client = RpcClient::new_with_commitment(rpc_url, rpc_commitment); + + // Instantiate messenger. + let mut messenger = T::new(config.get_messneger_client_config()).await.unwrap(); + messenger + .add_stream(TRANSACTION_BACKFILL_STREAM) + .await + .unwrap(); + messenger + .set_buffer_size(TRANSACTION_BACKFILL_STREAM, 10_000_000) + .await; + + Self { + config, + db, + rpc_client, + rpc_block_config, + messenger, + failure_delay: INITIAL_FAILURE_DELAY, + cache, + } + } + + async fn run_finder(&mut self) { + let mut interval = time::interval(tokio::time::Duration::from_secs(TREE_SYNC_INTERVAL)); + let sem = Semaphore::new(1); + loop { + interval.tick().await; + let _permit = sem.acquire().await.unwrap(); + + debug!("Looking for missing trees..."); + + let missing = self.get_missing_trees(&self.db).await; + match missing { + Ok(missing_trees) => { + let txn = self.db.begin().await.unwrap(); + let len = missing_trees.len(); + metric! { + statsd_gauge!("ingester.backfiller.missing_trees", len as f64); + } + debug!("Found {} missing trees", len); + if len > 0 { + let res = self.force_backfill_missing_trees(missing_trees, &txn).await; + + let res2 = txn.commit().await; + match (res, res2) { + (Ok(_), Ok(_)) => { + debug!("Set {} trees to backfill from 0", len); + } + (Err(e), _) => { + error!("Error setting trees to backfill from 0: {}", e); + } + (_, Err(e)) => { + error!("Error setting trees to backfill from 0: error committing transaction: {}", e); + } + } + } + } + Err(e) => { + error!("Error getting missing trees: {}", e); + } + } + } + } + /// Run the backfiller task. + async fn run_filler(&mut self) { + let mut interval = + time::interval(tokio::time::Duration::from_millis(MAX_BACKFILL_CHECK_WAIT)); + loop { + interval.tick().await; + match self.get_trees_to_backfill().await { + Ok(backfill_trees) => { + if !backfill_trees.is_empty() { + for backfill_tree in backfill_trees { + for tries in 1..=NUM_TRIES { + // Get the tree out of nested structs. + let tree = &backfill_tree.unique_tree.tree; + let tree_string = bs58::encode(&tree).into_string(); + info!("Backfilling tree: {tree_string}"); + // Call different methods based on whether tree needs to be backfilled + // completely from seq number 1 or just have any gaps in seq number + // filled. + let result = if backfill_tree.backfill_from_seq_1 { + self.backfill_tree_from_seq_1(&backfill_tree).await + } else { + self.fetch_and_plug_gaps(tree).await + }; + + match result { + Ok(opt_max_seq) => { + // Successfully backfilled the tree. Now clean up database. + self.clean_up_backfilled_tree( + opt_max_seq, + tree, + &tree_string, + tries, + ) + .await; + self.reset_delay(); + break; + } + Err(err) => { + error!("Failed to fetch and plug gaps for {tree_string}, attempt {tries}"); + error!("{err}"); + } + } + + if tries == NUM_TRIES { + if let Err(err) = self.mark_tree_as_failed(tree).await { + error!("Error marking tree as failed to backfill: {err}"); + } + } else { + self.sleep_and_increase_delay().await; + } + } + } + } + } + Err(err) => { + // Print error but keep trying. + error!("Could not get trees to backfill from db: {err}"); + self.sleep_and_increase_delay().await; + } + } + } + } + + async fn force_backfill_missing_trees( + &mut self, + missing_trees: Vec, + cn: &impl ConnectionTrait, + ) -> Result<(), IngesterError> { + let trees = missing_trees + .into_iter() + .map(|tree| backfill_items::ActiveModel { + tree: Set(tree.tree.as_ref().to_vec()), + seq: Set(0), + slot: Set(tree.slot as i64), + force_chk: Set(true), + backfilled: Set(false), + failed: Set(false), + ..Default::default() + }) + .collect::>(); + + backfill_items::Entity::insert_many(trees).exec(cn).await?; + + Ok(()) + } + + async fn clean_up_backfilled_tree( + &mut self, + opt_max_seq: Option, + tree: &[u8], + tree_string: &String, + tries: i32, + ) { + match opt_max_seq { + Some(max_seq) => { + debug!("Successfully backfilled tree: {tree_string}, attempt {tries}"); + + // Delete extra rows and mark as backfilled. + match self + .delete_extra_rows_and_mark_as_backfilled(tree, max_seq) + .await + { + Ok(_) => { + // Debug. + debug!("Successfully deleted rows up to {max_seq}"); + } + Err(err) => { + error!("Error deleting rows and marking as backfilled: {err}"); + if let Err(err) = self.mark_tree_as_failed(tree).await { + error!("Error marking tree as failed to backfill: {err}"); + } + } + } + } + None => { + // Debug. + error!("Unexpected error, tree was in list, but no rows found for {tree_string}"); + if let Err(err) = self.mark_tree_as_failed(tree).await { + error!("Error marking tree as failed to backfill: {err}"); + } + } + } + } + + async fn sleep_and_increase_delay(&mut self) { + sleep(Duration::from_millis(self.failure_delay)).await; + + // Increase failure delay up to `MAX_FAILURE_DELAY_MS`. + self.failure_delay = self.failure_delay.saturating_mul(2); + if self.failure_delay > MAX_FAILURE_DELAY_MS { + self.failure_delay = MAX_FAILURE_DELAY_MS; + } + } + + fn reset_delay(&mut self) { + self.failure_delay = INITIAL_FAILURE_DELAY; + } + + async fn get_missing_trees( + &self, + cn: &impl ConnectionTrait, + ) -> Result, IngesterError> { + let mut all_trees: HashMap = self.fetch_trees_by_gpa().await?; + debug!("Number of Trees on Chain {}", all_trees.len()); + + if let Some(only_trees) = &self.config.backfiller_trees { + let mut trees = HashSet::with_capacity(only_trees.len()); + for tree in only_trees { + trees.insert(Pubkey::try_from(tree.as_str()).expect("backfiller tree is invalid")); + } + + all_trees.retain(|key, _value| trees.contains(key)); + info!( + "Number of Trees to backfill (with only filter): {}", + all_trees.len() + ); + } + let get_locked_or_failed_trees = Statement::from_string( + DbBackend::Postgres, + "SELECT DISTINCT tree FROM backfill_items WHERE failed = true\n\ + OR locked = true" + .to_string(), + ); + let locked_trees = cn.query_all(get_locked_or_failed_trees).await?; + for row in locked_trees.into_iter() { + let tree = UniqueTree::from_query_result(&row, "")?; + let key = Pubkey::try_from(tree.tree.as_slice()).unwrap(); + all_trees.remove(&key); + } + info!( + "Number of Trees to backfill (with failed/locked filter): {}", + all_trees.len() + ); + + // Get all the local trees already in cl_items and remove them + let get_all_local_trees = Statement::from_string( + DbBackend::Postgres, + "SELECT DISTINCT cl_items.tree FROM cl_items".to_string(), + ); + let force_chk_trees = cn.query_all(get_all_local_trees).await?; + for row in force_chk_trees.into_iter() { + let tree = UniqueTree::from_query_result(&row, "")?; + let key = Pubkey::try_from(tree.tree.as_slice()).unwrap(); + all_trees.remove(&key); + } + info!( + "Number of Trees to backfill (with cl_items existed filter): {}", + all_trees.len() + ); + + // After removing all the tres in backfill_itemsa nd the trees already in CL Items then return the list + // of missing trees + let missing_trees = all_trees + .into_iter() + .map(|(k, s)| MissingTree { tree: k, slot: s.0 }) + .collect::>(); + if !missing_trees.is_empty() { + info!("Number of Missing local trees: {}", missing_trees.len()); + } else { + debug!("No missing trees"); + } + Ok(missing_trees) + } + + async fn get_trees_to_backfill(&self) -> Result, DbErr> { + // Start a db transaction. + let txn = self.db.begin().await?; + + // Get trees with the `force_chk` flag set to true (that have not failed and are not locked). + let force_chk_trees = Statement::from_string( + DbBackend::Postgres, + "SELECT DISTINCT backfill_items.tree, backfill_items.slot FROM backfill_items\n\ + WHERE backfill_items.force_chk = TRUE\n\ + AND backfill_items.failed = FALSE\n\ + AND backfill_items.locked = FALSE" + .to_string(), + ); + + let force_chk_trees: Vec = + txn.query_all(force_chk_trees).await.map(|qr| { + qr.iter() + .map(|q| TreeWithSlot::from_query_result(q, "").unwrap()) + .collect() + })?; + + debug!( + "Number of force check trees to backfill: {} {}", + force_chk_trees.len(), + Utc::now() + ); + + for tree in force_chk_trees.iter() { + let stmt = backfill_items::Entity::update_many() + .col_expr(backfill_items::Column::Locked, Expr::value(true)) + .filter(backfill_items::Column::Tree.eq(&*tree.tree)) + .build(DbBackend::Postgres); + + if let Err(err) = txn.execute(stmt).await { + error!( + "Error marking tree {} as locked: {}", + bs58::encode(&tree.tree).into_string(), + err + ); + return Err(err); + } + } + + // Get trees with multiple rows from `backfill_items` table (that have not failed and are not locked). + let multi_row_trees = Statement::from_string( + DbBackend::Postgres, + "SELECT backfill_items.tree, max(backfill_items.slot) as slot FROM backfill_items\n\ + WHERE backfill_items.failed = FALSE + AND backfill_items.locked = FALSE\n\ + GROUP BY backfill_items.tree\n\ + HAVING COUNT(*) > 1" + .to_string(), + ); + + let multi_row_trees: Vec = + txn.query_all(multi_row_trees).await.map(|qr| { + qr.iter() + .map(|q| TreeWithSlot::from_query_result(q, "").unwrap()) + .collect() + })?; + + debug!( + "Number of multi-row trees to backfill {}", + multi_row_trees.len() + ); + + for tree in multi_row_trees.iter() { + let stmt = backfill_items::Entity::update_many() + .col_expr(backfill_items::Column::Locked, Expr::value(true)) + .filter(backfill_items::Column::Tree.eq(&*tree.tree)) + .build(DbBackend::Postgres); + + if let Err(err) = txn.execute(stmt).await { + error!( + "Error marking tree {} as locked: {}", + bs58::encode(&tree.tree).into_string(), + err + ); + return Err(err); + } + } + + // Close out transaction and relinqish the lock. + txn.commit().await?; + + // Convert force check trees Vec of `UniqueTree` to a Vec of `BackfillTree` (which contain extra info). + let mut trees: Vec = force_chk_trees + .into_iter() + .map(|tree| BackfillTree::new(UniqueTree { tree: tree.tree }, true, tree.slot as u64)) + .collect(); + + // Convert multi-row trees Vec of `UniqueTree` to a Vec of `BackfillTree` (which contain extra info). + let mut multi_row_trees: Vec = multi_row_trees + .into_iter() + .map(|tree| BackfillTree::new(UniqueTree { tree: tree.tree }, false, tree.slot as u64)) + .collect(); + + trees.append(&mut multi_row_trees); + + Ok(trees) + } + + async fn backfill_tree_from_seq_1( + &mut self, + btree: &BackfillTree, + ) -> Result, IngesterError> { + let address = match Pubkey::try_from(btree.unique_tree.tree.as_slice()) { + Ok(pubkey) => pubkey, + Err(error) => { + return Err(IngesterError::DeserializationError(format!( + "failed to parse pubkey: {error:?}" + ))) + } + }; + + let slots = self.find_slots_via_address(&address).await?; + let address = btree.unique_tree.tree.clone(); + for slot in slots { + let gap = GapInfo { + prev: SimpleBackfillItem { + seq: 0, + slot: slot as i64, + }, + curr: SimpleBackfillItem { + seq: 0, + slot: slot as i64, + }, + }; + self.plug_gap(&gap, &address).await?; + } + Ok(Some(0)) + } + + async fn find_slots_via_address(&self, address: &Pubkey) -> Result, IngesterError> { + let mut last_sig = None; + let mut slots = HashSet::new(); + // TODO: Any log running function like this should actually be run in a way that supports re-entry, + // usually we woudl break the tasks into smaller parralel tasks and we woudl not worry about it, but in this we have several linearally dpendent async tasks + // and if they fail, it causes a chain reaction of failures since the dependant nature of it affects the next task. Right now you are just naivley looping and + // hoping for the best what needs to happen is to start saving the state opf each task with the last signature that was retuned iun durable storage. + // Then if the task fails, you can restart it from the last signature that was returned. + loop { + let before = last_sig; + let sigs = self + .rpc_client + .get_signatures_for_address_with_config( + address, + GetConfirmedSignaturesForAddress2Config { + before, + until: None, + ..GetConfirmedSignaturesForAddress2Config::default() + }, + ) + .await + .map_err(|e| { + IngesterError::RpcGetDataError(format!( + "GetSignaturesForAddressWithConfig failed {}", + e + )) + })?; + for sig in sigs.iter() { + let slot = sig.slot; + let sig = Signature::from_str(&sig.signature).map_err(|e| { + IngesterError::RpcDataUnsupportedFormat(format!( + "Failed to parse signature {}", + e + )) + })?; + + slots.insert(slot); + last_sig = Some(sig); + } + if sigs.is_empty() || sigs.len() < 1000 { + break; + } + } + Ok(Vec::from_iter(slots)) + } + + #[allow(dead_code)] + async fn get_max_seq(&self, tree: &[u8]) -> Result, DbErr> { + let query = backfill_items::Entity::find() + .select_only() + .column(backfill_items::Column::Seq) + .filter(backfill_items::Column::Tree.eq(tree)) + .order_by_desc(backfill_items::Column::Seq) + .limit(1) + .build(DbBackend::Postgres); + + let start_seq_vec = MaxSeqItem::find_by_statement(query).all(&self.db).await?; + + Ok(start_seq_vec.last().map(|row| row.seq)) + } + + async fn clear_force_chk_flag(&self, tree: &[u8]) -> Result { + backfill_items::Entity::update_many() + .col_expr(backfill_items::Column::ForceChk, Expr::value(false)) + .filter(backfill_items::Column::Tree.eq(tree)) + .exec(&self.db) + .await + } + + async fn fetch_trees_by_gpa(&self) -> Result, IngesterError> { + let config = RpcProgramAccountsConfig { + filters: Some(vec![RpcFilterType::Memcmp(Memcmp::new_raw_bytes( + 0, + vec![1u8], + ))]), + account_config: RpcAccountInfoConfig { + encoding: Some(UiAccountEncoding::Base64), + ..RpcAccountInfoConfig::default() + }, + ..RpcProgramAccountsConfig::default() + }; + let results: Vec<(Pubkey, Account)> = self + .rpc_client + .get_program_accounts_with_config(&spl_account_compression::id(), config) + .await + .map_err(|e| IngesterError::RpcGetDataError(e.to_string()))?; + let mut list = HashMap::with_capacity(results.len()); + for r in results.into_iter() { + let (pubkey, mut account) = r; + let (header_bytes, rest) = account + .data + .split_at_mut(CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1); + let header: ConcurrentMerkleTreeHeader = + ConcurrentMerkleTreeHeader::try_from_slice(header_bytes) + .map_err(|e| IngesterError::RpcGetDataError(e.to_string()))?; + + let auth = Pubkey::find_program_address(&[pubkey.as_ref()], &mpl_bubblegum::ID).0; + + let merkle_tree_size = merkle_tree_get_size(&header) + .map_err(|e| IngesterError::RpcGetDataError(e.to_string()))?; + let (tree_bytes, _canopy_bytes) = rest.split_at_mut(merkle_tree_size); + let seq_bytes = tree_bytes[0..8].try_into().map_err(|_e| { + IngesterError::RpcGetDataError("Failed to convert seq bytes to array".to_string()) + })?; + let seq = u64::from_le_bytes(seq_bytes); + list.insert(pubkey, SlotSeq(header.get_creation_slot(), seq)); + + if header.assert_valid_authority(&auth).is_err() { + continue; + } + } + Ok(list) + } + + // Similar to `fetchAndPlugGaps()` in `backfiller.ts`. + async fn fetch_and_plug_gaps(&mut self, tree: &[u8]) -> Result, IngesterError> { + let (opt_max_seq, gaps) = self.get_missing_data(tree).await?; + + // Similar to `plugGapsBatched()` in `backfiller.ts` (although not batched). + for gap in gaps.iter() { + // Similar to `plugGaps()` in `backfiller.ts`. + self.plug_gap(gap, tree).await?; + } + + Ok(opt_max_seq) + } + + // Similar to `getMissingData()` in `db.ts`. + async fn get_missing_data(&self, tree: &[u8]) -> Result<(Option, Vec), DbErr> { + // Get the maximum sequence number that has been backfilled, and use + // that for the starting sequence number for backfilling. + let query = backfill_items::Entity::find() + .select_only() + .column(backfill_items::Column::Seq) + .filter( + Condition::all() + .add(backfill_items::Column::Tree.eq(tree)) + .add(backfill_items::Column::Backfilled.eq(true)), + ) + .order_by_desc(backfill_items::Column::Seq) + .limit(1) + .build(DbBackend::Postgres); + + let start_seq_vec = MaxSeqItem::find_by_statement(query).all(&self.db).await?; + let start_seq = start_seq_vec.last().map(|row| row.seq).unwrap_or_default(); + + // Get all rows for the tree that have not yet been backfilled. + let mut query = backfill_items::Entity::find() + .select_only() + .column(backfill_items::Column::Seq) + .column(backfill_items::Column::Slot) + .filter( + Condition::all() + .add(backfill_items::Column::Seq.gte(start_seq)) + .add(backfill_items::Column::Tree.eq(tree)), + ) + .order_by_asc(backfill_items::Column::Seq) + .build(DbBackend::Postgres); + + query.sql = query.sql.replace("SELECT", "SELECT DISTINCT"); + let rows = SimpleBackfillItem::find_by_statement(query) + .all(&self.db) + .await?; + let mut gaps = vec![]; + + // Look at each pair of subsequent rows, looking for a gap in sequence number. + for (prev, curr) in rows.iter().zip(rows.iter().skip(1)) { + if curr.seq == prev.seq { + let message = format!( + "Error in DB, identical sequence numbers with different slots: {}, {}", + prev.slot, curr.slot + ); + error!("{}", message); + return Err(DbErr::Custom(message)); + } else if curr.seq - prev.seq > 1 { + gaps.push(GapInfo::new(prev.clone(), curr.clone())); + } + } + + // Get the max sequence number if any rows were returned from the query. + let opt_max_seq = rows.last().map(|row| row.seq); + + Ok((opt_max_seq, gaps)) + } + + async fn plug_gap(&mut self, gap: &GapInfo, tree: &[u8]) -> Result<(), IngesterError> { + // TODO: This needs to make sure all slots are available otherwise it will partially + // fail and redo the whole backfill process. So for now checking the max block before + // looping as a quick workaround. + let diff = gap.curr.slot - gap.prev.slot; + let mut num_iter = (diff + 250_000) / 500_000; + let mut start_slot = gap.prev.slot; + let mut end_slot = gap.prev.slot + cmp::min(500_000, diff); + let get_confirmed_slot_tasks = FuturesUnordered::new(); + if num_iter == 0 { + num_iter = 1; + } + for _ in 0..num_iter { + get_confirmed_slot_tasks.push(self.rpc_client.get_blocks_with_commitment( + start_slot as u64, + Some(end_slot as u64), + CommitmentConfig { + commitment: CommitmentLevel::Confirmed, + }, + )); + start_slot = end_slot; + end_slot = cmp::min(end_slot + 500_000, gap.curr.slot); + } + let result_slots = get_confirmed_slot_tasks + .collect::>() + .await + .into_iter() + .filter_map(|x| x.ok()) + .flatten(); + for slot in result_slots { + let key = format!("block{}", slot); + let mut cached_block = self.cache.get(&key).await; + if cached_block.is_none() { + debug!("Fetching block {} from RPC", slot); + let block = EncodedConfirmedBlock::from( + self.rpc_client + .get_block_with_config(slot, self.rpc_block_config) + .await + .map_err(|e| IngesterError::RpcGetDataError(e.to_string()))?, + ); + let cost = cmp::min(32, block.transactions.len() as i64); + let write = self + .cache + .try_insert_with_ttl( + key.clone(), + block, + cost, + Duration::from_secs(BLOCK_CACHE_DURATION), + ) + .await?; + + if !write { + return Err(IngesterError::CacheStorageWriteError(format!( + "Cache Write Failed on {} is missing.", + &key + ))); + } + self.cache.wait().await?; + cached_block = self.cache.get(&key).await; + } + if cached_block.is_none() { + return Err(IngesterError::CacheStorageWriteError(format!( + "Cache Procedure Failed {} is missing.", + &key + ))); + } + let block_ref = cached_block.unwrap(); + let block_data = block_ref.value(); + + for tx in block_data.transactions.iter() { + // See if transaction has an error. + let meta = if let Some(meta) = &tx.meta { + if let Some(_err) = &meta.err { + continue; + } + meta + } else { + error!("Unexpected, EncodedTransactionWithStatusMeta struct has no metadata"); + continue; + }; + let decoded_tx = if let Some(decoded_tx) = tx.transaction.decode() { + decoded_tx + } else { + error!("Unable to decode transaction"); + continue; + }; + let sig = decoded_tx.signatures[0].to_string(); + let msg = decoded_tx.message; + let atl_keys = msg.address_table_lookups(); + let tree = Pubkey::try_from(tree) + .map_err(|e| IngesterError::DeserializationError(e.to_string()))?; + let account_keys = msg.static_account_keys(); + let account_keys = { + let mut account_keys_vec = vec![]; + for key in account_keys.iter() { + account_keys_vec.push(key.to_bytes()); + } + if atl_keys.is_some() { + if let OptionSerializer::Some(ad) = &meta.loaded_addresses { + for i in &ad.writable { + let mut output: [u8; 32] = [0; 32]; + bs58::decode(i).into(&mut output).map_err(|e| { + IngesterError::DeserializationError(e.to_string()) + })?; + account_keys_vec.push(output); + } + + for i in &ad.readonly { + let mut output: [u8; 32] = [0; 32]; + bs58::decode(i).into(&mut output).map_err(|e| { + IngesterError::DeserializationError(e.to_string()) + })?; + account_keys_vec.push(output); + } + } + } + account_keys_vec + }; + + // Filter out transactions that don't have to do with the tree we are interested in or + // the Bubblegum program. + let tb = tree.to_bytes(); + let bubblegum = blockbuster::programs::bubblegum::ID.to_bytes(); + if account_keys.iter().all(|pk| *pk != tb && *pk != bubblegum) { + continue; + } + + // Serialize data. + let builder = FlatBufferBuilder::new(); + debug!("Serializing transaction in backfiller {}", sig); + let tx_wrap = EncodedConfirmedTransactionWithStatusMeta { + transaction: tx.to_owned(), + slot, + block_time: block_data.block_time, + }; + let builder = seralize_encoded_transaction_with_status(builder, tx_wrap)?; + self.messenger + .send(TRANSACTION_BACKFILL_STREAM, builder.finished_data()) + .await?; + } + drop(block_ref); + } + + Ok(()) + } + + async fn delete_extra_rows_and_mark_as_backfilled( + &self, + tree: &[u8], + max_seq: i64, + ) -> Result<(), DbErr> { + // Debug. + let test_items = backfill_items::Entity::find() + .filter(backfill_items::Column::Tree.eq(tree)) + .all(&self.db) + .await?; + debug!("Count of items before delete: {}", test_items.len()); + // Delete all rows in the `backfill_items` table for a specified tree, except for the row with + // the caller-specified max seq number. One row for each tree must remain so that gaps can be + // detected after subsequent inserts. + backfill_items::Entity::delete_many() + .filter( + Condition::all() + .add(backfill_items::Column::Tree.eq(tree)) + .add(backfill_items::Column::Seq.ne(max_seq)), + ) + .exec(&self.db) + .await?; + + // Remove any duplicates that have the caller-specified max seq number. This happens when + // a transaction that was already handled is replayed during backfilling. + let items = backfill_items::Entity::find() + .filter( + Condition::all() + .add(backfill_items::Column::Tree.eq(tree)) + .add(backfill_items::Column::Seq.ne(max_seq)), + ) + .all(&self.db) + .await?; + + if items.len() > 1 { + for item in items.iter().skip(1) { + backfill_items::Entity::delete_by_id(item.id) + .exec(&self.db) + .await?; + } + } + + // Mark remaining row as backfilled so future backfilling can start above this sequence number. + self.mark_tree_as_backfilled(tree).await?; + + // Clear the `force_chk` flag if it was set. + self.clear_force_chk_flag(tree).await?; + + // Unlock tree. + self.unlock_tree(tree).await?; + + // Debug. + let test_items = backfill_items::Entity::find() + .filter(backfill_items::Column::Tree.eq(tree)) + .all(&self.db) + .await?; + debug!("Count of items after delete: {}", test_items.len()); + Ok(()) + } + + async fn mark_tree_as_backfilled(&self, tree: &[u8]) -> Result<(), DbErr> { + backfill_items::Entity::update_many() + .col_expr(backfill_items::Column::Backfilled, Expr::value(true)) + .filter(backfill_items::Column::Tree.eq(tree)) + .exec(&self.db) + .await?; + + Ok(()) + } + + async fn mark_tree_as_failed(&self, tree: &[u8]) -> Result<(), DbErr> { + backfill_items::Entity::update_many() + .col_expr(backfill_items::Column::Failed, Expr::value(true)) + .filter(backfill_items::Column::Tree.eq(tree)) + .exec(&self.db) + .await?; + + Ok(()) + } + + async fn unlock_tree(&self, tree: &[u8]) -> Result<(), DbErr> { + backfill_items::Entity::update_many() + .col_expr(backfill_items::Column::Locked, Expr::value(false)) + .filter(backfill_items::Column::Tree.eq(tree)) + .exec(&self.db) + .await?; + + Ok(()) + } +} diff --git a/program_transformers/Cargo.toml b/program_transformers/Cargo.toml index da0848ced..4262824a5 100644 --- a/program_transformers/Cargo.toml +++ b/program_transformers/Cargo.toml @@ -30,6 +30,7 @@ sqlx = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true, features = ["time"] } tracing = { workspace = true } +spl-token-2022 = {workspace = true, features = ["no-entrypoint"]} [lints] workspace = true diff --git a/program_transformers/src/asset_upserts.rs b/program_transformers/src/asset_upserts.rs index a8e155031..66d7cb86d 100644 --- a/program_transformers/src/asset_upserts.rs +++ b/program_transformers/src/asset_upserts.rs @@ -30,6 +30,7 @@ pub async fn upsert_assets_token_account_columns, pub supply: Decimal, - pub supply_mint: Option>, - pub slot_updated_mint_account: u64, + pub decimals: u8, + pub slot_updated_mint_account: i64, + pub extensions: Option, } pub async fn upsert_assets_mint_account_columns( columns: AssetMintAccountColumns, txn_or_conn: &T, ) -> Result<(), DbErr> { - let owner_type = if columns.supply == Decimal::from(1) { - OwnerType::Single - } else { - OwnerType::Token - }; + let (specification_asset_class, owner_type) = + if columns.supply == Decimal::from(1) && columns.decimals == 0 { + (SpecificationAssetClass::Nft, OwnerType::Single) + } else { + (SpecificationAssetClass::FungibleToken, OwnerType::Token) + }; let active_model = asset::ActiveModel { - id: Set(columns.mint), + id: Set(columns.mint.clone()), supply: Set(columns.supply), - supply_mint: Set(columns.supply_mint), - slot_updated_mint_account: Set(Some(columns.slot_updated_mint_account as i64)), + supply_mint: Set(Some(columns.mint.clone())), + slot_updated_mint_account: Set(Some(columns.slot_updated_mint_account)), + slot_updated: Set(Some(columns.slot_updated_mint_account)), + mint_extensions: Set(columns.extensions), + asset_data: Set(Some(columns.mint.clone())), + // assume every token is a fungible token when mint account is created + specification_asset_class: Set(Some(specification_asset_class)), + // // assume multiple ownership as we set asset class to fungible token owner_type: Set(owner_type), ..Default::default() }; @@ -145,6 +154,8 @@ pub async fn upsert_assets_mint_account_columns( txn: &T, id: Vec, - creators: &Vec, + creators: &[Creator], slot_updated: i64, seq: i64, ) -> ProgramTransformerResult<()> diff --git a/program_transformers/src/lib.rs b/program_transformers/src/lib.rs index 0b75b5180..bd2eafa46 100644 --- a/program_transformers/src/lib.rs +++ b/program_transformers/src/lib.rs @@ -4,6 +4,7 @@ use { error::{ProgramTransformerError, ProgramTransformerResult}, mpl_core_program::handle_mpl_core_account, token::handle_token_program_account, + token_inscription::handle_token_inscription_program_update, token_metadata::handle_token_metadata_account, }, blockbuster::{ @@ -11,7 +12,8 @@ use { program_handler::ProgramParser, programs::{ bubblegum::BubblegumParser, mpl_core_program::MplCoreParser, - token_account::TokenAccountParser, token_metadata::TokenMetadataParser, + token_account::TokenAccountParser, token_extensions::Token2022AccountParser, + token_inscriptions::TokenInscriptionParser, token_metadata::TokenMetadataParser, ProgramParseResult, }, }, @@ -21,12 +23,14 @@ use { TransactionTrait, }, serde::Deserialize, + serde_json::{Map, Value}, solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey, signature::Signature}, solana_transaction_status::InnerInstructions, sqlx::PgPool, std::collections::{HashMap, HashSet, VecDeque}, + token_extensions::handle_token_extensions_program_account, tokio::time::{sleep, Duration}, - tracing::{debug, error, info}, + tracing::{debug, error}, }; mod asset_upserts; @@ -34,6 +38,8 @@ pub mod bubblegum; pub mod error; mod mpl_core_program; mod token; +mod token_extensions; +mod token_inscription; mod token_metadata; #[derive(Debug, Clone, PartialEq, Eq, Deserialize)] @@ -62,15 +68,19 @@ pub struct ProgramTransformer { impl ProgramTransformer { pub fn new(pool: PgPool, download_metadata_notifier: DownloadMetadataNotifier) -> Self { - let mut parsers: HashMap> = HashMap::with_capacity(4); + let mut parsers: HashMap> = HashMap::with_capacity(6); let bgum = BubblegumParser {}; let token_metadata = TokenMetadataParser {}; let token = TokenAccountParser {}; let mpl_core = MplCoreParser {}; + let token_extensions = Token2022AccountParser {}; + let token_inscription = TokenInscriptionParser {}; parsers.insert(bgum.key(), Box::new(bgum)); parsers.insert(token_metadata.key(), Box::new(token_metadata)); parsers.insert(token.key(), Box::new(token)); parsers.insert(mpl_core.key(), Box::new(mpl_core)); + parsers.insert(token_extensions.key(), Box::new(token_extensions)); + parsers.insert(token_inscription.key(), Box::new(token_inscription)); let hs = parsers.iter().fold(HashSet::new(), |mut acc, (k, _)| { acc.insert(*k); acc @@ -104,7 +114,6 @@ impl ProgramTransformer { &self, tx_info: &TransactionInfo, ) -> ProgramTransformerResult<()> { - info!("Handling Transaction: {:?}", tx_info.signature); let instructions = self.break_transaction(tx_info); let mut not_impl = 0; let ixlen = instructions.len(); @@ -212,6 +221,9 @@ impl ProgramTransformer { ) .await } + ProgramParseResult::TokenExtensionsProgramAccount(parsing_result) => { + handle_token_extensions_program_account(account_info, parsing_result, &db).await + } ProgramParseResult::MplCore(parsing_result) => { handle_mpl_core_account( account_info, @@ -221,6 +233,9 @@ impl ProgramTransformer { ) .await } + ProgramParseResult::TokenInscriptionAccount(parsing_result) => { + handle_token_inscription_program_update(account_info, parsing_result, &db).await + } _ => Err(ProgramTransformerError::NotImplemented), }?; } @@ -260,3 +275,27 @@ fn record_metric(metric_name: &str, success: bool, retries: u32) { cadence_macros::statsd_count!(metric_name, 1, "success" => success, "retry_count" => retry_count); } } + +pub fn filter_non_null_fields(value: Value) -> Option { + match value { + Value::Null => None, + Value::Object(map) => { + if map.values().all(|v| matches!(v, Value::Null)) { + None + } else { + let filtered_map: Map = map + .into_iter() + .filter(|(_k, v)| !matches!(v, Value::Null)) + .map(|(k, v)| (k.clone(), v.clone())) + .collect(); + + if filtered_map.is_empty() { + None + } else { + Some(Value::Object(filtered_map)) + } + } + } + _ => Some(value), + } +} diff --git a/program_transformers/src/mpl_core_program/v1_asset.rs b/program_transformers/src/mpl_core_program/v1_asset.rs index 211f6dd72..c22af75d2 100644 --- a/program_transformers/src/mpl_core_program/v1_asset.rs +++ b/program_transformers/src/mpl_core_program/v1_asset.rs @@ -388,6 +388,7 @@ pub async fn save_v1_asset( None }; + // Get transfer delegate from `TransferDelegate` plugin if available. let transfer_delegate = asset .plugins @@ -413,7 +414,6 @@ pub async fn save_v1_asset( let asset_model = asset::ActiveModel { id: ActiveValue::Set(id_vec.clone()), - owner_type: ActiveValue::Set(ownership_type), supply: ActiveValue::Set(Decimal::from(1)), supply_mint: ActiveValue::Set(None), slot_updated_mint_account: ActiveValue::Set(Some(slot as i64)), @@ -432,6 +432,7 @@ pub async fn save_v1_asset( mpl_core_external_plugins: ActiveValue::Set(Some(external_plugins_json)), mpl_core_unknown_external_plugins: ActiveValue::Set(unknown_external_plugins_json), owner: ActiveValue::Set(owner), + owner_type: ActiveValue::Set(ownership_type), frozen: ActiveValue::Set(frozen), delegate: ActiveValue::Set(transfer_delegate.clone()), slot_updated_token_account: ActiveValue::Set(Some(slot_i)), diff --git a/program_transformers/src/token/mod.rs b/program_transformers/src/token/mod.rs index f712c149c..8df8586bd 100644 --- a/program_transformers/src/token/mod.rs +++ b/program_transformers/src/token/mod.rs @@ -8,11 +8,13 @@ use { AccountInfo, DownloadMetadataNotifier, }, blockbuster::programs::token_account::TokenProgramAccount, - digital_asset_types::dao::{token_accounts, tokens}, + digital_asset_types::dao::{ + token_accounts, + tokens::{self}, + }, sea_orm::{ entity::ActiveValue, - sea_query::query::OnConflict, - sea_query::{Alias, Condition, Expr}, + sea_query::{query::OnConflict, Alias, Condition, Expr}, ConnectionTrait, DatabaseConnection, EntityTrait, Statement, TransactionTrait, }, solana_sdk::program_option::COption, @@ -27,6 +29,7 @@ pub async fn handle_token_program_account<'a, 'b>( ) -> ProgramTransformerResult<()> { let account_key = account_info.pubkey.to_bytes().to_vec(); let account_owner = account_info.owner.to_bytes().to_vec(); + let slot = account_info.slot as i64; match &parsing_result { TokenProgramAccount::TokenAccount(ta) => { let mint = ta.mint.to_bytes().to_vec(); @@ -44,9 +47,10 @@ pub async fn handle_token_program_account<'a, 'b>( frozen: ActiveValue::Set(frozen), delegated_amount: ActiveValue::Set(ta.delegated_amount as i64), token_program: ActiveValue::Set(account_owner.clone()), - slot_updated: ActiveValue::Set(account_info.slot as i64), + slot_updated: ActiveValue::Set(slot), amount: ActiveValue::Set(ta.amount as i64), close_authority: ActiveValue::Set(None), + extensions: ActiveValue::Set(None), }; let txn = db.begin().await?; @@ -176,21 +180,20 @@ pub async fn handle_token_program_account<'a, 'b>( .exec_without_returning(&txn) .await?; - if ta.amount == 1 { - upsert_assets_token_account_columns( - AssetTokenAccountColumns { - mint: mint.clone(), - owner: Some(owner.clone()), - frozen, - delegate, - slot_updated_token_account: Some(account_info.slot as i64), - }, - &txn, - ) - .await?; - } + upsert_assets_token_account_columns( + AssetTokenAccountColumns { + mint: mint.clone(), + owner: Some(owner.clone()), + frozen, + delegate, + slot_updated_token_account: Some(slot), + }, + &txn, + ) + .await?; txn.commit().await?; + Ok(()) } TokenProgramAccount::Mint(m) => { @@ -202,6 +205,7 @@ pub async fn handle_token_program_account<'a, 'b>( COption::Some(d) => Some(d.to_bytes().to_vec()), COption::None => None, }; + let model = tokens::ActiveModel { mint: ActiveValue::Set(account_key.clone()), token_program: ActiveValue::Set(account_owner), @@ -212,6 +216,7 @@ pub async fn handle_token_program_account<'a, 'b>( extension_data: ActiveValue::Set(None), mint_authority: ActiveValue::Set(mint_auth), freeze_authority: ActiveValue::Set(freeze_auth), + extensions: ActiveValue::Set(None), }; let txn = db.begin().await?; @@ -307,16 +312,16 @@ pub async fn handle_token_program_account<'a, 'b>( upsert_assets_mint_account_columns( AssetMintAccountColumns { mint: account_key.clone(), - supply_mint: Some(account_key), supply: m.supply.into(), - slot_updated_mint_account: account_info.slot, + decimals: m.decimals, + slot_updated_mint_account: slot, + extensions: None, }, &txn, ) .await?; txn.commit().await?; - Ok(()) } } diff --git a/program_transformers/src/token_extensions/mod.rs b/program_transformers/src/token_extensions/mod.rs new file mode 100644 index 000000000..5d1605e07 --- /dev/null +++ b/program_transformers/src/token_extensions/mod.rs @@ -0,0 +1,297 @@ +use { + crate::{ + asset_upserts::{ + upsert_assets_mint_account_columns, upsert_assets_token_account_columns, + AssetMintAccountColumns, AssetTokenAccountColumns, + }, + error::{ProgramTransformerError, ProgramTransformerResult}, + filter_non_null_fields, AccountInfo, + }, + blockbuster::programs::token_extensions::{ + extension::ShadowMetadata, MintAccount, TokenAccount, TokenExtensionsProgramAccount, + }, + digital_asset_types::dao::{ + asset, asset_data, + sea_orm_active_enums::ChainMutability, + token_accounts, + tokens::{self, IsNonFungible as IsNonFungibleModel}, + }, + sea_orm::{ + entity::ActiveValue, query::QueryTrait, sea_query::query::OnConflict, ConnectionTrait, + DatabaseConnection, DatabaseTransaction, DbBackend, DbErr, EntityTrait, Set, + TransactionTrait, + }, + serde_json::Value, + solana_sdk::program_option::COption, + spl_token_2022::state::AccountState, +}; + +pub async fn handle_token_extensions_program_account<'a, 'b, 'c>( + account_info: &'a AccountInfo, + parsing_result: &'b TokenExtensionsProgramAccount, + db: &'c DatabaseConnection, +) -> ProgramTransformerResult<()> { + let account_key = account_info.pubkey.to_bytes().to_vec(); + let account_owner = account_info.owner.to_bytes().to_vec(); + let slot = account_info.slot as i64; + match parsing_result { + TokenExtensionsProgramAccount::TokenAccount(ta) => { + let TokenAccount { + account, + extensions, + } = ta; + let ta = account; + + let extensions: Option = if extensions.is_some() { + filter_non_null_fields( + serde_json::to_value(extensions.clone()) + .map_err(|e| ProgramTransformerError::SerializatonError(e.to_string()))?, + ) + } else { + None + }; + + let mint = ta.mint.to_bytes().to_vec(); + let delegate: Option> = match ta.delegate { + COption::Some(d) => Some(d.to_bytes().to_vec()), + COption::None => None, + }; + let frozen = matches!(ta.state, AccountState::Frozen); + let owner = ta.owner.to_bytes().to_vec(); + let model = token_accounts::ActiveModel { + pubkey: ActiveValue::Set(account_key.clone()), + mint: ActiveValue::Set(mint.clone()), + delegate: ActiveValue::Set(delegate.clone()), + owner: ActiveValue::Set(owner.clone()), + frozen: ActiveValue::Set(frozen), + delegated_amount: ActiveValue::Set(ta.delegated_amount as i64), + token_program: ActiveValue::Set(account_owner.clone()), + slot_updated: ActiveValue::Set(slot), + amount: ActiveValue::Set(ta.amount as i64), + close_authority: ActiveValue::Set(None), + extensions: ActiveValue::Set(extensions.clone()), + }; + let txn = db.begin().await?; + + let mut query = token_accounts::Entity::insert(model) + .on_conflict( + OnConflict::columns([token_accounts::Column::Pubkey]) + .update_columns([ + token_accounts::Column::Mint, + token_accounts::Column::DelegatedAmount, + token_accounts::Column::Delegate, + token_accounts::Column::Amount, + token_accounts::Column::Frozen, + token_accounts::Column::TokenProgram, + token_accounts::Column::Owner, + token_accounts::Column::CloseAuthority, + token_accounts::Column::SlotUpdated, + token_accounts::Column::Extensions, + ]) + .to_owned(), + ) + .build(DbBackend::Postgres); + query.sql = format!( + "{} WHERE excluded.slot_updated > token_accounts.slot_updated", + query.sql + ); + db.execute(query).await?; + + let token = tokens::Entity::find_by_id(mint.clone()).one(db).await?; + + let is_non_fungible = token.map(|t| t.is_non_fungible()).unwrap_or(false); + + if is_non_fungible { + upsert_assets_token_account_columns( + AssetTokenAccountColumns { + mint: mint.clone(), + owner: Some(owner.clone()), + frozen, + delegate, + slot_updated_token_account: Some(slot), + }, + &txn, + ) + .await?; + } + + txn.commit().await?; + + Ok(()) + } + TokenExtensionsProgramAccount::MintAccount(m) => { + let MintAccount { + account, + extensions, + } = m; + + let mint_extensions: Option = if extensions.is_some() { + filter_non_null_fields( + serde_json::to_value(extensions.clone()) + .map_err(|e| ProgramTransformerError::SerializatonError(e.to_string()))?, + ) + } else { + None + }; + + let m = account; + let freeze_auth: Option> = match m.freeze_authority { + COption::Some(d) => Some(d.to_bytes().to_vec()), + COption::None => None, + }; + let mint_auth: Option> = match m.mint_authority { + COption::Some(d) => Some(d.to_bytes().to_vec()), + COption::None => None, + }; + let model = tokens::ActiveModel { + mint: ActiveValue::Set(account_key.clone()), + token_program: ActiveValue::Set(account_owner), + slot_updated: ActiveValue::Set(slot), + supply: ActiveValue::Set(m.supply.into()), + decimals: ActiveValue::Set(m.decimals as i32), + close_authority: ActiveValue::Set(None), + extension_data: ActiveValue::Set(None), + mint_authority: ActiveValue::Set(mint_auth), + freeze_authority: ActiveValue::Set(freeze_auth), + extensions: ActiveValue::Set(mint_extensions.clone()), + }; + let txn: DatabaseTransaction = db.begin().await?; + + let mut query = tokens::Entity::insert(model) + .on_conflict( + OnConflict::columns([tokens::Column::Mint]) + .update_columns([ + tokens::Column::Supply, + tokens::Column::TokenProgram, + tokens::Column::MintAuthority, + tokens::Column::CloseAuthority, + tokens::Column::ExtensionData, + tokens::Column::SlotUpdated, + tokens::Column::Decimals, + tokens::Column::FreezeAuthority, + tokens::Column::Extensions, + ]) + .to_owned(), + ) + .build(DbBackend::Postgres); + query.sql = format!( + "{} WHERE excluded.slot_updated >= tokens.slot_updated", + query.sql + ); + txn.execute(query).await?; + + upsert_assets_mint_account_columns( + AssetMintAccountColumns { + mint: account_key.clone(), + supply: m.supply.into(), + decimals: m.decimals, + slot_updated_mint_account: slot, + extensions: mint_extensions.clone(), + }, + &txn, + ) + .await?; + + if let Some(metadata) = &extensions.metadata { + upsert_asset_data(metadata, account_key.clone(), slot, &txn).await?; + } + + txn.commit().await?; + + Ok(()) + } + _ => Err(ProgramTransformerError::NotImplemented), + } +} + +async fn upsert_asset_data( + metadata: &ShadowMetadata, + key_bytes: Vec, + slot: i64, + db: &T, +) -> ProgramTransformerResult<()> +where + T: ConnectionTrait + TransactionTrait, +{ + let metadata_json = serde_json::to_value(metadata.clone()) + .map_err(|e| ProgramTransformerError::SerializatonError(e.to_string()))?; + let asset_data_model = asset_data::ActiveModel { + metadata_url: ActiveValue::Set(metadata.uri.clone()), + metadata: ActiveValue::Set(Value::String("processing".to_string())), + id: ActiveValue::Set(key_bytes.clone()), + chain_data_mutability: ActiveValue::Set(ChainMutability::Mutable), + chain_data: ActiveValue::Set(metadata_json), + slot_updated: ActiveValue::Set(slot), + base_info_seq: ActiveValue::Set(Some(0)), + raw_name: ActiveValue::Set(Some(metadata.name.clone().into_bytes().to_vec())), + raw_symbol: ActiveValue::Set(Some(metadata.symbol.clone().into_bytes().to_vec())), + ..Default::default() + }; + let mut asset_data_query = asset_data::Entity::insert(asset_data_model) + .on_conflict( + OnConflict::columns([asset_data::Column::Id]) + .update_columns([ + asset_data::Column::ChainDataMutability, + asset_data::Column::ChainData, + asset_data::Column::MetadataUrl, + asset_data::Column::SlotUpdated, + asset_data::Column::BaseInfoSeq, + asset_data::Column::RawName, + asset_data::Column::RawSymbol, + ]) + .to_owned(), + ) + .build(DbBackend::Postgres); + asset_data_query.sql = format!( + "{} WHERE excluded.slot_updated >= asset_data.slot_updated", + asset_data_query.sql + ); + db.execute(asset_data_query).await?; + + upsert_assets_metadata_cols( + AssetMetadataAccountCols { + mint: key_bytes.clone(), + slot_updated_metadata_account: slot, + }, + db, + ) + .await?; + + Ok(()) +} + +struct AssetMetadataAccountCols { + mint: Vec, + slot_updated_metadata_account: i64, +} + +async fn upsert_assets_metadata_cols( + metadata: AssetMetadataAccountCols, + db: &T, +) -> Result<(), DbErr> +where + T: ConnectionTrait + TransactionTrait, +{ + let asset = asset::ActiveModel { + id: ActiveValue::Set(metadata.mint.clone()), + slot_updated_metadata_account: Set(Some(metadata.slot_updated_metadata_account)), + ..Default::default() + }; + + let mut asset_query = asset::Entity::insert(asset) + .on_conflict( + OnConflict::columns([asset::Column::Id]) + .update_columns([asset::Column::SlotUpdatedMetadataAccount]) + .to_owned(), + ) + .build(DbBackend::Postgres); + + asset_query.sql = format!( + "{} WHERE excluded.slot_updated_metadata_account >= asset.slot_updated_metadata_account OR asset.slot_updated_metadata_account IS NULL", + asset_query.sql + ); + + db.execute(asset_query).await?; + + Ok(()) +} diff --git a/program_transformers/src/token_inscription/mod.rs b/program_transformers/src/token_inscription/mod.rs new file mode 100644 index 000000000..957275fc8 --- /dev/null +++ b/program_transformers/src/token_inscription/mod.rs @@ -0,0 +1,59 @@ +use std::str::FromStr; + +use crate::AccountInfo; +use blockbuster::programs::token_inscriptions::TokenInscriptionAccount; +use digital_asset_types::dao::asset_v1_account_attachments; +use digital_asset_types::dao::sea_orm_active_enums::V1AccountAttachments; +use sea_orm::sea_query::OnConflict; +use sea_orm::{ + ActiveValue, ConnectionTrait, DatabaseConnection, DbBackend, EntityTrait, QueryTrait, +}; +use solana_sdk::pubkey::Pubkey; + +use crate::error::{ProgramTransformerError, ProgramTransformerResult}; + +pub async fn handle_token_inscription_program_update<'a, 'b>( + account_info: &AccountInfo, + parsing_result: &'a TokenInscriptionAccount, + db: &'b DatabaseConnection, +) -> ProgramTransformerResult<()> { + let account_key = account_info.pubkey.to_bytes().to_vec(); + + let TokenInscriptionAccount { data } = parsing_result; + + let ser = serde_json::to_value(data) + .map_err(|e| ProgramTransformerError::SerializatonError(e.to_string()))?; + + let asset_id = Pubkey::from_str(&data.root) + .map_err(|e| ProgramTransformerError::ParsingError(e.to_string()))? + .to_bytes() + .to_vec(); + + let model = asset_v1_account_attachments::ActiveModel { + id: ActiveValue::Set(account_key), + asset_id: ActiveValue::Set(Some(asset_id)), + data: ActiveValue::Set(Some(ser)), + slot_updated: ActiveValue::Set(account_info.slot as i64), + initialized: ActiveValue::Set(true), + attachment_type: ActiveValue::Set(V1AccountAttachments::TokenInscription), + }; + + let mut query = asset_v1_account_attachments::Entity::insert(model) + .on_conflict( + OnConflict::columns([asset_v1_account_attachments::Column::Id]) + .update_columns([ + asset_v1_account_attachments::Column::Data, + asset_v1_account_attachments::Column::SlotUpdated, + ]) + .to_owned(), + ) + .build(DbBackend::Postgres); + + query.sql = format!( + "{} WHERE excluded.slot_updated > asset_v1_account_attachments.slot_updated", + query.sql + ); + db.execute(query).await?; + + Ok(()) +} diff --git a/program_transformers/src/token_metadata/master_edition.rs b/program_transformers/src/token_metadata/master_edition.rs index df8872dfd..7286f148d 100644 --- a/program_transformers/src/token_metadata/master_edition.rs +++ b/program_transformers/src/token_metadata/master_edition.rs @@ -1,7 +1,7 @@ use { crate::error::{ProgramTransformerError, ProgramTransformerResult}, blockbuster::token_metadata::{ - accounts::{DeprecatedMasterEditionV1, MasterEdition}, + accounts::{DeprecatedMasterEditionV1, Edition, MasterEdition}, types::Key, }, digital_asset_types::dao::{ @@ -121,3 +121,81 @@ pub async fn save_master_edition( Ok(()) } + +pub async fn save_edition( + id: Pubkey, + slot: u64, + e_data: &Edition, + txn: &DatabaseTransaction, +) -> ProgramTransformerResult<()> { + let id_bytes = id.to_bytes().to_vec(); + + let ser = serde_json::to_value(e_data) + .map_err(|e| ProgramTransformerError::SerializatonError(e.to_string()))?; + + let model = asset_v1_account_attachments::ActiveModel { + id: ActiveValue::Set(id_bytes), + attachment_type: ActiveValue::Set(V1AccountAttachments::Edition), + data: ActiveValue::Set(Some(ser)), + slot_updated: ActiveValue::Set(slot as i64), + ..Default::default() + }; + + asset_v1_account_attachments::Entity::insert(model) + .on_conflict( + OnConflict::columns([asset_v1_account_attachments::Column::Id]) + .update_columns([ + asset_v1_account_attachments::Column::AttachmentType, + asset_v1_account_attachments::Column::Data, + asset_v1_account_attachments::Column::SlotUpdated, + ]) + .action_cond_where( + Condition::all() + .add( + Condition::any() + .add( + Expr::tbl( + Alias::new("excluded"), + asset_v1_account_attachments::Column::AttachmentType, + ) + .ne(Expr::tbl( + asset_v1_account_attachments::Entity, + asset_v1_account_attachments::Column::AttachmentType, + )), + ) + .add( + Expr::tbl( + Alias::new("excluded"), + asset_v1_account_attachments::Column::Data, + ) + .ne(Expr::tbl( + asset_v1_account_attachments::Entity, + asset_v1_account_attachments::Column::Data, + )), + ), + ) + .add( + Condition::any() + .add( + Expr::tbl( + asset_v1_account_attachments::Entity, + asset_v1_account_attachments::Column::SlotUpdated, + ) + .lte(slot as i64), + ) + .add( + Expr::tbl( + asset_v1_account_attachments::Entity, + asset_v1_account_attachments::Column::SlotUpdated, + ) + .is_null(), + ), + ), + ) + .to_owned(), + ) + .exec_without_returning(txn) + .await?; + + Ok(()) +} diff --git a/program_transformers/src/token_metadata/mod.rs b/program_transformers/src/token_metadata/mod.rs index cbeb94171..0080303ed 100644 --- a/program_transformers/src/token_metadata/mod.rs +++ b/program_transformers/src/token_metadata/mod.rs @@ -7,7 +7,11 @@ use { }, AccountInfo, DownloadMetadataNotifier, }, - blockbuster::programs::token_metadata::{TokenMetadataAccountData, TokenMetadataAccountState}, + blockbuster::{ + programs::token_metadata::{TokenMetadataAccountData, TokenMetadataAccountState}, + token_metadata::types::TokenStandard, + }, + master_edition::save_edition, sea_orm::{DatabaseConnection, TransactionTrait}, }; @@ -45,9 +49,32 @@ pub async fn handle_token_metadata_account<'a, 'b>( txn.commit().await?; Ok(()) } + TokenMetadataAccountData::EditionV1(e) => { + let txn = db.begin().await?; + save_edition(account_info.pubkey, account_info.slot, e, &txn).await?; + txn.commit().await?; + Ok(()) + } + // TokenMetadataAccountData::EditionMarker(_) => {} // TokenMetadataAccountData::UseAuthorityRecord(_) => {} // TokenMetadataAccountData::CollectionAuthorityRecord(_) => {} _ => Err(ProgramTransformerError::NotImplemented), } } + +pub trait IsNonFungibe { + fn is_non_fungible(&self) -> bool; +} + +impl IsNonFungibe for TokenStandard { + fn is_non_fungible(&self) -> bool { + matches!( + self, + TokenStandard::NonFungible + | TokenStandard::NonFungibleEdition + | TokenStandard::ProgrammableNonFungible + | TokenStandard::ProgrammableNonFungibleEdition + ) + } +} diff --git a/program_transformers/src/token_metadata/v1_asset.rs b/program_transformers/src/token_metadata/v1_asset.rs index d500ac2cd..43208c623 100644 --- a/program_transformers/src/token_metadata/v1_asset.rs +++ b/program_transformers/src/token_metadata/v1_asset.rs @@ -1,4 +1,5 @@ use { + super::IsNonFungibe, crate::{ asset_upserts::{upsert_assets_metadata_account_columns, AssetMetadataAccountColumns}, error::{ProgramTransformerError, ProgramTransformerResult}, @@ -26,6 +27,7 @@ use { Condition, ConnectionTrait, Statement, TransactionTrait, }, solana_sdk::pubkey, + solana_sdk::pubkey::Pubkey, tracing::warn, }; @@ -63,7 +65,6 @@ pub async fn burn_v1_asset( } static WSOL_PUBKEY: pubkey::Pubkey = pubkey!("So11111111111111111111111111111111111111112"); - pub async fn save_v1_asset( conn: &T, metadata: &Metadata, @@ -479,6 +480,12 @@ pub async fn save_v1_asset( .await .map_err(|db_err| ProgramTransformerError::AssetIndexError(db_err.to_string()))?; } + + // If the asset is a non-fungible token, then we need to insert to the asset_v1_account_attachments table + if let Some(true) = metadata.token_standard.map(|t| t.is_non_fungible()) { + upsert_asset_v1_account_attachments(&txn, &mint_pubkey, slot).await?; + } + txn.commit().await?; if uri.is_empty() { @@ -495,3 +502,39 @@ pub async fn save_v1_asset( slot_i, ))) } + +async fn upsert_asset_v1_account_attachments( + conn: &T, + mint_pubkey: &Pubkey, + slot: u64, +) -> ProgramTransformerResult<()> { + let edition_pubkey = MasterEdition::find_pda(mint_pubkey).0; + let mint_pubkey_vec = mint_pubkey.to_bytes().to_vec(); + let attachment = asset_v1_account_attachments::ActiveModel { + id: ActiveValue::Set(edition_pubkey.to_bytes().to_vec()), + asset_id: ActiveValue::Set(Some(mint_pubkey_vec.clone())), + slot_updated: ActiveValue::Set(slot as i64), + // by default, the attachment type is MasterEditionV2 + attachment_type: ActiveValue::Set(V1AccountAttachments::MasterEditionV2), + ..Default::default() + }; + + asset_v1_account_attachments::Entity::insert(attachment) + .on_conflict( + OnConflict::columns([asset_v1_account_attachments::Column::Id]) + .update_columns([asset_v1_account_attachments::Column::AssetId]) + .action_cond_where( + Expr::tbl( + asset_v1_account_attachments::Entity, + asset_v1_account_attachments::Column::SlotUpdated, + ) + .lte(slot as i64), + ) + .to_owned(), + ) + .exec_without_returning(conn) + .await + .map_err(|db_err| ProgramTransformerError::AssetIndexError(db_err.to_string()))?; + + Ok(()) +} diff --git a/solana-test-validator-geyser-config/accountsdb-plugin-config.json b/solana-test-validator-geyser-config/accountsdb-plugin-config.json index 3925d085a..34a6adce8 100644 --- a/solana-test-validator-geyser-config/accountsdb-plugin-config.json +++ b/solana-test-validator-geyser-config/accountsdb-plugin-config.json @@ -4,19 +4,18 @@ "metrics_port": 8125, "metrics_uri": "graphite", "env": "dev", - "accounts_selector" : { - "owners" : [ + "accounts_selector": { + "owners": [ "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s", "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", "BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY", - "CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d" + "CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d", + "inscokhJarcjaEs59QbQ7hYjrKz25LEPRfCbP8EmdUp" ] }, - "transaction_selector" : { - "mentions" : [ - "BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY" - ] + "transaction_selector": { + "mentions": ["BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY"] } }