diff --git a/Cargo.lock b/Cargo.lock index f414e26..e0d30e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,8 +123,8 @@ checksum = "cf7d535e1381be3de2c0716c0a1c1e32ad9df1042cddcf7bc18d743569e53319" dependencies = [ "anchor-syn", "anyhow", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "regex", "syn 1.0.109", ] @@ -138,8 +138,8 @@ dependencies = [ "anchor-syn", "anyhow", "bs58 0.4.0", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "rustversion", "syn 1.0.109", ] @@ -151,7 +151,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1be64a48e395fe00b8217287f226078be2cf32dae42fdf8a885b997945c3d28" dependencies = [ "anchor-syn", - "proc-macro2 1.0.60", + "proc-macro2 1.0.63", "syn 1.0.109", ] @@ -162,8 +162,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38ea6713d1938c0da03656ff8a693b17dc0396da66d1ba320557f07e86eca0d4" dependencies = [ "anchor-syn", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -175,8 +175,8 @@ checksum = "d401f11efb3644285685f8339829a9786d43ed7490bb1699f33c478d04d5a582" dependencies = [ "anchor-syn", "anyhow", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -189,8 +189,8 @@ dependencies = [ "anchor-syn", "anyhow", "heck 0.3.3", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -202,8 +202,8 @@ checksum = "6ad769993b5266714e8939e47fbdede90e5c030333c7522d99a4d4748cf26712" dependencies = [ "anchor-syn", "anyhow", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -215,8 +215,8 @@ checksum = "4e677fae4a016a554acdd0e3b7f178d3acafaa7e7ffac6b8690cf4e171f1c116" dependencies = [ "anchor-syn", "anyhow", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -228,8 +228,8 @@ checksum = "340beef6809d1c3fcc7ae219153d981e95a8a277ff31985bd7050e32645dc9a8" dependencies = [ "anchor-syn", "anyhow", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -266,9 +266,9 @@ dependencies = [ "anyhow", "bs58 0.3.1", "heck 0.3.3", - "proc-macro2 1.0.60", + "proc-macro2 1.0.63", "proc-macro2-diagnostics", - "quote 1.0.28", + "quote 1.0.29", "serde", "serde_json", "sha2 0.9.9", @@ -392,8 +392,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", "synstructure", ] @@ -404,8 +404,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -421,7 +421,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" dependencies = [ - "quote 1.0.28", + "quote 1.0.29", "syn 1.0.109", ] @@ -562,9 +562,9 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", - "syn 2.0.18", + "proc-macro2 1.0.63", + "quote 1.0.29", + "syn 2.0.23", ] [[package]] @@ -575,13 +575,13 @@ checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", - "syn 2.0.18", + "proc-macro2 1.0.63", + "quote 1.0.29", + "syn 2.0.23", ] [[package]] @@ -707,8 +707,8 @@ checksum = "33b8de67cc41132507eeece2584804efcb15f85ba516e34c944b7667f480397a" dependencies = [ "heck 0.3.3", "proc-macro-error", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -736,6 +736,15 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +dependencies = [ + "serde", +] + [[package]] name = "bigdecimal" version = "0.3.1" @@ -866,7 +875,7 @@ dependencies = [ "borsh-derive-internal 0.9.3", "borsh-schema-derive-internal 0.9.3", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.60", + "proc-macro2 1.0.63", "syn 1.0.109", ] @@ -879,7 +888,7 @@ dependencies = [ "borsh-derive-internal 0.10.3", "borsh-schema-derive-internal 0.10.3", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.60", + "proc-macro2 1.0.63", "syn 1.0.109", ] @@ -889,8 +898,8 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -900,8 +909,8 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -911,8 +920,8 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -922,8 +931,8 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -1002,8 +1011,8 @@ version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -1022,9 +1031,9 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdde5c9cd29ebd706ce1b35600920a33550e402fc998a2e53ad3b42c3c47a192" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", - "syn 2.0.18", + "proc-macro2 1.0.63", + "quote 1.0.29", + "syn 2.0.23", ] [[package]] @@ -1164,8 +1173,8 @@ checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -1176,9 +1185,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.60", - "quote 1.0.28", - "syn 2.0.18", + "proc-macro2 1.0.63", + "quote 1.0.29", + "syn 2.0.23", ] [[package]] @@ -1425,10 +1434,10 @@ checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "strsim 0.10.0", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -1438,8 +1447,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" dependencies = [ "darling_core", - "quote 1.0.28", - "syn 2.0.18", + "quote 1.0.29", + "syn 2.0.23", ] [[package]] @@ -1569,9 +1578,9 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", - "syn 2.0.18", + "proc-macro2 1.0.63", + "quote 1.0.29", + "syn 2.0.23", ] [[package]] @@ -1698,8 +1707,8 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8958699f9359f0b04e691a13850d48b7de329138023876d07cbd024c2c820598" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -1710,8 +1719,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11f36e95862220b211a6e2aa5eca09b4fa391b13cd52ceb8035a24bf65a79de2" dependencies = [ "once_cell", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -1902,9 +1911,9 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", - "syn 2.0.18", + "proc-macro2 1.0.63", + "quote 1.0.29", + "syn 2.0.23", ] [[package]] @@ -2226,9 +2235,11 @@ version = "0.1.0" dependencies = [ "anchor-lang", "bincode", + "bs58 0.5.0", "holaplex-hub-core", "holaplex-hub-nfts-solana-core", "holaplex-hub-nfts-solana-entity", + "jsonrpsee", "mpl-bubblegum", "mpl-token-metadata", "serde", @@ -2345,7 +2356,9 @@ checksum = "0646026eb1b3eea4cd9ba47912ea5ce9cc07713d105b1a14698f4e6433d348b7" dependencies = [ "http", "hyper", + "log", "rustls 0.21.1", + "rustls-native-certs", "tokio", "tokio-rustls 0.24.1", ] @@ -2548,6 +2561,84 @@ dependencies = [ "serde_json", ] +[[package]] +name = "jsonrpsee" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1822d18e4384a5e79d94dc9e4d1239cfa9fad24e55b44d2efeff5b394c9fece4" +dependencies = [ + "jsonrpsee-core", + "jsonrpsee-http-client", + "jsonrpsee-proc-macros", + "jsonrpsee-types", + "tracing", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c6832a55f662b5a6ecc844db24b8b9c387453f923de863062c60ce33d62b81" +dependencies = [ + "anyhow", + "async-trait", + "beef", + "futures-util", + "hyper", + "jsonrpsee-types", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1705c65069729e3dccff6fd91ee431d5d31cabcf00ce68a62a2c6435ac713af9" +dependencies = [ + "async-trait", + "hyper", + "hyper-rustls", + "jsonrpsee-core", + "jsonrpsee-types", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower", + "tracing", +] + +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6027ac0b197ce9543097d02a290f550ce1d9432bf301524b013053c0b75cc94" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 1.3.1", + "proc-macro2 1.0.63", + "quote 1.0.29", + "syn 1.0.109", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5bf6c75ce2a4217421154adfc65a24d2b46e77286e59bba5d9fa6544ccc8f4" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", +] + [[package]] name = "keccak" version = "0.1.4" @@ -2875,7 +2966,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12989bc45715b0ee91944855130131479f9c772e198a910c3eb0ea327d5bffc3" dependencies = [ - "quote 1.0.28", + "quote 1.0.29", "syn 1.0.109", ] @@ -2999,8 +3090,8 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -3072,8 +3163,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -3134,9 +3225,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", - "syn 2.0.18", + "proc-macro2 1.0.63", + "quote 1.0.29", + "syn 2.0.23", ] [[package]] @@ -3191,8 +3282,8 @@ checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" dependencies = [ "Inflector", "proc-macro-error", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -3329,9 +3420,9 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", - "syn 2.0.18", + "proc-macro2 1.0.63", + "quote 1.0.29", + "syn 2.0.23", ] [[package]] @@ -3403,7 +3494,7 @@ version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" dependencies = [ - "proc-macro2 1.0.60", + "proc-macro2 1.0.63", "syn 1.0.109", ] @@ -3433,8 +3524,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", "version_check", ] @@ -3445,8 +3536,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "version_check", ] @@ -3461,9 +3552,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.60" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" dependencies = [ "unicode-ident", ] @@ -3474,8 +3565,8 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", "version_check", "yansi", @@ -3521,8 +3612,8 @@ checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -3559,8 +3650,8 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -3637,11 +3728,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.28" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" dependencies = [ - "proc-macro2 1.0.60", + "proc-macro2 1.0.63", ] [[package]] @@ -3950,8 +4041,8 @@ version = "0.7.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -4244,8 +4335,8 @@ checksum = "7216195de9c6b2474fd0efab486173dccd0eff21f28cc54aa4c0205d52fb3af0" dependencies = [ "bae", "heck 0.3.3", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -4257,8 +4348,8 @@ checksum = "28936f26d62234ff0be16f80115dbdeb3237fe9c25cf18fbcd1e3b3592360f20" dependencies = [ "bae", "heck 0.3.3", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -4346,8 +4437,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34cdc022b4f606353fe5dc85b09713a04e433323b70163e81513b141c6ae6eb5" dependencies = [ "heck 0.3.3", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", "thiserror", ] @@ -4359,8 +4450,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63f62030c60f3a691f5fe251713b4e220b306e50a71e1d6f9cce1f24bb781978" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", "thiserror", ] @@ -4383,8 +4474,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56821b7076f5096b8f726e2791ad255a99c82498e08ec477a65a96c461ff1927" dependencies = [ "heck 0.3.3", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", ] @@ -4404,8 +4495,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69b4397b825df6ccf1e98bcdabef3bbcfc47ff5853983467850eeab878384f21" dependencies = [ "heck 0.3.3", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "rustversion", "syn 1.0.109", ] @@ -4469,9 +4560,9 @@ version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", - "syn 2.0.18", + "proc-macro2 1.0.63", + "quote 1.0.29", + "syn 2.0.23", ] [[package]] @@ -4520,9 +4611,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" dependencies = [ "darling", - "proc-macro2 1.0.60", - "quote 1.0.28", - "syn 2.0.18", + "proc-macro2 1.0.63", + "quote 1.0.29", + "syn 2.0.23", ] [[package]] @@ -4620,8 +4711,8 @@ version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63927d22a1e8b74bda98cc6e151fcdf178b7abb0dc6c4f81e0bbf5ffe2fc4ec8" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "shank_macro_impl", "syn 1.0.109", ] @@ -4633,8 +4724,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce03403df682f80f4dc1efafa87a4d0cb89b03726d0565e6364bdca5b9a441" dependencies = [ "anyhow", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "serde", "syn 1.0.109", ] @@ -4938,8 +5029,8 @@ version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be23cc7a382f54dfe1348edb94610e5cc146b8eb21563cdd04062a403c75ba62" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "rustc_version", "syn 1.0.109", ] @@ -5212,8 +5303,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d0acbad862093ea123f3a27364336dcb0c8373522cd6810496a34e932c56c1" dependencies = [ "bs58 0.4.0", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "rustversion", "syn 1.0.109", ] @@ -5540,8 +5631,8 @@ dependencies = [ "either", "heck 0.4.1", "once_cell", - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "serde_json", "sha2 0.10.6", "sqlx-core", @@ -5606,19 +5697,19 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.18" +version = "2.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "unicode-ident", ] @@ -5634,8 +5725,8 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", + "proc-macro2 1.0.63", + "quote 1.0.29", "syn 1.0.109", "unicode-xid 0.2.4", ] @@ -5699,9 +5790,9 @@ version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", - "syn 2.0.18", + "proc-macro2 1.0.63", + "quote 1.0.29", + "syn 2.0.23", ] [[package]] @@ -5821,9 +5912,9 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", - "syn 2.0.18", + "proc-macro2 1.0.63", + "quote 1.0.29", + "syn 2.0.23", ] [[package]] @@ -5965,9 +6056,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6fdaae4c2c638bb70fe42803a26fbd6fc6ac8c72f5c59f67ecc2a2dcabf4b07" dependencies = [ "prettyplease", - "proc-macro2 1.0.60", + "proc-macro2 1.0.63", "prost-build", - "quote 1.0.28", + "quote 1.0.29", "syn 1.0.109", ] @@ -6035,9 +6126,9 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", - "syn 2.0.18", + "proc-macro2 1.0.63", + "quote 1.0.29", + "syn 2.0.23", ] [[package]] @@ -6338,9 +6429,9 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.60", - "quote 1.0.28", - "syn 2.0.18", + "proc-macro2 1.0.63", + "quote 1.0.29", + "syn 2.0.23", "wasm-bindgen-shared", ] @@ -6362,7 +6453,7 @@ version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" dependencies = [ - "quote 1.0.28", + "quote 1.0.29", "wasm-bindgen-macro-support", ] @@ -6372,9 +6463,9 @@ version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", - "syn 2.0.18", + "proc-macro2 1.0.63", + "quote 1.0.29", + "syn 2.0.23", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6745,9 +6836,9 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.28", - "syn 2.0.18", + "proc-macro2 1.0.63", + "quote 1.0.29", + "syn 2.0.23", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index b166a91..458a1ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,3 @@ [workspace] members = ["consumer", "core", "entity", "migration", "indexer"] resolver = "2" - -[workspace.dependencies] -holaplex-hub-nfts-solana-core = { path = "core" } -holaplex-hub-nfts-solana-entity = { path = "entity" } diff --git a/consumer/Cargo.toml b/consumer/Cargo.toml index 21674a9..a8f8c0c 100644 --- a/consumer/Cargo.toml +++ b/consumer/Cargo.toml @@ -32,10 +32,12 @@ mpl-bubblegum = "0.8.0" mpl-token-metadata = "1.8.3" holaplex-hub-nfts-solana-core = { path = "../core" } holaplex-hub-nfts-solana-entity = { path = "../entity" } +jsonrpsee = { version = "0.18.2", features = ["macros", "http-client"] } +bs58 = "0.5.0" [dependencies.hub-core] package = "holaplex-hub-core" version = "0.2.0" git = "https://github.com/holaplex/hub-core" branch = "stable" -features = ["kafka"] \ No newline at end of file +features = ["kafka"] diff --git a/consumer/src/asset_api.rs b/consumer/src/asset_api.rs new file mode 100644 index 0000000..23aa64c --- /dev/null +++ b/consumer/src/asset_api.rs @@ -0,0 +1,141 @@ +mod b58 { + use serde::{de::Visitor, Deserializer, Serializer}; + + pub fn serialize(bytes: &[u8], ser: S) -> Result { + ser.serialize_str(&bs58::encode(bytes).into_string()) + } + + pub fn deserialize<'de, D: Deserializer<'de>>(de: D) -> Result, D::Error> { + struct Vis; + + impl<'a> Visitor<'a> for Vis { + type Value = Vec; + + fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str("a base58-encoded string") + } + + fn visit_str(self, s: &str) -> Result { + bs58::decode(s).into_vec().map_err(E::custom) + } + } + + de.deserialize_str(Vis) + } +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct Base58(#[serde(with = "b58")] pub Vec); + +impl From> for Base58 { + fn from(v: Vec) -> Self { + Self(v) + } +} + +impl From for Vec { + fn from(Base58(v): Base58) -> Self { + v + } +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct Asset { + pub interface: String, + pub id: Base58, + pub content: serde_json::Value, + pub authorities: Vec, + pub compression: AssetCompression, + pub grouping: Vec, + pub royalty: AssetRoyalty, + pub creators: Vec, + pub ownership: AssetOwnership, + pub supply: Option, + pub mutable: bool, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct AssetAuthority { + pub address: Base58, + pub scopes: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct AssetCompression { + pub eligible: bool, + pub compressed: bool, + pub data_hash: Base58, + pub creator_hash: Base58, + pub asset_hash: Base58, + pub tree: Base58, + pub seq: u32, + pub leaf_id: u32, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct AssetGrouping { + pub group_key: String, + pub group_value: Base58, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct AssetRoyalty { + pub royalty_model: String, + pub target: Option, // TODO: what type is this + pub percent: serde_json::Number, // TODO: this is fractional, use BCD to avoid rounding error + pub basis_points: u32, + pub primary_sale_happened: bool, + pub locked: bool, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct AssetCreator { + pub address: Base58, + pub share: u32, + pub verified: bool, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct AssetOwnership { + pub frozen: bool, + pub delegated: bool, + pub delegate: Base58, + pub ownership_model: String, + pub owner: Base58, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct AssetProof { + pub root: Base58, + pub proof: Vec, + pub node_index: u32, + pub leaf: Base58, + pub tree_id: Base58, +} + +#[jsonrpsee::proc_macros::rpc(client)] +pub trait Rpc { + #[method(name = "getAsset", param_kind = map)] + fn get_asset(&self, id: &str) -> Result; + + #[method(name = "getAssetProof", param_kind = map)] + fn get_asset_proof(&self, id: &str) -> Result; + + // Supposedly Triton offers these but their docs were crashing my browser + // so I don't know what the signatures are. + + // #[method(name = "getAssetsByAuthority")] + // fn get_assets_by_authority(&self); + + // #[method(name = "getAssetsByOwner")] + // fn get_assets_by_owner(&self); + + // #[method(name = "getAssetsByGroup")] + // fn get_assets_by_group(&self); + + // #[method(name = "getAssetsByCreator")] + // fn get_assets_by_creator(&self); + + // #[method(name = "searchAssets")] + // fn search_assets(&self); +} diff --git a/consumer/src/backend.rs b/consumer/src/backend.rs index b621d86..621b60f 100644 --- a/consumer/src/backend.rs +++ b/consumer/src/backend.rs @@ -54,27 +54,45 @@ pub struct TransactionResponse { pub addresses: A, } -pub trait Backend { +// TODO: include this in collections::Model +pub enum CollectionType { + Legacy, + Verified, +} + +// Legacy, Verified +#[async_trait] +pub trait CollectionBackend { fn create( &self, txn: MetaplexMasterEditionTransaction, ) -> Result>; +} +// Uncompressed, Compressed +#[async_trait] +pub trait MintBackend { fn mint( &self, + collection_ty: CollectionType, collection: &collections::Model, txn: MintMetaplexEditionTransaction, ) -> Result>; - fn update( + // TODO: probably better to replace all errors here with an Error enum + fn try_update( &self, + collection_ty: CollectionType, collection: &collections::Model, txn: MetaplexMasterEditionTransaction, - ) -> Result>; + ) -> Result>>; - fn transfer( + // Right now only this one needs to be async to support hitting the asset + // API for transfer data + async fn transfer( &self, - mint: &collection_mints::Model, + collection_ty: CollectionType, + collection_mint: &collection_mints::Model, txn: TransferMetaplexAssetTransaction, ) -> Result>; } diff --git a/consumer/src/events.rs b/consumer/src/events.rs index 7493766..34ccdbd 100644 --- a/consumer/src/events.rs +++ b/consumer/src/events.rs @@ -28,8 +28,7 @@ use hub_core::{chrono::Utc, prelude::*, producer::Producer, thiserror::Error, uu use crate::{ backend::{self, MasterEditionAddresses, TransactionResponse}, - solana::Solana, - solana_compressed::SolanaCompressed, + solana::{Solana, UncompressedRef}, }; #[derive(Error, Debug)] @@ -45,22 +44,15 @@ pub enum ProcessorError { #[derive(Clone)] pub struct Processor { solana: Solana, - compressed: SolanaCompressed, db: Connection, producer: Producer, } impl Processor { #[must_use] - pub fn new( - solana: Solana, - compressed: SolanaCompressed, - db: Connection, - producer: Producer, - ) -> Self { + pub fn new(solana: Solana, db: Connection, producer: Producer) -> Self { Self { solana, - compressed, db, producer, } @@ -76,93 +68,92 @@ impl Processor { Services::Nfts(key, e) => { let key = SolanaNftEventKey::from(key); - if true { - // TODO - match e.event { - Some(SolanaCreateDrop(payload)) => { - let create_drop_result = self.create_drop(&self.solana, key.clone(), payload).await; + // TODO: swap UncompressedRef for CompressedRef or LegacyCollectionRef depending on + // message context - if create_drop_result.is_err() { - self.create_drop_failed( - key, - SolanaTransactionFailureReason::Assemble, - ) + match e.event { + Some(SolanaCreateDrop(payload)) => { + let create_drop_result = self + .create_drop(&UncompressedRef(&self.solana), key.clone(), payload) + .await; + + if create_drop_result.is_err() { + self.create_drop_failed(key, SolanaTransactionFailureReason::Assemble) .await?; - } + } - Ok(()) - }, - Some(SolanaMintDrop(payload)) => { - let mint_drop_result = self.mint_drop(&self.solana, key.clone(), payload).await; + Ok(()) + }, + Some(SolanaMintDrop(payload)) => { + let mint_drop_result = self + .mint_drop(&UncompressedRef(&self.solana), key.clone(), payload) + .await; - if mint_drop_result.is_err() { - self.mint_drop_failed( - key, - SolanaTransactionFailureReason::Assemble, - ) + if mint_drop_result.is_err() { + self.mint_drop_failed(key, SolanaTransactionFailureReason::Assemble) .await?; - } + } - Ok(()) - }, - Some(SolanaUpdateDrop(payload)) => { - let update_drop_result = self.update_drop(&self.solana, key.clone(), payload).await; + Ok(()) + }, + Some(SolanaUpdateDrop(payload)) => { + let update_drop_result = self + .update_drop(&UncompressedRef(&self.solana), key.clone(), payload) + .await; - if update_drop_result.is_err() { - self.update_drop_failed( - key, - SolanaTransactionFailureReason::Assemble, - ) + if update_drop_result.is_err() { + self.update_drop_failed(key, SolanaTransactionFailureReason::Assemble) .await?; - } + } - Ok(()) - }, - Some(SolanaTransferAsset(payload)) => { - let transfer_asset_result = - self.transfer_asset(&self.solana, key.clone(), payload).await; + Ok(()) + }, + Some(SolanaTransferAsset(payload)) => { + let transfer_asset_result = self + .transfer_asset(&UncompressedRef(&self.solana), key.clone(), payload) + .await; - if transfer_asset_result.is_err() { - self.transfer_asset_failed( - key, - SolanaTransactionFailureReason::Assemble, - ) - .await?; - } + if transfer_asset_result.is_err() { + self.transfer_asset_failed( + key, + SolanaTransactionFailureReason::Assemble, + ) + .await?; + } - Ok(()) - }, - Some(SolanaRetryDrop(payload)) => { - let retry_drop_result = self.retry_drop(&self.solana, key.clone(), payload).await; + Ok(()) + }, + Some(SolanaRetryDrop(payload)) => { + let retry_drop_result = self + .retry_drop(&UncompressedRef(&self.solana), key.clone(), payload) + .await; - if retry_drop_result.is_err() { - self.retry_create_drop_failed( - key, - SolanaTransactionFailureReason::Assemble, - ) - .await?; - } + if retry_drop_result.is_err() { + self.retry_create_drop_failed( + key, + SolanaTransactionFailureReason::Assemble, + ) + .await?; + } - Ok(()) - }, - Some(SolanaRetryMintDrop(payload)) => { - let retry_mint_drop_result = - self.retry_mint_drop(&self.solana, key.clone(), payload).await; + Ok(()) + }, + Some(SolanaRetryMintDrop(payload)) => { + let retry_mint_drop_result = self + .retry_mint_drop(&UncompressedRef(&self.solana), key.clone(), payload) + .await; - if retry_mint_drop_result.is_err() { - self.retry_mint_drop_failed( - key, - SolanaTransactionFailureReason::Assemble, - ) - .await?; - } - - Ok(()) - }, - Some(_) | None => Ok(()), - } - } else { - todo!() + if retry_mint_drop_result.is_err() { + self.retry_mint_drop_failed( + key, + SolanaTransactionFailureReason::Assemble, + ) + .await?; + } + + Ok(()) + }, + Some(_) | None => Ok(()), } }, Services::Treasury(key, e) => { @@ -342,7 +333,7 @@ impl Processor { } } - async fn create_drop( + async fn create_drop( &self, backend: &B, key: SolanaNftEventKey, @@ -404,7 +395,7 @@ impl Processor { Ok(()) } - async fn retry_drop( + async fn retry_drop( &self, backend: &B, key: SolanaNftEventKey, @@ -468,7 +459,7 @@ impl Processor { Ok(()) } - async fn mint_drop( + async fn mint_drop( &self, backend: &B, key: SolanaNftEventKey, @@ -481,7 +472,8 @@ impl Processor { .ok_or(ProcessorError::RecordNotFound)?; // TODO: the collection mint record may fail to be created if this fails. Need to handle upserting the record in retry mint. - let tx = backend.mint(&collection, payload)?; + let collection_ty = todo!("determine collection type"); + let tx = backend.mint(collection_ty, &collection, payload)?; let collection_mint = collection_mints::Model { id, @@ -525,7 +517,7 @@ impl Processor { Ok(()) } - async fn retry_mint_drop( + async fn retry_mint_drop( &self, backend: &B, key: SolanaNftEventKey, @@ -540,7 +532,8 @@ impl Processor { let collection = collection.ok_or(ProcessorError::RecordNotFound)?; - let tx = backend.mint(&collection, payload)?; + let collection_ty = todo!("determine collection type"); + let tx = backend.mint(collection_ty, &collection, payload)?; let mut collection_mint: collection_mints::ActiveModel = collection_mint.into(); @@ -582,7 +575,7 @@ impl Processor { Ok(()) } - async fn update_drop( + async fn update_drop( &self, backend: &B, key: SolanaNftEventKey, @@ -593,7 +586,9 @@ impl Processor { .await? .ok_or(ProcessorError::RecordNotFound)?; - let tx = backend.update(&collection, payload)?; + let collection_ty = todo!("determine collection type"); + let tx = backend.try_update(collection_ty, &collection, payload)?; + let Some(tx) = tx else { todo!("handle un-updateable assets") }; self.producer .send( @@ -626,7 +621,7 @@ impl Processor { Ok(()) } - async fn transfer_asset( + async fn transfer_asset( &self, backend: &B, key: SolanaNftEventKey, @@ -637,7 +632,12 @@ impl Processor { .await? .ok_or(ProcessorError::RecordNotFound)?; - let tx = backend.transfer(&collection_mint, payload)?; + let collection_ty = todo!("determine collection type"); + let tx = backend.transfer( + collection_ty, + &collection_mint, + payload, + ).await?; self.producer .send( diff --git a/consumer/src/lib.rs b/consumer/src/lib.rs index 52a7fb7..7dc73f0 100644 --- a/consumer/src/lib.rs +++ b/consumer/src/lib.rs @@ -2,10 +2,10 @@ #![warn(clippy::pedantic, clippy::cargo)] #![allow(clippy::module_name_repetitions)] +pub(crate) mod asset_api; mod backend; pub mod events; pub mod solana; -pub mod solana_compressed; use holaplex_hub_nfts_solana_core::db::DbArgs; use hub_core::{clap, prelude::*}; @@ -19,7 +19,4 @@ pub struct Args { #[command(flatten)] pub solana: SolanaArgs, - - #[command(flatten)] - pub solana_compressed: solana_compressed::SolanaCompressedArgs, } diff --git a/consumer/src/main.rs b/consumer/src/main.rs index 91ca302..c84aed9 100644 --- a/consumer/src/main.rs +++ b/consumer/src/main.rs @@ -1,14 +1,6 @@ -use std::sync::Arc; - -use holaplex_hub_nfts_solana::{ - events::Processor, - solana::{Solana, SolanaArgs}, - solana_compressed::SolanaCompressed, - Args, -}; +use holaplex_hub_nfts_solana::{events::Processor, solana::Solana, Args}; use holaplex_hub_nfts_solana_core::{db::Connection, proto::SolanaNftEvents, Services}; use hub_core::{prelude::*, tokio}; -use solana_client::rpc_client::RpcClient; pub fn main() { let opts = hub_core::StartConfig { @@ -16,38 +8,19 @@ pub fn main() { }; hub_core::run(opts, |common, args| { - let Args { - db, - solana: - SolanaArgs { - solana_endpoint, - solana_treasury_wallet_address, - }, - solana_compressed, - } = args; + let Args { db, solana } = args; common.rt.block_on(async move { let connection = Connection::new(db) .await .context("failed to get database connection")?; - let producer = common - .producer_cfg - .build::() - .await?; + let producer = common.producer_cfg.build::().await?; - let solana_rpc = Arc::new(RpcClient::new(solana_endpoint)); - let solana = Solana::new(solana_rpc.clone(), solana_treasury_wallet_address.clone()); - let compressed = SolanaCompressed::new( - solana_rpc, - solana_treasury_wallet_address - .parse() - .context("Error parsing treasury wallet address")?, - solana_compressed, - ); + let solana = Solana::new(solana)?; let cons = common.consumer_cfg.build::().await?; - let event_processor = Processor::new(solana, compressed, connection, producer); + let event_processor = Processor::new(solana, connection, producer); let mut stream = cons.stream(); loop { diff --git a/consumer/src/solana.rs b/consumer/src/solana.rs index cd351df..31409f8 100644 --- a/consumer/src/solana.rs +++ b/consumer/src/solana.rs @@ -1,3 +1,4 @@ +use anchor_lang::{prelude::AccountMeta, InstructionData}; use holaplex_hub_nfts_solana_core::proto::{ treasury_events::SolanaTransactionResult, MasterEdition, MetaplexMasterEditionTransaction, MintMetaplexEditionTransaction, TransferMetaplexAssetTransaction, @@ -9,7 +10,10 @@ use mpl_token_metadata::{ state::{Creator, DataV2, EDITION, PREFIX}, }; use solana_client::rpc_client::RpcClient; -use solana_program::{program_pack::Pack, pubkey::Pubkey, system_instruction::create_account}; +use solana_program::{ + instruction::Instruction, program_pack::Pack, pubkey::Pubkey, + system_instruction::create_account, system_program, +}; use solana_sdk::{ signature::Signature, signer::{keypair::Keypair, Signer}, @@ -23,20 +27,33 @@ use spl_token::{ state, }; -use crate::backend::{ - Backend, MasterEditionAddresses, MintEditionAddresses, TransactionResponse, - TransferAssetAddresses, UpdateMasterEditionAddresses, +use crate::{ + asset_api::RpcClient as _, + backend::{ + CollectionBackend, CollectionType, MasterEditionAddresses, MintBackend, + MintEditionAddresses, TransactionResponse, TransferAssetAddresses, + UpdateMasterEditionAddresses, + }, }; const TOKEN_PROGRAM_PUBKEY: &str = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"; -#[derive(Debug, clap::Args, Clone)] +#[derive(Debug, clap::Args)] pub struct SolanaArgs { #[arg(long, env)] pub solana_endpoint: String, #[arg(long, env)] - pub solana_treasury_wallet_address: String, + pub solana_treasury_wallet_address: Pubkey, + + #[arg(long, env)] + pub digital_asset_api_endpoint: String, + + // TODO: remove these + #[arg(long, env)] + pub tree_authority: Pubkey, + #[arg(long, env)] + pub merkle_tree: Pubkey, } #[derive(Clone)] @@ -76,15 +93,34 @@ enum SolanaError { #[derive(Clone)] pub struct Solana { rpc_client: Arc, - treasury_wallet_address: String, + treasury_wallet_address: Pubkey, + bubblegum_tree_authority: Pubkey, + bubblegum_merkle_tree: Pubkey, + asset_rpc_client: jsonrpsee::http_client::HttpClient, } impl Solana { - pub fn new(rpc_client: Arc, treasury_wallet_address: String) -> Self { - Self { + pub fn new(args: SolanaArgs) -> Result { + let SolanaArgs { + solana_endpoint, + solana_treasury_wallet_address, + digital_asset_api_endpoint, + tree_authority, + merkle_tree, + } = args; + let rpc_client = Arc::new(RpcClient::new(solana_endpoint)); + + Ok(Self { rpc_client, - treasury_wallet_address, - } + treasury_wallet_address: solana_treasury_wallet_address, + bubblegum_tree_authority: tree_authority, + bubblegum_merkle_tree: merkle_tree, + asset_rpc_client: jsonrpsee::http_client::HttpClientBuilder::default() + .request_timeout(std::time::Duration::from_secs(5)) + // .set_headers(...) // TODO: add auth here? + .build(digital_asset_api_endpoint) + .context("Failed to initialize asset API client")?, + }) } #[must_use] @@ -128,7 +164,7 @@ impl Solana { &self, payload: MasterEdition, ) -> Result> { - let payer: Pubkey = self.treasury_wallet_address.parse()?; + let payer: Pubkey = self.treasury_wallet_address; let rpc = &self.rpc_client; let mint = Keypair::new(); let MasterEdition { @@ -263,33 +299,53 @@ impl Solana { } } -impl Backend for Solana { +#[repr(transparent)] +pub struct UncompressedRef<'a>(pub &'a Solana); +#[repr(transparent)] +pub struct CompressedRef<'a>(pub &'a Solana); +#[repr(transparent)] +pub struct LegacyCollectionRef<'a>(pub &'a Solana); + +impl<'a> CollectionBackend for UncompressedRef<'a> { fn create( &self, - payload: MetaplexMasterEditionTransaction, - ) -> Result> { - let MetaplexMasterEditionTransaction { master_edition, .. } = payload; + txn: MetaplexMasterEditionTransaction, + ) -> hub_core::prelude::Result> { + todo!() + } +} + +impl<'a> CollectionBackend for LegacyCollectionRef<'a> { + fn create( + &self, + txn: MetaplexMasterEditionTransaction, + ) -> hub_core::prelude::Result> { + let MetaplexMasterEditionTransaction { master_edition, .. } = txn; let master_edition = master_edition.ok_or(SolanaError::MasterEditionMessageNotFound)?; - let tx = self.master_edition_transaction(master_edition)?; + let tx = self.0.master_edition_transaction(master_edition)?; Ok(tx) } +} +#[async_trait] +impl<'a> MintBackend for UncompressedRef<'a> { fn mint( &self, + collection_ty: CollectionType, collection: &collections::Model, - payload: MintMetaplexEditionTransaction, - ) -> Result> { - let rpc = &self.rpc_client; + txn: MintMetaplexEditionTransaction, + ) -> hub_core::prelude::Result> { + let rpc = &self.0.rpc_client; let MintMetaplexEditionTransaction { recipient_address, owner_address, edition, .. - } = payload; + } = txn; - let payer: Pubkey = self.treasury_wallet_address.parse()?; + let payer: Pubkey = self.0.treasury_wallet_address; let owner = owner_address.parse()?; let program_pubkey = mpl_token_metadata::id(); @@ -386,14 +442,15 @@ impl Backend for Solana { }) } - fn update( + fn try_update( &self, + collection_ty: CollectionType, collection: &collections::Model, - payload: MetaplexMasterEditionTransaction, - ) -> Result> { - let rpc = &self.rpc_client; + txn: MetaplexMasterEditionTransaction, + ) -> hub_core::prelude::Result>> { + let rpc = &self.0.rpc_client; - let MetaplexMasterEditionTransaction { master_edition, .. } = payload; + let MetaplexMasterEditionTransaction { master_edition, .. } = txn; let master_edition = master_edition.ok_or(SolanaError::MasterEditionMessageNotFound)?; @@ -406,7 +463,7 @@ impl Backend for Solana { .. } = master_edition; - let payer: Pubkey = self.treasury_wallet_address.parse()?; + let payer: Pubkey = self.0.treasury_wallet_address; let program_pubkey = mpl_token_metadata::id(); let update_authority: Pubkey = master_edition.owner_address.parse()?; @@ -442,7 +499,7 @@ impl Backend for Solana { let serialized_message = message.serialize(); - Ok(TransactionResponse { + Ok(Some(TransactionResponse { serialized_message, signatures_or_signers_public_keys: vec![ payer.to_string(), @@ -452,25 +509,26 @@ impl Backend for Solana { metadata, update_authority, }, - }) + })) } - fn transfer( + async fn transfer( &self, + collection_ty: CollectionType, collection_mint: &collection_mints::Model, - payload: TransferMetaplexAssetTransaction, - ) -> Result> { - let rpc = &self.rpc_client; + txn: TransferMetaplexAssetTransaction, + ) -> hub_core::prelude::Result> { + let rpc = &self.0.rpc_client; let TransferMetaplexAssetTransaction { owner_address, recipient_address, .. - } = payload; + } = txn; let sender: Pubkey = owner_address.parse()?; let recipient: Pubkey = recipient_address.parse()?; let mint_address: Pubkey = collection_mint.mint.parse()?; - let payer: Pubkey = self.treasury_wallet_address.parse()?; + let payer: Pubkey = self.0.treasury_wallet_address; let blockhash = rpc.get_latest_blockhash()?; let source_ata = get_associated_token_address(&sender, &mint_address); let destination_ata = get_associated_token_address(&recipient, &mint_address); @@ -516,3 +574,271 @@ impl Backend for Solana { }) } } + +#[async_trait] +impl<'a> MintBackend for CompressedRef<'a> { + // fn create( + // &self, + // evt: MetaplexMasterEditionTransaction, + // ) -> Result> { + // let MetaplexMasterEditionTransaction { master_edition, .. } = evt; + // let MasterEdition { + // name, + // symbol, + // metadata_uri, + // creators, + // seller_fee_basis_points, + // supply, // TODO: ? + // owner_address, + // } = master_edition.context("Missing master edition message")?; + // let payer = self.treasury; + // let owner = owner_address.parse()?; + // let mint = Keypair::new(); + + // let (metadata, _) = Pubkey::find_program_address( + // &[ + // b"metadata", + // mpl_token_metadata::ID.as_ref(), + // mint.pubkey().as_ref(), + // ], + // &mpl_token_metadata::ID, + // ); + // let associated_token_account = get_associated_token_address(&owner, &mint.pubkey()); + + // let mint_len = spl_token::state::Mint::LEN; + + // // TODO: this is the collection NFT, right? + // let instructions = [ + // solana_program::system_instruction::create_account( + // &payer, + // &mint.pubkey(), + // self.rpc.get_minimum_balance_for_rent_exemption(mint_len)?, + // mint_len.try_into()?, + // &spl_token::ID, + // ), + // spl_token::instruction::initialize_mint( + // &spl_token::ID, + // &mint.pubkey(), + // &owner, + // Some(&owner), + // 0, + // )?, + // spl_associated_token_account::instruction::create_associated_token_account( + // &payer, + // &owner, + // &mint.pubkey(), + // &spl_token::ID, + // ), + // spl_token::instruction::mint_to( + // &spl_token::ID, + // &mint.pubkey(), + // &associated_token_account, + // &owner, + // &[], + // 1, + // )?, + // mpl_token_metadata::instruction::create_metadata_accounts_v3( + // mpl_token_metadata::ID, + // metadata, + // mint.pubkey(), + // owner, + // payer, + // owner, + // name, + // symbol, + // metadata_uri, + // Some( + // creators + // .into_iter() + // .map(TryInto::try_into) + // .collect::, _>>()?, + // ), + // seller_fee_basis_points.try_into()?, + // true, + // true, + // None, + // None, + // None, + // ), + // ]; + + // let serialized_message = solana_program::message::Message::new_with_blockhash( + // &instructions, + // Some(&payer), + // &self.rpc.get_latest_blockhash()?, + // ) + // .serialize(); + // let mint_signature = mint.try_sign_message(&serialized_message)?; + + // Ok(TransactionResponse { + // serialized_message, + // signatures_or_signers_public_keys: vec![ + // payer.to_string(), + // mint_signature.to_string(), + // owner.to_string(), + // ], + // addresses: MasterEditionAddresses { + // metadata, + // associated_token_account, + // owner, + // master_edition: todo!("what"), + // mint: mint.pubkey(), + // update_authority: owner, + // }, + // }) + // } + + fn mint( + &self, + collection_ty: CollectionType, + collection: &collections::Model, + txn: MintMetaplexEditionTransaction, + ) -> hub_core::prelude::Result> { + match collection_ty { + CollectionType::Legacy => { + bail!("Legacy collections are not supported with compressed NFTs.") + }, + CollectionType::Verified => (), + } + + let MintMetaplexEditionTransaction { + recipient_address, + owner_address, + edition, + .. + } = txn; + let payer = self.0.treasury_wallet_address; + let recipient = recipient_address.parse()?; + let owner = owner_address.parse()?; + + let instructions = [Instruction { + program_id: mpl_bubblegum::ID, + accounts: [ + AccountMeta::new(self.0.bubblegum_tree_authority, false), + AccountMeta::new_readonly(recipient, false), + AccountMeta::new_readonly(recipient, false), + AccountMeta::new(self.0.bubblegum_merkle_tree, false), + AccountMeta::new_readonly(payer, true), + AccountMeta::new_readonly(owner, true), // TODO: who will own the trees?? + AccountMeta::new_readonly(spl_noop::ID, false), + AccountMeta::new_readonly(spl_account_compression::ID, false), + AccountMeta::new_readonly(system_program::ID, false), + ] + .into_iter() + .collect(), + data: mpl_bubblegum::instruction::MintV1 { + message: mpl_bubblegum::state::metaplex_adapter::MetadataArgs { + name: todo!(), + symbol: todo!(), + uri: todo!(), + seller_fee_basis_points: todo!(), + primary_sale_happened: todo!(), + is_mutable: todo!(), + edition_nonce: todo!(), + token_standard: todo!(), + collection: todo!(), + uses: todo!(), + token_program_version: todo!(), + creators: todo!(), + }, + } + .data(), + }]; + + let serialized_message = solana_program::message::Message::new_with_blockhash( + &instructions, + Some(&payer), + &self.0.rpc_client.get_latest_blockhash()?, + ) + .serialize(); + + Ok(TransactionResponse { + serialized_message, + signatures_or_signers_public_keys: vec![payer.to_string(), owner.to_string()], + addresses: MintEditionAddresses { + edition: todo!("what"), + mint: todo!("what"), + metadata: todo!("what"), + owner, + associated_token_account: todo!("what"), + recipient, + }, + }) + } + + fn try_update( + &self, + collection_ty: CollectionType, + collection: &collections::Model, + txn: MetaplexMasterEditionTransaction, + ) -> hub_core::prelude::Result>> { + Ok(None) + } + + async fn transfer( + &self, + collection_ty: CollectionType, + collection_mint: &collection_mints::Model, + txn: TransferMetaplexAssetTransaction, + ) -> hub_core::prelude::Result> { + let TransferMetaplexAssetTransaction { + recipient_address, + owner_address, + collection_mint_id, + .. + } = txn; + let payer = self.0.treasury_wallet_address; + let recipient = recipient_address.parse()?; + let owner = owner_address.parse()?; + + let asset_id = todo!("wait where's the asset address"); + let asset = self + .0 + .asset_rpc_client + .get_asset(asset_id) + .await + .context("Error getting asset data")?; + + let instructions = [Instruction { + program_id: mpl_bubblegum::ID, + accounts: [ + AccountMeta::new(self.0.bubblegum_tree_authority, false), + AccountMeta::new_readonly(owner, true), + AccountMeta::new_readonly(owner, false), + AccountMeta::new_readonly(recipient, false), + AccountMeta::new(self.0.bubblegum_merkle_tree, false), + AccountMeta::new_readonly(spl_noop::ID, false), + AccountMeta::new_readonly(spl_account_compression::ID, false), + AccountMeta::new_readonly(system_program::ID, false), + ] + .into_iter() + .collect(), + data: mpl_bubblegum::instruction::Transfer { + root: todo!("how does DAA work"), + data_hash: todo!("how does DAA work"), + creator_hash: todo!("how does DAA work"), + nonce: todo!("how does DAA work"), + index: todo!("how does DAA work"), + } + .data(), + }]; + + let serialized_message = solana_program::message::Message::new_with_blockhash( + &instructions, + Some(&payer), + &self.0.rpc_client.get_latest_blockhash()?, + ) + .serialize(); + + Ok(TransactionResponse { + serialized_message, + signatures_or_signers_public_keys: vec![payer.to_string(), owner.to_string()], + addresses: TransferAssetAddresses { + owner, + recipient, + recipient_associated_token_account: todo!("what"), + owner_associated_token_account: todo!("what"), + }, + }) + } +} diff --git a/consumer/src/solana_compressed.rs b/consumer/src/solana_compressed.rs deleted file mode 100644 index be041cf..0000000 --- a/consumer/src/solana_compressed.rs +++ /dev/null @@ -1,303 +0,0 @@ -use anchor_lang::InstructionData; -use holaplex_hub_nfts_solana_core::proto::{ - MasterEdition, MetaplexMasterEditionTransaction, MintMetaplexEditionTransaction, - TransferMetaplexAssetTransaction, -}; -use holaplex_hub_nfts_solana_entity::{collection_mints, collections}; -use hub_core::{anyhow::Result, clap, prelude::*}; -use mpl_token_metadata::state::Creator; -use solana_client::rpc_client::RpcClient; -use solana_program::{ - instruction::{AccountMeta, Instruction}, - program_pack::Pack, - pubkey::Pubkey, - system_program, -}; -use solana_sdk::{signature::Keypair, signer::Signer}; -use spl_associated_token_account::get_associated_token_address; - -use crate::backend::{ - Backend, MasterEditionAddresses, MintEditionAddresses, TransactionResponse, - TransferAssetAddresses, UpdateMasterEditionAddresses, -}; - -#[derive(Debug, clap::Args)] -pub struct SolanaCompressedArgs { - #[arg(long, env)] - pub tree_authority: Pubkey, - #[arg(long, env)] - pub merkle_tree: Pubkey, -} - -#[derive(Clone)] -pub struct SolanaCompressed { - rpc: Arc, - treasury: Pubkey, - tree_authority: Pubkey, - merkle_tree: Pubkey, -} - -impl Backend for SolanaCompressed { - fn create( - &self, - evt: MetaplexMasterEditionTransaction, - ) -> Result> { - let MetaplexMasterEditionTransaction { master_edition, .. } = evt; - let MasterEdition { - name, - symbol, - metadata_uri, - creators, - seller_fee_basis_points, - supply, // TODO: ? - owner_address, - } = master_edition.context("Missing master edition message")?; - let payer = self.treasury; - let owner = owner_address.parse()?; - let mint = Keypair::new(); - - let (metadata, _) = Pubkey::find_program_address( - &[ - b"metadata", - mpl_token_metadata::ID.as_ref(), - mint.pubkey().as_ref(), - ], - &mpl_token_metadata::ID, - ); - let associated_token_account = get_associated_token_address(&owner, &mint.pubkey()); - - let mint_len = spl_token::state::Mint::LEN; - - // TODO: this is the collection NFT, right? - let instructions = [ - solana_program::system_instruction::create_account( - &payer, - &mint.pubkey(), - self.rpc.get_minimum_balance_for_rent_exemption(mint_len)?, - mint_len.try_into()?, - &spl_token::ID, - ), - spl_token::instruction::initialize_mint( - &spl_token::ID, - &mint.pubkey(), - &owner, - Some(&owner), - 0, - )?, - spl_associated_token_account::instruction::create_associated_token_account( - &payer, - &owner, - &mint.pubkey(), - &spl_token::ID, - ), - spl_token::instruction::mint_to( - &spl_token::ID, - &mint.pubkey(), - &associated_token_account, - &owner, - &[], - 1, - )?, - mpl_token_metadata::instruction::create_metadata_accounts_v3( - mpl_token_metadata::ID, - metadata, - mint.pubkey(), - owner, - payer, - owner, - name, - symbol, - metadata_uri, - Some( - creators - .into_iter() - .map(TryInto::try_into) - .collect::, _>>()?, - ), - seller_fee_basis_points.try_into()?, - true, - true, - None, - None, - None, - ), - ]; - - let serialized_message = solana_program::message::Message::new_with_blockhash( - &instructions, - Some(&payer), - &self.rpc.get_latest_blockhash()?, - ) - .serialize(); - let mint_signature = mint.try_sign_message(&serialized_message)?; - - Ok(TransactionResponse { - serialized_message, - signatures_or_signers_public_keys: vec![ - payer.to_string(), - mint_signature.to_string(), - owner.to_string(), - ], - addresses: MasterEditionAddresses { - metadata, - associated_token_account, - owner, - master_edition: todo!("what"), - mint: mint.pubkey(), - update_authority: owner, - }, - }) - } - - fn mint( - &self, - collection: &collections::Model, - evt: MintMetaplexEditionTransaction, - ) -> Result> { - let MintMetaplexEditionTransaction { - recipient_address, - owner_address, - edition, - .. - } = evt; - let payer = self.treasury; - let recipient = recipient_address.parse()?; - let owner = owner_address.parse()?; - - let instructions = [Instruction { - program_id: mpl_bubblegum::ID, - accounts: [ - AccountMeta::new(self.tree_authority, false), - AccountMeta::new_readonly(recipient, false), - AccountMeta::new_readonly(recipient, false), - AccountMeta::new(self.merkle_tree, false), - AccountMeta::new_readonly(payer, true), - AccountMeta::new_readonly(owner, true), // TODO: who will own the trees?? - AccountMeta::new_readonly(spl_noop::ID, false), - AccountMeta::new_readonly(spl_account_compression::ID, false), - AccountMeta::new_readonly(system_program::ID, false), - ] - .into_iter() - .collect(), - data: mpl_bubblegum::instruction::MintV1 { - message: mpl_bubblegum::state::metaplex_adapter::MetadataArgs { - name: todo!(), - symbol: todo!(), - uri: todo!(), - seller_fee_basis_points: todo!(), - primary_sale_happened: todo!(), - is_mutable: todo!(), - edition_nonce: todo!(), - token_standard: todo!(), - collection: todo!(), - uses: todo!(), - token_program_version: todo!(), - creators: todo!(), - }, - } - .data(), - }]; - - let serialized_message = solana_program::message::Message::new_with_blockhash( - &instructions, - Some(&payer), - &self.rpc.get_latest_blockhash()?, - ) - .serialize(); - - Ok(TransactionResponse { - serialized_message, - signatures_or_signers_public_keys: vec![payer.to_string(), owner.to_string()], - addresses: MintEditionAddresses { - edition: todo!("what"), - mint: todo!("what"), - metadata: todo!("what"), - owner, - associated_token_account: todo!("what"), - recipient, - }, - }) - } - - fn update( - &self, - collection: &collections::Model, - evt: MetaplexMasterEditionTransaction, - ) -> Result> { - bail!("Cannot update a compressed NFT") - } - - fn transfer( - &self, - mint: &collection_mints::Model, - evt: TransferMetaplexAssetTransaction, - ) -> Result> { - let TransferMetaplexAssetTransaction { - recipient_address, - owner_address, - collection_mint_id, - .. - } = evt; - let payer = self.treasury; - let recipient = recipient_address.parse()?; - let owner = owner_address.parse()?; - - let instructions = [Instruction { - program_id: mpl_bubblegum::ID, - accounts: [ - AccountMeta::new(self.tree_authority, false), - AccountMeta::new_readonly(owner, true), - AccountMeta::new_readonly(owner, false), - AccountMeta::new_readonly(recipient, false), - AccountMeta::new(self.merkle_tree, false), - AccountMeta::new_readonly(spl_noop::ID, false), - AccountMeta::new_readonly(spl_account_compression::ID, false), - AccountMeta::new_readonly(system_program::ID, false), - ] - .into_iter() - .collect(), - data: mpl_bubblegum::instruction::Transfer { - root: todo!("set up DAA"), - data_hash: todo!("set up DAA"), - creator_hash: todo!("set up DAA"), - nonce: todo!("set up DAA"), - index: todo!("set up DAA"), - } - .data(), - }]; - - let serialized_message = solana_program::message::Message::new_with_blockhash( - &instructions, - Some(&payer), - &self.rpc.get_latest_blockhash()?, - ) - .serialize(); - - Ok(TransactionResponse { - serialized_message, - signatures_or_signers_public_keys: vec![payer.to_string(), owner.to_string()], - addresses: TransferAssetAddresses { - owner, - recipient, - recipient_associated_token_account: todo!("what"), - owner_associated_token_account: todo!("what"), - }, - }) - } -} - -impl SolanaCompressed { - #[must_use] - pub fn new(rpc: Arc, treasury: Pubkey, args: SolanaCompressedArgs) -> Self { - let SolanaCompressedArgs { - tree_authority, - merkle_tree, - } = args; - Self { - rpc, - treasury, - tree_authority, - merkle_tree, - } - } -}