diff --git a/Cargo.lock b/Cargo.lock index 4592a43..745ca1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -164,7 +164,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0bb0e0911ad4a70cab880cdd6287fe1e880a1a9d8e4e6defa8e9044b9796a6c" dependencies = [ "anchor-syn", - "borsh-derive-internal 0.9.3", + "borsh-derive-internal 0.10.3", "proc-macro2", "quote", "syn 1.0.109", @@ -200,10 +200,10 @@ dependencies = [ "arrayref", "base64 0.21.7", "bincode", - "borsh 0.9.3", + "borsh 0.10.3", "bytemuck", "getrandom 0.2.15", - "solana-program", + "solana-program 1.18.18", "thiserror", ] @@ -240,8 +240,8 @@ checksum = "04bd077c34449319a1e4e0bc21cea572960c9ae0d0fefda0dd7c52fcc3c647a3" dependencies = [ "anchor-lang", "spl-associated-token-account", - "spl-pod 0.2.2", - "spl-token", + "spl-pod 0.2.5", + "spl-token 4.0.0", "spl-token-2022", "spl-token-group-interface", "spl-token-metadata-interface", @@ -436,6 +436,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bincode" version = "1.3.3" @@ -519,7 +525,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" dependencies = [ "borsh-derive 0.10.3", - "hashbrown 0.11.2", + "hashbrown 0.13.2", ] [[package]] @@ -568,7 +574,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", "syn_derive", ] @@ -664,7 +670,7 @@ checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -685,13 +691,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.99" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" +checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052" dependencies = [ "jobserver", "libc", - "once_cell", ] [[package]] @@ -835,9 +840,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", @@ -845,27 +850,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] name = "darling_macro" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -1250,9 +1255,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" @@ -1292,14 +1297,25 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", ] +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "num-derive" version = "0.4.2" @@ -1308,7 +1324,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -1329,13 +1345,34 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_enum" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +dependencies = [ + "num_enum_derive 0.6.1", +] + [[package]] name = "num_enum" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" dependencies = [ - "num_enum_derive", + "num_enum_derive 0.7.2", +] + +[[package]] +name = "num_enum_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 2.0.71", ] [[package]] @@ -1347,7 +1384,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -1442,6 +1479,16 @@ dependencies = [ "toml 0.5.11", ] +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + [[package]] name = "proc-macro-crate" version = "3.1.0" @@ -1500,7 +1547,7 @@ checksum = "9e2e25ee72f5b24d773cae88422baddefff7714f97aab68d96fe2b6fc4a28fb2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -1614,9 +1661,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ "bitflags", ] @@ -1691,9 +1738,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] @@ -1709,20 +1756,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] name = "serde_json" -version = "1.0.118" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" dependencies = [ "itoa", "ryu", @@ -1757,7 +1804,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -1836,9 +1883,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "solana-frozen-abi" -version = "1.18.17" +version = "1.18.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4867f66e9527fa44451c861c1dc6d9b2a7c7a668d7c6a297cdefbe39f4395b33" +checksum = "3f498a2b290abca1cf77feacef01b904be725fd46a7aea5ba121cce8c1269dcf" dependencies = [ "block-buffer 0.10.4", "bs58 0.4.0", @@ -1861,21 +1908,21 @@ dependencies = [ [[package]] name = "solana-frozen-abi-macro" -version = "1.18.17" +version = "1.18.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168f24d97347b85f05192df58d6be3e3047a4aadc4001bc1b9e711a5ec878eea" +checksum = "e4ab48d1be18021f5c13f94671e766699511044f81aab3376313f6a2392f8fab" dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] name = "solana-logger" -version = "1.18.17" +version = "1.18.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0511082fc62f2d086520fff5aa1917c389d8c840930c08ad255ae05952c08a2" +checksum = "ed08bcdd54232d2017071a6f5d664b34649ef0110801ac310a01418215f22ff7" dependencies = [ "env_logger", "lazy_static", @@ -1884,9 +1931,9 @@ dependencies = [ [[package]] name = "solana-program" -version = "1.18.17" +version = "1.18.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc5a636dc75e5c25651e34f7a36afc9ae60d38166687c5b0375abb580ac81a2" +checksum = "d97cec6d3d60ef58168c8b3e97fd88e8903fa059eff6635361427c61c946ec1e" dependencies = [ "ark-bn254", "ark-ec", @@ -1916,7 +1963,7 @@ dependencies = [ "log", "memoffset", "num-bigint", - "num-derive", + "num-derive 0.4.2", "num-traits", "parking_lot", "rand 0.8.5", @@ -1930,18 +1977,64 @@ dependencies = [ "sha3 0.10.8", "solana-frozen-abi", "solana-frozen-abi-macro", - "solana-sdk-macro", + "solana-sdk-macro 1.18.18", "thiserror", "tiny-bip39", "wasm-bindgen", "zeroize", ] +[[package]] +name = "solana-program" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46779502637be111d121691aace200175ec1a57643f9760a36450b52d4eccdfd" +dependencies = [ + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-serialize", + "base64 0.22.1", + "bincode", + "bitflags", + "blake3", + "borsh 0.10.3", + "borsh 1.5.1", + "bs58 0.5.1", + "bv", + "bytemuck", + "bytemuck_derive", + "console_error_panic_hook", + "console_log", + "curve25519-dalek", + "getrandom 0.2.15", + "js-sys", + "lazy_static", + "libsecp256k1", + "log", + "memoffset", + "num-bigint", + "num-derive 0.4.2", + "num-traits", + "parking_lot", + "rand 0.8.5", + "rustc_version", + "rustversion", + "serde", + "serde_bytes", + "serde_derive", + "sha2 0.10.8", + "sha3 0.10.8", + "solana-sdk-macro 2.0.2", + "thiserror", + "wasm-bindgen", +] + [[package]] name = "solana-sdk" -version = "1.18.17" +version = "1.18.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df43d3a1e1637397ab43cbc216a5a8f977ec8a3cc3f3ae8c3851c83a3255dbcf" +checksum = "1c335bdf35728ea876506babffcfd85fa4dd66af6438f9472afc91b278946909" dependencies = [ "assert_matches", "base64 0.21.7", @@ -1964,9 +2057,9 @@ dependencies = [ "libsecp256k1", "log", "memmap2", - "num-derive", + "num-derive 0.4.2", "num-traits", - "num_enum", + "num_enum 0.7.2", "pbkdf2 0.11.0", "qstring", "qualifier_attr", @@ -1985,8 +2078,8 @@ dependencies = [ "solana-frozen-abi", "solana-frozen-abi-macro", "solana-logger", - "solana-program", - "solana-sdk-macro", + "solana-program 1.18.18", + "solana-sdk-macro 1.18.18", "thiserror", "uriparse", "wasm-bindgen", @@ -1994,15 +2087,28 @@ dependencies = [ [[package]] name = "solana-sdk-macro" -version = "1.18.17" +version = "1.18.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86c76414183a325038ff020b22c07d1e9d2da0703ddc0244acfed37ee2921d96" +checksum = "4ba67050b90454a8638913a7d5775703c0557157def04ddcc8b59c964cda8535" dependencies = [ "bs58 0.4.0", "proc-macro2", "quote", "rustversion", - "syn 2.0.68", + "syn 2.0.71", +] + +[[package]] +name = "solana-sdk-macro" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa59130529bdfa2df945762785ea6c887cc07c901f52eb67ea4dfaa9e0b9c60b" +dependencies = [ + "bs58 0.5.1", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.71", ] [[package]] @@ -2013,9 +2119,9 @@ checksum = "468aa43b7edb1f9b7b7b686d5c3aeb6630dc1708e86e31343499dd5c4d775183" [[package]] name = "solana-zk-token-sdk" -version = "1.18.17" +version = "1.18.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513407f88394e437b4ff5aad892bc5bf51a655ae2401e6e63549734d3695c46f" +checksum = "616130045004bccc9dd016fe03ac458db38bd61456f1d16f126acb60f968dcae" dependencies = [ "aes-gcm-siv", "base64 0.21.7", @@ -2027,13 +2133,13 @@ dependencies = [ "itertools", "lazy_static", "merlin", - "num-derive", + "num-derive 0.4.2", "num-traits", "rand 0.7.3", "serde", "serde_json", "sha3 0.9.1", - "solana-program", + "solana-program 1.18.18", "solana-sdk", "subtle", "thiserror", @@ -2042,39 +2148,39 @@ dependencies = [ [[package]] name = "spl-associated-token-account" -version = "3.0.2" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2e688554bac5838217ffd1fab7845c573ff106b6336bf7d290db7c98d5a8efd" +checksum = "143109d789171379e6143ef23191786dfaac54289ad6e7917cfb26b36c432b10" dependencies = [ "assert_matches", "borsh 1.5.1", - "num-derive", + "num-derive 0.4.2", "num-traits", - "solana-program", - "spl-token", + "solana-program 1.18.18", + "spl-token 4.0.0", "spl-token-2022", "thiserror", ] [[package]] name = "spl-discriminator" -version = "0.1.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daa600f2fe56f32e923261719bae640d873edadbc5237681a39b8e37bfd4d263" +checksum = "cce5d563b58ef1bb2cdbbfe0dfb9ffdc24903b10ae6a4df2d8f425ece375033f" dependencies = [ "bytemuck", - "solana-program", + "solana-program 1.18.18", "spl-discriminator-derive 0.1.2", ] [[package]] name = "spl-discriminator" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34d1814406e98b08c5cd02c1126f83fd407ad084adce0b05fda5730677822eac" +checksum = "210101376962bb22bb13be6daea34656ea1cbc248fce2164b146e39203b55e03" dependencies = [ "bytemuck", - "solana-program", + "solana-program 1.18.18", "spl-discriminator-derive 0.2.0", ] @@ -2086,7 +2192,7 @@ checksum = "07fd7858fc4ff8fb0e34090e41d7eb06a823e1057945c26d480bfc21d2338a93" dependencies = [ "quote", "spl-discriminator-syn 0.1.2", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -2097,7 +2203,7 @@ checksum = "d9e8418ea6269dcfb01c712f0444d2c75542c04448b480e87de59d2865edc750" dependencies = [ "quote", "spl-discriminator-syn 0.2.0", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -2109,7 +2215,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.68", + "syn 2.0.71", "thiserror", ] @@ -2122,66 +2228,66 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.68", + "syn 2.0.71", "thiserror", ] [[package]] name = "spl-memo" -version = "4.0.1" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58e9bae02de3405079a057fe244c867a08f92d48327d231fc60da831f94caf0a" +checksum = "f0f180b03318c3dbab3ef4e1e4d46d5211ae3c780940dd0a28695aba4b59a75a" dependencies = [ - "solana-program", + "solana-program 1.18.18", ] [[package]] name = "spl-pod" -version = "0.1.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5db7e4efb1107b0b8e52a13f035437cdcb36ef99c58f6d467f089d9b2915a" +checksum = "2881dddfca792737c0706fa0175345ab282b1b0879c7d877bad129645737c079" dependencies = [ "bytemuck", - "solana-program", + "solana-program 1.18.18", "solana-zk-token-sdk", - "spl-program-error 0.3.1", + "spl-program-error 0.3.0", ] [[package]] name = "spl-pod" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046ce669f48cf2eca1ec518916d8725596bfb655beb1c74374cf71dc6cb773c9" +checksum = "c52d84c55efeef8edcc226743dc089d7e3888b8e3474569aa3eff152b37b9996" dependencies = [ "borsh 1.5.1", "bytemuck", - "solana-program", + "solana-program 1.18.18", "solana-zk-token-sdk", - "spl-program-error 0.4.1", + "spl-program-error 0.4.4", ] [[package]] name = "spl-program-error" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e0657b6490196971d9e729520ba934911ff41fbb2cb9004463dbe23cf8b4b4f" +checksum = "249e0318493b6bcf27ae9902600566c689b7dfba9f1bdff5893e92253374e78c" dependencies = [ - "num-derive", + "num-derive 0.4.2", "num-traits", - "solana-program", + "solana-program 1.18.18", "spl-program-error-derive 0.3.2", "thiserror", ] [[package]] name = "spl-program-error" -version = "0.4.1" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49065093ea91f57b9b2bd81493ff705e2ad4e64507a07dbc02b085778e02770e" +checksum = "e45a49acb925db68aa501b926096b2164adbdcade7a0c24152af9f0742d0a602" dependencies = [ - "num-derive", + "num-derive 0.4.2", "num-traits", - "solana-program", + "solana-program 1.18.18", "spl-program-error-derive 0.4.1", "thiserror", ] @@ -2195,7 +2301,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -2207,173 +2313,144 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.68", -] - -[[package]] -name = "spl-tlv-account-resolution" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "062e148d3eab7b165582757453632ffeef490c02c86a48bfdb4988f63eefb3b9" -dependencies = [ - "bytemuck", - "solana-program", - "spl-discriminator 0.1.1", - "spl-pod 0.1.1", - "spl-program-error 0.3.1", - "spl-type-length-value 0.3.1", + "syn 2.0.71", ] [[package]] name = "spl-tlv-account-resolution" -version = "0.5.2" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f335787add7fa711819f9e7c573f8145a5358a709446fe2d24bf2a88117c90" +checksum = "fab8edfd37be5fa17c9e42c1bff86abbbaf0494b031b37957f2728ad2ff842ba" dependencies = [ "bytemuck", - "solana-program", - "spl-discriminator 0.1.1", - "spl-pod 0.1.1", - "spl-program-error 0.3.1", - "spl-type-length-value 0.3.1", + "solana-program 1.18.18", + "spl-discriminator 0.2.5", + "spl-pod 0.2.5", + "spl-program-error 0.4.4", + "spl-type-length-value 0.4.6", ] [[package]] -name = "spl-tlv-account-resolution" -version = "0.6.3" +name = "spl-token" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cace91ba08984a41556efe49cbf2edca4db2f577b649da7827d3621161784bf8" +checksum = "08459ba1b8f7c1020b4582c4edf0f5c7511a5e099a7a97570c9698d4f2337060" dependencies = [ + "arrayref", "bytemuck", - "solana-program", - "spl-discriminator 0.2.2", - "spl-pod 0.2.2", - "spl-program-error 0.4.1", - "spl-type-length-value 0.4.3", + "num-derive 0.3.3", + "num-traits", + "num_enum 0.6.1", + "solana-program 1.18.18", + "thiserror", ] [[package]] name = "spl-token" -version = "4.0.1" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ae123223633a389f95d1da9d49c2d0a50d499e7060b9624626a69e536ad2a4" +checksum = "70a0f06ac7f23dc0984931b1fe309468f14ea58e32660439c1cef19456f5d0e3" dependencies = [ "arrayref", "bytemuck", - "num-derive", + "num-derive 0.4.2", "num-traits", - "num_enum", - "solana-program", + "num_enum 0.7.2", + "solana-program 2.0.2", "thiserror", ] [[package]] name = "spl-token-2022" -version = "3.0.2" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5412f99ae7ee6e0afde00defaa354e6228e47e30c0e3adf553e2e01e6abb584" +checksum = "b01d1b2851964e257187c0bca43a0de38d0af59192479ca01ac3e2b58b1bd95a" dependencies = [ "arrayref", "bytemuck", - "num-derive", + "num-derive 0.4.2", "num-traits", - "num_enum", - "solana-program", + "num_enum 0.7.2", + "solana-program 1.18.18", "solana-security-txt", "solana-zk-token-sdk", "spl-memo", - "spl-pod 0.2.2", - "spl-token", + "spl-pod 0.2.5", + "spl-token 4.0.0", "spl-token-group-interface", "spl-token-metadata-interface", - "spl-transfer-hook-interface 0.6.3", - "spl-type-length-value 0.4.3", + "spl-transfer-hook-interface", + "spl-type-length-value 0.4.6", "thiserror", ] [[package]] name = "spl-token-group-interface" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d419b5cfa3ee8e0f2386fd7e02a33b3ec8a7db4a9c7064a2ea24849dc4a273b6" +checksum = "014817d6324b1e20c4bbc883e8ee30a5faa13e59d91d1b2b95df98b920150c17" dependencies = [ "bytemuck", - "solana-program", - "spl-discriminator 0.2.2", - "spl-pod 0.2.2", - "spl-program-error 0.4.1", + "solana-program 1.18.18", + "spl-discriminator 0.2.5", + "spl-pod 0.2.5", + "spl-program-error 0.4.4", ] [[package]] name = "spl-token-metadata-interface" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30179c47e93625680dabb620c6e7931bd12d62af390f447bc7beb4a3a9b5feee" +checksum = "f3da00495b602ebcf5d8ba8b3ecff1ee454ce4c125c9077747be49c2d62335ba" dependencies = [ "borsh 1.5.1", - "solana-program", - "spl-discriminator 0.2.2", - "spl-pod 0.2.2", - "spl-program-error 0.4.1", - "spl-type-length-value 0.4.3", + "solana-program 1.18.18", + "spl-discriminator 0.2.5", + "spl-pod 0.2.5", + "spl-program-error 0.4.4", + "spl-type-length-value 0.4.6", ] [[package]] name = "spl-transfer-hook-interface" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6dfe329fcff44cbe2eea994bd8f737f0b0a69faed39e56f9b6ee03badf7e14" -dependencies = [ - "arrayref", - "bytemuck", - "solana-program", - "spl-discriminator 0.1.1", - "spl-pod 0.1.1", - "spl-program-error 0.3.1", - "spl-tlv-account-resolution 0.5.2", - "spl-type-length-value 0.3.1", -] - -[[package]] -name = "spl-transfer-hook-interface" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a98359769cd988f7b35c02558daa56d496a7e3bd8626e61f90a7c757eedb9b" +checksum = "a9b5c08a89838e5a2931f79b17f611857f281a14a2100968a3ccef352cb7414b" dependencies = [ "arrayref", "bytemuck", - "solana-program", - "spl-discriminator 0.2.2", - "spl-pod 0.2.2", - "spl-program-error 0.4.1", - "spl-tlv-account-resolution 0.6.3", - "spl-type-length-value 0.4.3", + "solana-program 1.18.18", + "spl-discriminator 0.2.5", + "spl-pod 0.2.5", + "spl-program-error 0.4.4", + "spl-tlv-account-resolution", + "spl-type-length-value 0.4.6", ] [[package]] name = "spl-type-length-value" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f9ebd75d29c5f48de5f6a9c114e08531030b75b8ac2c557600ac7da0b73b1e8" +checksum = "a468e6f6371f9c69aae760186ea9f1a01c2908351b06a5e0026d21cfc4d7ecac" dependencies = [ "bytemuck", - "solana-program", - "spl-discriminator 0.1.1", - "spl-pod 0.1.1", - "spl-program-error 0.3.1", + "solana-program 1.18.18", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", ] [[package]] name = "spl-type-length-value" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422ce13429dbd41d2cee8a73931c05fda0b0c8ca156a8b0c19445642550bb61a" +checksum = "c872f93d0600e743116501eba2d53460e73a12c9a496875a42a7d70e034fe06d" dependencies = [ "bytemuck", - "solana-program", - "spl-discriminator 0.2.2", - "spl-pod 0.2.2", - "spl-program-error 0.4.1", + "solana-program 1.18.18", + "spl-discriminator 0.2.5", + "spl-pod 0.2.5", + "spl-program-error 0.4.4", ] [[package]] @@ -2401,9 +2478,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.68" +version = "2.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" dependencies = [ "proc-macro2", "quote", @@ -2419,7 +2496,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -2433,22 +2510,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -2472,9 +2549,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -2503,7 +2580,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.14", + "toml_edit 0.22.15", ] [[package]] @@ -2515,6 +2592,17 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow 0.5.40", +] + [[package]] name = "toml_edit" version = "0.21.1" @@ -2528,9 +2616,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.14" +version = "0.22.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" +checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1" dependencies = [ "indexmap", "serde", @@ -2625,7 +2713,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", "wasm-bindgen-shared", ] @@ -2647,7 +2735,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2668,14 +2756,26 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "wen-royalties-interface" +version = "0.1.0" +dependencies = [ + "solana-program 1.18.18", + "spl-associated-token-account", + "spl-token 6.0.0", + "spl-token-2022", + "spl-token-metadata-interface", +] + [[package]] name = "wen_new_standard" version = "0.4.2-alpha" dependencies = [ "anchor-lang", "anchor-spl", - "spl-tlv-account-resolution 0.4.0", - "spl-transfer-hook-interface 0.5.1", + "spl-tlv-account-resolution", + "spl-transfer-hook-interface", + "spl-type-length-value 0.3.0", "wen_royalty_distribution", ] @@ -2687,8 +2787,9 @@ dependencies = [ "anchor-spl", "bincode", "serde", - "spl-tlv-account-resolution 0.4.0", - "spl-transfer-hook-interface 0.5.1", + "spl-tlv-account-resolution", + "spl-transfer-hook-interface", + "spl-type-length-value 0.3.0", ] [[package]] @@ -2697,9 +2798,10 @@ version = "0.1.0" dependencies = [ "anchor-lang", "anchor-spl", - "spl-pod 0.2.2", - "spl-tlv-account-resolution 0.4.0", - "spl-transfer-hook-interface 0.5.1", + "spl-pod 0.2.5", + "spl-tlv-account-resolution", + "spl-transfer-hook-interface", + "spl-type-length-value 0.3.0", ] [[package]] @@ -2708,7 +2810,9 @@ version = "0.1.0" dependencies = [ "anchor-lang", "anchor-spl", - "spl-transfer-hook-interface 0.5.1", + "spl-transfer-hook-interface", + "spl-type-length-value 0.3.0", + "wen-royalties-interface", "wen_new_standard", "wen_royalty_distribution", ] @@ -2755,9 +2859,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -2771,51 +2875,51 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" @@ -2837,22 +2941,22 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -2872,5 +2976,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] diff --git a/Cargo.toml b/Cargo.toml index 62fad78..486a7a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["programs/*"] +members = ["programs/*", "libraries/*"] resolver = "2" [workspace.dependencies] @@ -13,9 +13,18 @@ wen_new_standard = { path = "programs/wen_new_standard", features = ["cpi"] } wen_royalty_distribution = { path = "programs/wen_royalty_distribution", features = [ "cpi", ] } -spl-transfer-hook-interface = "0.5.1" -spl-tlv-account-resolution = "0.4.0" -spl-pod = "0.2.2" +wen-royalties-interface = { path = "libraries/royalties-interface" } +spl-transfer-hook-interface = "0.6.5" +spl-tlv-account-resolution = "0.6.5" +spl-pod = "0.2.5" +spl-type-length-value = "=0.3.0" +solana-program = "1.18.18" +spl-associated-token-account = "3.0.2" +spl-token = { version = "6.0.0", features = ["no-entrypoint"] } +spl-token-2022 = { version = "3.0.2", features = ["no-entrypoint"] } +spl-token-metadata-interface = "0.3.5" +solana-system-program = "1.18.18" + [profile.release] overflow-checks = true diff --git a/idls/wen_transfer_guard.json b/idls/wen_transfer_guard.json new file mode 100644 index 0000000..383b2ff --- /dev/null +++ b/idls/wen_transfer_guard.json @@ -0,0 +1,645 @@ +{ + "address": "LockdqYQ9X2kwtWB99ioSbxubAmEi8o9jqYwbXgrrRw", + "metadata": { + "name": "wen_transfer_guard", + "version": "0.1.0", + "spec": "0.1.0", + "description": "A generic transfer hook implementation for Token Extensions" + }, + "instructions": [ + { + "name": "create_guard", + "discriminator": [251, 254, 17, 198, 219, 218, 154, 99], + "accounts": [ + { + "name": "guard", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 119, 101, 110, 95, 116, 111, 107, 101, 110, 95, 116, 114, 97, + 110, 115, 102, 101, 114, 95, 103, 117, 97, 114, 100 + ] + }, + { + "kind": "const", + "value": [103, 117, 97, 114, 100, 95, 118, 49] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "mint", + "writable": true, + "signer": true + }, + { + "name": "mint_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "guard_authority" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "guard_authority", + "signer": true + }, + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "token_program", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "args", + "type": { + "defined": { + "name": "CreateGuardArgs" + } + } + } + ] + }, + { + "name": "execute", + "discriminator": [105, 37, 101, 197, 75, 251, 102, 26], + "accounts": [ + { + "name": "source_account" + }, + { + "name": "mint" + }, + { + "name": "destination_account" + }, + { + "name": "owner_delegate" + }, + { + "name": "extra_metas_account", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 101, 120, 116, 114, 97, 45, 97, 99, 99, 111, 117, 110, 116, + 45, 109, 101, 116, 97, 115 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "guard", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 119, 101, 110, 95, 116, 111, 107, 101, 110, 95, 116, 114, 97, + 110, 115, 102, 101, 114, 95, 103, 117, 97, 114, 100 + ] + }, + { + "kind": "const", + "value": [103, 117, 97, 114, 100, 95, 118, 49] + }, + { + "kind": "account", + "path": "guard.mint", + "account": "GuardV1" + } + ] + } + }, + { + "name": "instruction_sysvar_account", + "address": "Sysvar1nstructions1111111111111111111111111" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "initialize", + "discriminator": [43, 34, 13, 49, 167, 88, 235, 235], + "accounts": [ + { + "name": "extra_metas_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 101, 120, 116, 114, 97, 45, 97, 99, 99, 111, 117, 110, 116, + 45, 109, 101, 116, 97, 115 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "guard", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 119, 101, 110, 95, 116, 111, 107, 101, 110, 95, 116, 114, 97, + 110, 115, 102, 101, 114, 95, 103, 117, 97, 114, 100 + ] + }, + { + "kind": "const", + "value": [103, 117, 97, 114, 100, 95, 118, 49] + }, + { + "kind": "account", + "path": "guard.mint", + "account": "GuardV1" + } + ] + } + }, + { + "name": "mint" + }, + { + "name": "transfer_hook_authority", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "payer", + "writable": true, + "signer": true + } + ], + "args": [] + }, + { + "name": "update_guard", + "discriminator": [51, 38, 175, 180, 25, 249, 39, 24], + "accounts": [ + { + "name": "guard", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 119, 101, 110, 95, 116, 111, 107, 101, 110, 95, 116, 114, 97, + 110, 115, 102, 101, 114, 95, 103, 117, 97, 114, 100 + ] + }, + { + "kind": "const", + "value": [103, 117, 97, 114, 100, 95, 118, 49] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "mint" + }, + { + "name": "token_account", + "pda": { + "seeds": [ + { + "kind": "account", + "path": "guard_authority" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, + 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, + 219, 233, 248, 89 + ] + } + } + }, + { + "name": "guard_authority", + "signer": true + }, + { + "name": "token_program", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "args", + "type": { + "defined": { + "name": "UpdateGuardArgs" + } + } + } + ] + } + ], + "accounts": [ + { + "name": "GuardV1", + "discriminator": [185, 149, 156, 78, 245, 108, 172, 68] + } + ], + "errors": [ + { + "code": 6000, + "name": "CpiRuleEnforcementFailed", + "msg": "Cpi Rule Enforcement Failed" + }, + { + "code": 6001, + "name": "TransferAmountRuleEnforceFailed", + "msg": "Transfer Amount Rule Enforce Failed" + }, + { + "code": 6002, + "name": "MetadataFieldDoesNotExist", + "msg": "Metadata Field Does Not Exist" + }, + { + "code": 6003, + "name": "MetadataFieldDoesNotPass", + "msg": "Metadata Field Does Not Pass" + }, + { + "code": 6004, + "name": "GuardTokenAmountShouldBeAtLeastOne", + "msg": "Guard token amount should be at least 1" + }, + { + "code": 6005, + "name": "NotOwnedByToken2022Program", + "msg": "Not owned by token 2022 program" + }, + { + "code": 6006, + "name": "MustBeInitializedByTransferHookAuthority", + "msg": "Must be initialized by Transfer Hook Authority" + }, + { + "code": 6007, + "name": "MintAssignedTransferHookProgramIsNotThisOne", + "msg": "Mint's assigned Transfer Hook Program is not this one" + } + ], + "types": [ + { + "name": "CpiRule", + "docs": [ + "Controls which protocols can interact with the token by", + "enforcing Allow and Deny lists." + ], + "type": { + "kind": "enum", + "variants": [ + { + "name": "Allow", + "fields": [ + { + "vec": "pubkey" + } + ] + }, + { + "name": "Deny", + "fields": [ + { + "vec": "pubkey" + } + ] + } + ] + } + }, + { + "name": "CreateGuardArgs", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + }, + { + "name": "uri", + "type": "string" + }, + { + "name": "cpi_rule", + "type": { + "option": { + "defined": { + "name": "CpiRule" + } + } + } + }, + { + "name": "transfer_amount_rule", + "type": { + "option": { + "defined": { + "name": "TransferAmountRule" + } + } + } + }, + { + "name": "additional_fields_rule", + "type": { + "vec": { + "defined": { + "name": "MetadataAdditionalFieldRule" + } + } + } + } + ] + } + }, + { + "name": "GuardV1", + "type": { + "kind": "struct", + "fields": [ + { + "name": "mint", + "docs": [ + "Mint token representing the guard, do not confuse with the mint of the token being transferred." + ], + "type": "pubkey" + }, + { + "name": "bump", + "docs": ["Bump seed for the guard account."], + "type": "u8" + }, + { + "name": "cpi_rule", + "docs": ["CPI ruleset for the guard."], + "type": { + "option": { + "defined": { + "name": "CpiRule" + } + } + } + }, + { + "name": "transfer_amount_rule", + "docs": ["Transfer amount ruleset for the guard."], + "type": { + "option": { + "defined": { + "name": "TransferAmountRule" + } + } + } + }, + { + "name": "additional_fields_rule", + "docs": ["Additional fields ruleset for the guard."], + "type": { + "vec": { + "defined": { + "name": "MetadataAdditionalFieldRule" + } + } + } + } + ] + } + }, + { + "name": "MetadataAdditionalFieldRestriction", + "docs": [ + "Inner enum for the MetadataAdditionalFieldRestriction enum.", + "* Includes - The field must include one of the values in the vector.", + "* Excludes - The field must not include any of the values in the vector." + ], + "type": { + "kind": "enum", + "variants": [ + { + "name": "Includes", + "fields": [ + { + "vec": "string" + } + ] + }, + { + "name": "Excludes", + "fields": [ + { + "vec": "string" + } + ] + } + ] + } + }, + { + "name": "MetadataAdditionalFieldRule", + "docs": [ + "Enforces rules on a single additional field in the mint metadata.", + "The field must exist and the value must pass the restriction." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "field", + "type": "string" + }, + { + "name": "value_restrictions", + "type": { + "option": { + "defined": { + "name": "MetadataAdditionalFieldRestriction" + } + } + } + } + ] + } + }, + { + "name": "TransferAmountRule", + "docs": [ + "Enforces rules on the amount of tokens being transferred.", + "The rules can be above, below, equal to, or within a range." + ], + "type": { + "kind": "enum", + "variants": [ + { + "name": "Above", + "fields": ["u64"] + }, + { + "name": "Below", + "fields": ["u64"] + }, + { + "name": "Equal", + "fields": ["u64"] + }, + { + "name": "Rang", + "fields": ["u64", "u64"] + } + ] + } + }, + { + "name": "UpdateGuardArgs", + "type": { + "kind": "struct", + "fields": [ + { + "name": "cpi_rule", + "type": { + "option": { + "defined": { + "name": "CpiRule" + } + } + } + }, + { + "name": "transfer_amount_rule", + "type": { + "option": { + "defined": { + "name": "TransferAmountRule" + } + } + } + }, + { + "name": "additional_fields_rule", + "type": { + "vec": { + "defined": { + "name": "MetadataAdditionalFieldRule" + } + } + } + } + ] + } + } + ], + "constants": [ + { + "name": "EXTRA_ACCOUNT_METAS", + "type": { + "array": ["u8", 19] + }, + "value": "[101, 120, 116, 114, 97, 45, 97, 99, 99, 111, 117, 110, 116, 45, 109, 101, 116, 97, 115]" + }, + { + "name": "GUARD_V1", + "type": { + "array": ["u8", 8] + }, + "value": "[103, 117, 97, 114, 100, 95, 118, 49]" + }, + { + "name": "WEN_TOKEN_GUARD", + "type": { + "array": ["u8", 24] + }, + "value": "[119, 101, 110, 95, 116, 111, 107, 101, 110, 95, 116, 114, 97, 110, 115, 102, 101, 114, 95, 103, 117, 97, 114, 100]" + } + ] +} diff --git a/libraries/royalties-interface/Cargo.toml b/libraries/royalties-interface/Cargo.toml new file mode 100644 index 0000000..55c3e7f --- /dev/null +++ b/libraries/royalties-interface/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "wen-royalties-interface" +version = "0.1.0" +edition = "2021" + +[dependencies] +solana-program.workspace = true +spl-associated-token-account.workspace = true +spl-token.workspace = true +spl-token-2022.workspace = true +spl-token-metadata-interface.workspace = true diff --git a/libraries/royalties-interface/src/lib.rs b/libraries/royalties-interface/src/lib.rs new file mode 100644 index 0000000..4b56565 --- /dev/null +++ b/libraries/royalties-interface/src/lib.rs @@ -0,0 +1,121 @@ +use solana_program::{ + account_info::AccountInfo, instruction::Instruction, program_error::ProgramError, + pubkey::Pubkey, system_instruction::transfer, +}; +use spl_associated_token_account::{ + get_associated_token_address_with_program_id, + instruction::create_associated_token_account_idempotent, +}; +use spl_token::{solana_program::program_pack::Pack, state::Mint as TokenKegMint}; +use spl_token_2022::{ + extension::{BaseStateWithExtensions, StateWithExtensions}, + instruction::transfer_checked, + state::Mint as Token2022Mint, +}; +use spl_token_metadata_interface::state::TokenMetadata; +use std::str::FromStr; + +pub const ROYALTY_BASIS_POINTS_FIELD: &str = "royalty_basis_points"; + +pub fn calculate_royalties( + mint: &AccountInfo, + amount: u64, +) -> Result<(u64, TokenMetadata), ProgramError> { + let mint_account_data = mint.try_borrow_data()?; + let mint_data = StateWithExtensions::::unpack(&mint_account_data)?; + let metadata = mint_data.get_variable_len_extension::()?; + + // get royalty basis points from metadata Vec(String, String) + let royalty_basis_points = metadata + .additional_metadata + .iter() + .find(|(key, _)| key == ROYALTY_BASIS_POINTS_FIELD) + .map(|(_, value)| u64::from_str(value).unwrap()) + .unwrap_or(0); + + Ok((((amount * royalty_basis_points) / 10000), metadata)) +} + +pub fn generate_royalty_ixs( + amount: u64, + mint: &AccountInfo, + payment_mint: &AccountInfo, + buyer: &Pubkey, + token_program_id: &Option, + is_spl: bool, +) -> Result, ProgramError> { + let (royalty_amount, metadata) = calculate_royalties(mint, amount)?; + + let creators = metadata + .additional_metadata + .iter() + .filter(|(key, _)| key != ROYALTY_BASIS_POINTS_FIELD) + .filter_map(|(key, value)| match Pubkey::from_str(key) { + Ok(pubkey) => Some((pubkey, u8::from_str(value).unwrap_or(0))), + Err(_) => None, + }) + .collect::>(); + + let mut instructions = vec![]; + + for (creator, creator_share) in creators { + let creator_share_amount = royalty_amount + .checked_mul(creator_share.into()) + .and_then(|product| product.checked_div(100)) + .ok_or(ProgramError::ArithmeticOverflow)?; + + let transfer_instruction = if !is_spl { + transfer(buyer, &creator, creator_share_amount) + } else { + let payment_mint_data = payment_mint.try_borrow_data()?; + let decimals = if payment_mint + .key + .to_string() + .eq(&spl_token::id().to_string()) + { + let TokenKegMint { decimals, .. } = + TokenKegMint::unpack(&payment_mint_data).unwrap(); + decimals + } else { + let state_data = StateWithExtensions::::unpack(&payment_mint_data)?; + state_data.base.decimals + }; + + if token_program_id.is_none() { + return Err(ProgramError::IncorrectProgramId.into()); + } + let token_program_id = token_program_id.clone().unwrap().key; + let source_token_account = get_associated_token_address_with_program_id( + buyer, + payment_mint.key, + token_program_id, + ); + let destination_token_account = get_associated_token_address_with_program_id( + &creator, + payment_mint.key, + token_program_id, + ); + instructions.push(create_associated_token_account_idempotent( + buyer, + &creator, + payment_mint.key, + token_program_id, + )); + + transfer_checked( + token_program_id, + &source_token_account, + payment_mint.key, + &destination_token_account, + buyer, + &[], + creator_share_amount, + decimals, + )? + }; + + instructions.push(transfer_instruction) + } + + Ok(instructions) +} diff --git a/programs/wen_new_standard/Cargo.toml b/programs/wen_new_standard/Cargo.toml index 7e0ef10..60d1890 100644 --- a/programs/wen_new_standard/Cargo.toml +++ b/programs/wen_new_standard/Cargo.toml @@ -28,3 +28,4 @@ anchor-spl.workspace = true wen_royalty_distribution.workspace = true spl-transfer-hook-interface.workspace = true spl-tlv-account-resolution.workspace = true +spl-type-length-value.workspace = true diff --git a/programs/wen_new_standard/src/errors.rs b/programs/wen_new_standard/src/errors.rs index 345d7a2..8231969 100644 --- a/programs/wen_new_standard/src/errors.rs +++ b/programs/wen_new_standard/src/errors.rs @@ -29,3 +29,13 @@ pub enum MintErrors { #[msg("Invalid Token group member mint")] InvalidTokenGroupMemberMint, } + +#[error_code] +pub enum ExtraAccountMetaListErrors { + #[msg("Extra Account Meta is initialized already")] + ExtraAccountMetaAlreadyInitialized, + #[msg("Invalid extra account meta address")] + InvalidExtraAccountMeta, + #[msg("Invalid guard account provided")] + InvalidGuardAccount, +} diff --git a/programs/wen_new_standard/src/instructions/mint/royalties/add.rs b/programs/wen_new_standard/src/instructions/mint/royalties/add.rs index 7074c18..c5f0ccd 100644 --- a/programs/wen_new_standard/src/instructions/mint/royalties/add.rs +++ b/programs/wen_new_standard/src/instructions/mint/royalties/add.rs @@ -1,3 +1,4 @@ +use anchor_lang::system_program::{create_account, CreateAccount}; use anchor_lang::{prelude::*, solana_program::entrypoint::ProgramResult}; use spl_tlv_account_resolution::state::ExtraAccountMetaList; @@ -7,11 +8,14 @@ use anchor_spl::token_interface::{ }; use spl_transfer_hook_interface::instruction::ExecuteInstruction; +use crate::wen_transfer_guard::cpi::accounts::Initialize; +use crate::wen_transfer_guard::{cpi::initialize, ID as TRANSFER_GUARD_PROGRAM_ID}; use crate::{ get_approve_account_pda, get_meta_list, get_meta_list_size, - update_account_lamports_to_minimum_balance, MetadataErrors, UpdateRoyaltiesArgs, - META_LIST_ACCOUNT_SEED, ROYALTY_BASIS_POINTS_FIELD, + update_account_lamports_to_minimum_balance, ExtraAccountMetaListErrors, MetadataErrors, + UpdateRoyaltiesArgs, META_LIST_ACCOUNT_SEED, ROYALTY_BASIS_POINTS_FIELD, }; +use crate::{utils::*, ID as WNS_PROGRAM_ID}; #[derive(Accounts)] #[instruction(args: UpdateRoyaltiesArgs)] @@ -26,13 +30,7 @@ pub struct AddRoyalties<'info> { )] pub mint: Box>, /// CHECK: This account's data is a buffer of TLV data - #[account( - init, - space = get_meta_list_size(get_approve_account_pda(mint.key())), - seeds = [META_LIST_ACCOUNT_SEED, mint.key().as_ref()], - bump, - payer = payer, - )] + #[account(mut)] pub extra_metas_account: UncheckedAccount<'info>, pub system_program: Program<'info, System>, pub token_program: Program<'info, Token2022>, @@ -50,19 +48,28 @@ impl<'info> AddRoyalties<'info> { Ok(()) } - fn update_transfer_hook_program_id(&self) -> Result<()> { + fn update_transfer_hook_program_id(&self, hook_program_id: Pubkey) -> Result<()> { let cpi_accounts = TransferHookUpdate { token_program_id: self.token_program.to_account_info(), mint: self.mint.to_account_info(), authority: self.authority.to_account_info(), }; let cpi_ctx = CpiContext::new(self.token_program.to_account_info(), cpi_accounts); - transfer_hook_update(cpi_ctx, Some(crate::id()))?; + transfer_hook_update(cpi_ctx, Some(hook_program_id))?; Ok(()) } } -pub fn handler(ctx: Context, args: UpdateRoyaltiesArgs) -> Result<()> { +pub fn handler<'a, 'b, 'c, 'info>( + ctx: Context<'a, 'b, 'c, 'info, AddRoyalties<'info>>, + args: UpdateRoyaltiesArgs, +) -> Result<()> { + let mint = &ctx.accounts.mint; + let extra_metas_account = &ctx.accounts.extra_metas_account; + let system_program = &ctx.accounts.system_program; + let payer = &ctx.accounts.payer; + let authority = &ctx.accounts.authority; + // validate that the fee_basis_point is less than 10000 (100%) require!( args.royalty_basis_points <= 10000, @@ -91,14 +98,88 @@ pub fn handler(ctx: Context, args: UpdateRoyaltiesArgs) -> Result< return Err(MetadataErrors::CreatorShareInvalid.into()); } - // initialize the extra metas account - let extra_metas_account = &ctx.accounts.extra_metas_account; - let metas = get_meta_list(get_approve_account_pda(ctx.accounts.mint.key())); - let mut data = extra_metas_account.try_borrow_mut_data()?; - ExtraAccountMetaList::init::(&mut data, &metas)?; + let mint_key = mint.key(); + if !extra_metas_account.data_is_empty() { + return err!(ExtraAccountMetaListErrors::ExtraAccountMetaAlreadyInitialized); + } - // add metadata program as the transfer hook program - ctx.accounts.update_transfer_hook_program_id()?; + let transfer_hook_program_account_info = ctx.remaining_accounts.get(0); + let (transfer_hook_program_id, bump) = match transfer_hook_program_account_info { + Some(transfer_hook_program) => { + if transfer_hook_program.key.eq(&TRANSFER_GUARD_PROGRAM_ID) { + let bump = verify_extra_meta_account( + &mint.key(), + &extra_metas_account.key(), + &TRANSFER_GUARD_PROGRAM_ID, + )?; + (TRANSFER_GUARD_PROGRAM_ID, bump) + } else { + let bump = verify_extra_meta_account( + &mint.key(), + &extra_metas_account.key(), + &WNS_PROGRAM_ID, + )?; + (WNS_PROGRAM_ID, bump) + } + } + None => { + let bump = verify_extra_meta_account( + &mint.key(), + &extra_metas_account.key(), + &WNS_PROGRAM_ID, + )?; + (WNS_PROGRAM_ID, bump) + } + }; + + // add metadata program/transfer guard program as the transfer hook program + ctx.accounts + .update_transfer_hook_program_id(transfer_hook_program_id)?; + + if transfer_hook_program_id.eq(&WNS_PROGRAM_ID) { + let signer_seeds: &[&[&[u8]]] = &[&[META_LIST_ACCOUNT_SEED, mint_key.as_ref(), &[bump]]]; + let account_size = + ExtraAccountMetaList::size_of(get_meta_list_size(get_approve_account_pda(mint_key)))?; + let lamports = Rent::get()?.minimum_balance(account_size); + + // create ExtraAccountMetaList account + create_account( + CpiContext::new( + ctx.accounts.system_program.to_account_info(), + CreateAccount { + from: ctx.accounts.payer.to_account_info(), + to: ctx.accounts.extra_metas_account.to_account_info(), + }, + ) + .with_signer(signer_seeds), + lamports, + account_size as u64, + ctx.program_id, + )?; + + // initialize the extra metas account + let metas = get_meta_list(get_approve_account_pda(mint_key)); + let mut data = extra_metas_account.try_borrow_mut_data()?; + ExtraAccountMetaList::init::(&mut data, &metas)?; + } else { + let transfer_guard_program_info = transfer_hook_program_account_info.unwrap(); + let guard_account_info = ctx.remaining_accounts.get(1); + if guard_account_info.is_none() { + return err!(ExtraAccountMetaListErrors::InvalidGuardAccount); + } + let guard_account_info = guard_account_info.unwrap(); + initialize(CpiContext::new( + transfer_guard_program_info.clone(), + Initialize { + extra_metas_account: extra_metas_account.to_account_info(), + guard: guard_account_info.clone(), + mint: mint.to_account_info(), + payer: payer.to_account_info(), + system_program: system_program.to_account_info(), + transfer_hook_authority: authority.to_account_info(), + }, + ))?; + } // transfer minimum rent to mint account update_account_lamports_to_minimum_balance( diff --git a/programs/wen_new_standard/src/lib.rs b/programs/wen_new_standard/src/lib.rs index f68aa3e..a2c53ab 100644 --- a/programs/wen_new_standard/src/lib.rs +++ b/programs/wen_new_standard/src/lib.rs @@ -14,6 +14,8 @@ pub use utils::*; declare_id!("wns1gDLt8fgLcGhWi5MqAqgXpwEP1JftKE9eZnXS1HM"); +declare_program!(wen_transfer_guard); + #[program] pub mod wen_new_standard { use super::*; @@ -62,7 +64,10 @@ pub mod wen_new_standard { } /// add royalties to mint - pub fn add_royalties(ctx: Context, args: UpdateRoyaltiesArgs) -> Result<()> { + pub fn add_royalties<'a, 'b, 'c, 'info>( + ctx: Context<'a, 'b, 'c, 'info, AddRoyalties<'info>>, + args: UpdateRoyaltiesArgs, + ) -> Result<()> { instructions::mint::royalties::add::handler(ctx, args) } diff --git a/programs/wen_new_standard/src/utils.rs b/programs/wen_new_standard/src/utils.rs index 72ed3d6..504a149 100644 --- a/programs/wen_new_standard/src/utils.rs +++ b/programs/wen_new_standard/src/utils.rs @@ -1,6 +1,6 @@ -use crate::{APPROVE_ACCOUNT_SEED, META_LIST_ACCOUNT_SEED}; +use crate::{ExtraAccountMetaListErrors, APPROVE_ACCOUNT_SEED, META_LIST_ACCOUNT_SEED}; use anchor_lang::{ - prelude::Result, + prelude::*, solana_program::{ account_info::AccountInfo, instruction::{get_stack_height, TRANSACTION_LEVEL_STACK_HEIGHT}, @@ -27,6 +27,25 @@ pub fn get_bump_in_seed_form(bump: &u8) -> [u8; 1] { [bump_val] } +pub fn verify_extra_meta_account( + mint: &Pubkey, + extra_metas_account: &Pubkey, + transfer_hook_program_id: &Pubkey, +) -> Result { + let (expected_extra_account_metas, bump) = Pubkey::find_program_address( + &[META_LIST_ACCOUNT_SEED, mint.as_ref()], + transfer_hook_program_id, + ); + + require_eq!( + &expected_extra_account_metas, + extra_metas_account, + ExtraAccountMetaListErrors::InvalidExtraAccountMeta + ); + + Ok(bump) +} + pub fn update_account_lamports_to_minimum_balance<'info>( account: AccountInfo<'info>, payer: AccountInfo<'info>, diff --git a/programs/wen_royalty_distribution/Cargo.toml b/programs/wen_royalty_distribution/Cargo.toml index 4c7bee3..852baf9 100644 --- a/programs/wen_royalty_distribution/Cargo.toml +++ b/programs/wen_royalty_distribution/Cargo.toml @@ -24,5 +24,6 @@ anchor-lang = { workspace = true, features = ["init-if-needed"] } anchor-spl.workspace = true spl-transfer-hook-interface.workspace = true spl-tlv-account-resolution.workspace = true +spl-type-length-value.workspace = true bincode = "1.3.3" serde = "1.0.203" diff --git a/programs/wen_royalty_distribution/src/instructions/claim.rs b/programs/wen_royalty_distribution/src/instructions/claim.rs index 2b9cc58..055650c 100644 --- a/programs/wen_royalty_distribution/src/instructions/claim.rs +++ b/programs/wen_royalty_distribution/src/instructions/claim.rs @@ -116,7 +116,6 @@ pub fn handler(ctx: Context) -> Result<()> { if new_data_size < current_len { let account_info = ctx.accounts.distribution.to_account_info(); let current_len = account_info.data_len(); - let space_decrease = current_len - new_data_size; let rent_decrease = Rent::get()? .minimum_balance(current_len) .checked_sub(Rent::get()?.minimum_balance(new_data_size)) diff --git a/programs/wen_royalty_distribution/src/instructions/initialize.rs b/programs/wen_royalty_distribution/src/instructions/initialize.rs index 9b01c3a..99eb2d1 100644 --- a/programs/wen_royalty_distribution/src/instructions/initialize.rs +++ b/programs/wen_royalty_distribution/src/instructions/initialize.rs @@ -1,12 +1,10 @@ -use std::str::FromStr; - use anchor_lang::prelude::*; use anchor_spl::token_interface::Mint; use crate::DistributionAccount; #[derive(Accounts)] -#[instruction(payment_mint: String)] +#[instruction(payment_mint: Pubkey)] pub struct InitializeDistribution<'info> { #[account(mut)] pub payer: Signer<'info>, @@ -26,10 +24,9 @@ pub struct InitializeDistribution<'info> { pub system_program: Program<'info, System>, } -pub fn handler(ctx: Context, payment_mint: String) -> Result<()> { - let payment_key = Pubkey::from_str(&payment_mint).unwrap_or_default(); +pub fn handler(ctx: Context, payment_mint: Pubkey) -> Result<()> { ctx.accounts .distribution_account - .initialize_account_data(ctx.accounts.group_mint.key(), payment_key); + .initialize_account_data(ctx.accounts.group_mint.key(), payment_mint); Ok(()) } diff --git a/programs/wen_royalty_distribution/src/lib.rs b/programs/wen_royalty_distribution/src/lib.rs index eba037c..bb6e007 100644 --- a/programs/wen_royalty_distribution/src/lib.rs +++ b/programs/wen_royalty_distribution/src/lib.rs @@ -20,7 +20,7 @@ pub mod wen_royalty_distribution { /// Initializes a new distribution account. pub fn initialize_distribution( ctx: Context, - payment_mint: String, + payment_mint: Pubkey, ) -> Result<()> { instructions::initialize::handler(ctx, payment_mint) } diff --git a/programs/wen_transfer_guard/Cargo.toml b/programs/wen_transfer_guard/Cargo.toml index f2d8851..54e0215 100644 --- a/programs/wen_transfer_guard/Cargo.toml +++ b/programs/wen_transfer_guard/Cargo.toml @@ -22,3 +22,4 @@ anchor-spl.workspace = true spl-pod.workspace = true spl-transfer-hook-interface.workspace = true spl-tlv-account-resolution.workspace = true +spl-type-length-value.workspace = true diff --git a/programs/wen_transfer_guard/src/instructions/execute.rs b/programs/wen_transfer_guard/src/instructions/execute.rs index b6a0e9e..4b1904e 100644 --- a/programs/wen_transfer_guard/src/instructions/execute.rs +++ b/programs/wen_transfer_guard/src/instructions/execute.rs @@ -18,7 +18,7 @@ use crate::{ pub struct Execute<'info> { #[account( token::mint = mint, - token::authority = owner_delegate, + // token::authority = owner_delegate, token::token_program = TOKEN_2022_PROGRAM_ID, )] pub source_account: Box>, @@ -31,8 +31,8 @@ pub struct Execute<'info> { token::token_program = TOKEN_2022_PROGRAM_ID, )] pub destination_account: Box>, - - pub owner_delegate: SystemAccount<'info>, + /// CHECK: can be any type of account, checked with a constraint above + pub owner_delegate: UncheckedAccount<'info>, /// CHECK: This account's data is a buffer of TLV data #[account( diff --git a/programs/wen_transfer_guard/src/instructions/initialize.rs b/programs/wen_transfer_guard/src/instructions/initialize.rs index 3271681..3c58a4c 100644 --- a/programs/wen_transfer_guard/src/instructions/initialize.rs +++ b/programs/wen_transfer_guard/src/instructions/initialize.rs @@ -40,7 +40,7 @@ pub struct Initialize<'info> { )] pub mint: Box>, - #[account(mut)] + #[account()] pub transfer_hook_authority: Signer<'info>, pub system_program: Program<'info, System>, diff --git a/programs/wen_wns_marketplace/Cargo.toml b/programs/wen_wns_marketplace/Cargo.toml index f942297..11eb9f8 100644 --- a/programs/wen_wns_marketplace/Cargo.toml +++ b/programs/wen_wns_marketplace/Cargo.toml @@ -15,10 +15,10 @@ no-entrypoint = [] no-idl = [] no-log-ix-name = [] idl-build = [ - "anchor-lang/idl-build", - "anchor-spl/idl-build", - "wen_new_standard/idl-build", - "wen_royalty_distribution/idl-build" + "anchor-lang/idl-build", + "anchor-spl/idl-build", + "wen_new_standard/idl-build", + "wen_royalty_distribution/idl-build", ] [dependencies] @@ -27,3 +27,5 @@ anchor-spl.workspace = true wen_new_standard.workspace = true wen_royalty_distribution.workspace = true spl-transfer-hook-interface.workspace = true +spl-type-length-value.workspace = true +wen-royalties-interface.workspace = true diff --git a/programs/wen_wns_marketplace/src/errors.rs b/programs/wen_wns_marketplace/src/errors.rs index 81324e1..b7cdc12 100644 --- a/programs/wen_wns_marketplace/src/errors.rs +++ b/programs/wen_wns_marketplace/src/errors.rs @@ -10,4 +10,8 @@ pub enum WenWnsMarketplaceError { InvalidPaymentTokenAccount, #[msg("Arithmetic error")] ArithmeticError, + #[msg("Creator missing for royalty transfer")] + CreatorMissing, + #[msg("Creator token account missing for royalty transfer")] + CreatorTokenAccountMissing, } diff --git a/programs/wen_wns_marketplace/src/instructions/listing/buy.rs b/programs/wen_wns_marketplace/src/instructions/listing/buy.rs index 13bc8c2..7d72886 100644 --- a/programs/wen_wns_marketplace/src/instructions/listing/buy.rs +++ b/programs/wen_wns_marketplace/src/instructions/listing/buy.rs @@ -17,6 +17,7 @@ use wen_new_standard::{ }, program::WenNewStandard, }; +use wen_royalties_interface::calculate_royalties; use wen_royalty_distribution::{program::WenRoyaltyDistribution, DistributionAccount}; use crate::constants::*; @@ -147,7 +148,8 @@ pub fn handler(ctx: Context, args: FulfillListingArgs) -> Result ))?; // Transfer (listing_amount - royalty) to seller - let royalty_funds = calculate_royalties(&ctx.accounts.mint.to_account_info(), args.buy_amount)?; + let (royalty_funds, _) = + calculate_royalties(&ctx.accounts.mint.to_account_info(), args.buy_amount)?; let funds_to_send = listing .listing_amount diff --git a/programs/wen_wns_marketplace/src/instructions/listing/buy_transfer_guard.rs b/programs/wen_wns_marketplace/src/instructions/listing/buy_transfer_guard.rs new file mode 100644 index 0000000..a9d267a --- /dev/null +++ b/programs/wen_wns_marketplace/src/instructions/listing/buy_transfer_guard.rs @@ -0,0 +1,319 @@ +use anchor_lang::{ + prelude::*, + solana_program::{program::invoke, sysvar}, + system_program::{transfer, Transfer}, +}; +use anchor_spl::{ + associated_token::AssociatedToken, + token_2022::{ + spl_token_2022::{extension::StateWithExtensions, state::Mint as StateMint}, + transfer_checked, Token2022, TransferChecked, + }, + token_interface::{Mint, TokenAccount, TokenInterface}, +}; +use wen_new_standard::{ + cpi::{accounts::ThawDelegatedAccount, thaw_mint_account}, + program::WenNewStandard, + wen_transfer_guard::program::WenTransferGuard, +}; +use wen_royalties_interface::{calculate_royalties, generate_royalty_ixs}; + +use crate::constants::*; +use crate::errors::*; +use crate::state::*; +use crate::utils::*; + +#[derive(Accounts)] +#[instruction(args: FulfillListingTransferGuardArgs)] +pub struct FulfillListingTransferGuard<'info> { + #[account(mut)] + pub payer: Signer<'info>, + + #[account( + mut, + seeds = [ + MARKETPLACE, + LISTING, + listing.seller.as_ref(), + listing.mint.as_ref(), + ], + bump = listing.bump, + has_one = mint, + has_one = seller, + has_one = seller_token_account, + constraint = args.buy_amount.eq(&args.buy_amount) @ WenWnsMarketplaceError::ListingAmountMismatch + )] + pub listing: Account<'info, Listing>, + + /// CHECK: Could be SOL or SPL, checked in distribution program + pub payment_mint: UncheckedAccount<'info>, + + #[account(mut)] + pub buyer: Signer<'info>, + + #[account(mut)] + pub mint: InterfaceAccount<'info, Mint>, + + #[account( + mut, + token::mint = mint, + token::authority = seller, + )] + pub seller_token_account: InterfaceAccount<'info, TokenAccount>, + + #[account( + init_if_needed, + payer = payer, + associated_token::mint = mint, + associated_token::authority = buyer, + )] + pub buyer_token_account: InterfaceAccount<'info, TokenAccount>, + + #[account(mut)] + pub seller: SystemAccount<'info>, + + /// CHECK: Checked inside WNS program + pub manager: UncheckedAccount<'info>, + /// CHECK: Checked inside Token extensions program + pub extra_metas_account: UncheckedAccount<'info>, + /// CHECK: Checked inside Transfer Guard program + pub guard_account: UncheckedAccount<'info>, + + pub wns_program: Program<'info, WenNewStandard>, + pub wen_transfer_guard_program: Program<'info, WenTransferGuard>, + pub associated_token_program: Program<'info, AssociatedToken>, + pub token_program: Program<'info, Token2022>, + pub payment_token_program: Option>, + pub system_program: Program<'info, System>, + /// CHECK: Sysvar Instructions check + pub sysvar_instructions: UncheckedAccount<'info>, + + /* Optional accounts */ + #[account( + mut, + token::authority = seller, + token::mint = payment_mint, + token::token_program = payment_token_program + )] + pub seller_payment_token_account: Option>>, + #[account( + mut, + token::authority = buyer, + token::mint = payment_mint, + token::token_program = payment_token_program + )] + pub buyer_payment_token_account: Option>>, +} + +pub fn handler<'a, 'b, 'c, 'info>( + ctx: Context<'a, 'b, 'c, 'info, FulfillListingTransferGuard<'info>>, + args: FulfillListingTransferGuardArgs, +) -> Result<()> { + let sysvar_instructions = &ctx.accounts.sysvar_instructions; + require_eq!(sysvar_instructions.key, &sysvar::instructions::id()); + + let listing = &mut ctx.accounts.listing; + + let is_payment_mint_spl = ctx.accounts.payment_mint.key.ne(&Pubkey::default()); + + let signer_seeds: &[&[&[u8]]] = &[&[ + MARKETPLACE, + LISTING, + listing.seller.as_ref(), + listing.mint.as_ref(), + &[listing.bump], + ]]; + + // Thaw NFT + thaw_mint_account(CpiContext::new_with_signer( + ctx.accounts.wns_program.to_account_info(), + ThawDelegatedAccount { + delegate_authority: listing.to_account_info(), + manager: ctx.accounts.manager.to_account_info(), + mint: ctx.accounts.mint.to_account_info(), + mint_token_account: ctx.accounts.seller_token_account.to_account_info(), + token_program: ctx.accounts.token_program.to_account_info(), + user: ctx.accounts.seller.to_account_info(), + }, + signer_seeds, + ))?; + + // Transfer (listing_amount - royalty) to seller + let (royalty_funds, _) = + calculate_royalties(&ctx.accounts.mint.to_account_info(), args.buy_amount)?; + + let funds_to_send = listing + .listing_amount + .checked_sub(royalty_funds) + .ok_or(WenWnsMarketplaceError::ArithmeticError)?; + + let buyer_token_account_info = if is_payment_mint_spl { + let payment_mint = &ctx.accounts.payment_mint.try_borrow_data()?; + let payment_mint_data = StateWithExtensions::::unpack(payment_mint)?; + + let buyer_payment_token_account = ctx + .accounts + .buyer_payment_token_account + .clone() + .ok_or(WenWnsMarketplaceError::InvalidPaymentTokenAccount)?; + + let seller_payment_token_account = ctx + .accounts + .seller_payment_token_account + .clone() + .ok_or(WenWnsMarketplaceError::InvalidPaymentTokenAccount)?; + + transfer_checked( + CpiContext::new( + ctx.accounts + .payment_token_program + .clone() + .unwrap() + .to_account_info(), + TransferChecked { + authority: ctx.accounts.buyer.to_account_info(), + from: buyer_payment_token_account.to_account_info(), + to: seller_payment_token_account.to_account_info(), + mint: ctx.accounts.payment_mint.to_account_info(), + }, + ), + funds_to_send, + payment_mint_data.base.decimals, + )?; + + Some(buyer_payment_token_account.to_account_info()) + } else { + transfer( + CpiContext::new( + ctx.accounts.system_program.to_account_info(), + Transfer { + from: ctx.accounts.buyer.to_account_info(), + to: ctx.accounts.seller.to_account_info(), + }, + ), + funds_to_send, + )?; + None + }; + + // Royalties interface instructions + let payment_token_program = ctx + .accounts + .payment_token_program + .as_ref() + .map(|d| d.to_account_info()); + + let royalty_ixs = generate_royalty_ixs( + args.buy_amount, + &ctx.accounts.mint.to_account_info(), + &ctx.accounts.payment_mint.to_account_info(), + ctx.accounts.buyer.key, + &payment_token_program, + is_payment_mint_spl, + )?; + + for (ix_index, royalty_ix) in royalty_ixs.iter().enumerate() { + let remaining_accounts = ctx.remaining_accounts; + if royalty_ix.program_id == ctx.accounts.system_program.key() { + let creator = remaining_accounts.get(ix_index); + if creator.is_none() { + return err!(WenWnsMarketplaceError::CreatorMissing); + } + let creator = creator.unwrap(); + + invoke( + royalty_ix, + &[ + ctx.accounts.buyer.to_account_info(), + creator.clone(), + ctx.accounts.system_program.to_account_info(), + ], + )?; + continue; + } else { + if royalty_ix.program_id == ctx.accounts.associated_token_program.key() { + let creator = remaining_accounts.get(ix_index + 1); + if creator.is_none() { + return err!(WenWnsMarketplaceError::CreatorMissing); + } + let creator = creator.unwrap(); + + let creator_token_account = remaining_accounts.get(ix_index); + if creator_token_account.is_none() { + return err!(WenWnsMarketplaceError::CreatorTokenAccountMissing); + } + let creator_token_account = creator_token_account.unwrap(); + + invoke( + royalty_ix, + &[ + ctx.accounts.buyer.to_account_info(), + creator_token_account.clone(), + creator.clone(), + ctx.accounts.payment_mint.to_account_info(), + ctx.accounts.system_program.to_account_info(), + ctx.accounts + .payment_token_program + .clone() + .unwrap() + .to_account_info(), + ], + )?; + } else { + let creator_token_account = remaining_accounts.get(ix_index - 1); + if creator_token_account.is_none() { + return err!(WenWnsMarketplaceError::CreatorTokenAccountMissing); + } + let creator_token_account = creator_token_account.unwrap(); + + invoke( + royalty_ix, + &[ + buyer_token_account_info.clone().unwrap(), + ctx.accounts.payment_mint.to_account_info(), + creator_token_account.clone(), + ctx.accounts.buyer.to_account_info(), + ctx.accounts + .payment_token_program + .clone() + .unwrap() + .to_account_info(), + ], + )?; + } + } + } + + // Transfer NFT to buyer + transfer_checked_with_transfer_guard( + CpiContext::new_with_signer( + ctx.accounts.token_program.to_account_info(), + TransferCheckedWithTransferGuard { + authority: listing.to_account_info(), + mint: ctx.accounts.mint.to_account_info(), + from: ctx.accounts.seller_token_account.to_account_info(), + to: ctx.accounts.buyer_token_account.to_account_info(), + extra_metas_account: ctx.accounts.extra_metas_account.to_account_info(), + guard_account: ctx.accounts.guard_account.to_account_info(), + sysvar_instructions: ctx.accounts.sysvar_instructions.to_account_info(), + wen_transfer_guard_program: ctx + .accounts + .wen_transfer_guard_program + .to_account_info(), + }, + signer_seeds, + ), + 1, + 0, + )?; + + // Close listing + listing.close(ctx.accounts.payer.to_account_info())?; + + Ok(()) +} + +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct FulfillListingTransferGuardArgs { + pub buy_amount: u64, +} diff --git a/programs/wen_wns_marketplace/src/instructions/listing/mod.rs b/programs/wen_wns_marketplace/src/instructions/listing/mod.rs index 46ffac4..2fb9847 100644 --- a/programs/wen_wns_marketplace/src/instructions/listing/mod.rs +++ b/programs/wen_wns_marketplace/src/instructions/listing/mod.rs @@ -1,9 +1,11 @@ pub mod buy; +pub mod buy_transfer_guard; pub mod list; pub mod royalty; pub mod unlist; pub use buy::*; +pub use buy_transfer_guard::*; pub use list::*; pub use royalty::*; pub use unlist::*; diff --git a/programs/wen_wns_marketplace/src/lib.rs b/programs/wen_wns_marketplace/src/lib.rs index 281fd9c..2bd7094 100644 --- a/programs/wen_wns_marketplace/src/lib.rs +++ b/programs/wen_wns_marketplace/src/lib.rs @@ -28,6 +28,13 @@ pub mod wen_wns_marketplace { pub fn buy(ctx: Context, args: FulfillListingArgs) -> Result<()> { listing::buy::handler(ctx, args) } + + pub fn buy_transfer_guard<'a, 'b, 'c, 'info>( + ctx: Context<'a, 'b, 'c, 'info, FulfillListingTransferGuard<'info>>, + args: FulfillListingTransferGuardArgs, + ) -> Result<()> { + listing::buy_transfer_guard::handler(ctx, args) + } /* endregion */ /* region CLAIM ROYALTY */ diff --git a/programs/wen_wns_marketplace/src/utils.rs b/programs/wen_wns_marketplace/src/utils.rs index 8bf3800..8c644ea 100644 --- a/programs/wen_wns_marketplace/src/utils.rs +++ b/programs/wen_wns_marketplace/src/utils.rs @@ -5,35 +5,9 @@ use anchor_spl::{ create as create_associated_token, get_associated_token_address_with_program_id, Create as CreateAssociatedToken, }, - token_2022::{ - self, - spl_token_2022::{ - extension::{BaseStateWithExtensions, StateWithExtensions}, - instruction::transfer_checked, - state::Mint as StateMint, - }, - }, - token_interface::spl_token_metadata_interface::state::TokenMetadata, + token_2022::{self, spl_token_2022::instruction::transfer_checked}, }; use spl_transfer_hook_interface::onchain::add_extra_accounts_for_execute_cpi; -use std::str::FromStr; -use wen_new_standard::ROYALTY_BASIS_POINTS_FIELD; - -pub fn calculate_royalties(mint: &AccountInfo, amount: u64) -> Result { - let mint_account_data = mint.try_borrow_data()?; - let mint_data = StateWithExtensions::::unpack(&mint_account_data)?; - let metadata = mint_data.get_variable_len_extension::()?; - - // get royalty basis points from metadata Vec(String, String) - let royalty_basis_points = metadata - .additional_metadata - .iter() - .find(|(key, _)| key == ROYALTY_BASIS_POINTS_FIELD) - .map(|(_, value)| u64::from_str(value).unwrap()) - .unwrap_or(0); - - Ok((amount * royalty_basis_points) / 10000) -} pub fn assert_right_associated_token_account( owner: &Pubkey, @@ -140,3 +114,69 @@ pub fn transfer_checked_with_hook<'info>( anchor_lang::solana_program::program::invoke_signed(&ix, &account_infos, ctx.signer_seeds) .map_err(Into::into) } + +#[derive(Accounts)] +pub struct TransferCheckedWithTransferGuard<'info> { + /// CHECK: CPI Accounts + pub from: AccountInfo<'info>, + /// CHECK: CPI Accounts + pub mint: AccountInfo<'info>, + /// CHECK: CPI Accounts + pub to: AccountInfo<'info>, + /// CHECK: CPI Accounts + pub authority: AccountInfo<'info>, + /// CHECK: CPI Accounts + pub wen_transfer_guard_program: AccountInfo<'info>, + /// CHECK: CPI Accounts + pub extra_metas_account: AccountInfo<'info>, + /// CHECK: CPI Accounts + pub guard_account: AccountInfo<'info>, + /// CHECK: CPI Accounts + pub sysvar_instructions: AccountInfo<'info>, +} + +pub fn transfer_checked_with_transfer_guard<'info>( + ctx: CpiContext<'_, '_, '_, 'info, TransferCheckedWithTransferGuard<'info>>, + amount: u64, + decimals: u8, +) -> Result<()> { + let mut ix = transfer_checked( + ctx.program.key, + ctx.accounts.from.key, + ctx.accounts.mint.key, + ctx.accounts.to.key, + ctx.accounts.authority.key, + &[], + amount, + decimals, + )?; + + let mut account_infos = vec![ + ctx.accounts.from.clone(), + ctx.accounts.mint.clone(), + ctx.accounts.to.clone(), + ctx.accounts.authority.clone(), + ]; + + let additional_account_infos = vec![ + ctx.accounts.guard_account.to_account_info(), + ctx.accounts.sysvar_instructions.to_account_info(), + ctx.accounts.wen_transfer_guard_program.to_account_info(), + ctx.accounts.extra_metas_account.to_account_info(), + ]; + + add_extra_accounts_for_execute_cpi( + &mut ix, + &mut account_infos, + &ctx.accounts.wen_transfer_guard_program.key(), + ctx.accounts.from, + ctx.accounts.mint, + ctx.accounts.to, + ctx.accounts.authority, + amount, + &additional_account_infos, + )?; + + anchor_lang::solana_program::program::invoke_signed(&ix, &account_infos, ctx.signer_seeds) + .map_err(Into::into) +} diff --git a/tests/utils.ts b/tests/utils.ts index fdb078c..e09210b 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -36,13 +36,25 @@ export const MEMBER_ACCOUNT_SEED = Buffer.from("member"); export const MARKETPLACE = Buffer.from("marketplace"); export const SALE = Buffer.from("sale"); export const LISTING = Buffer.from("listing"); +export const APPROVE_ACCOUNT_SEED = Buffer.from("approve-account"); +export const EXTRA_METAS_ACCOUNT = Buffer.from("extra-account-metas"); +export const WEN_TOKEN_GUARD = Buffer.from("wen_token_transfer_guard"); +export const GUARD_V1 = Buffer.from("guard_v1"); + +export const getGuardAccountPda = (mint: PublicKey, programId: PublicKey) => { + const [guardAccount] = PublicKey.findProgramAddressSync( + [WEN_TOKEN_GUARD, GUARD_V1, mint.toBuffer()], + programId + ); + return guardAccount; +}; export const getExtraMetasAccountPda = ( mint: PublicKey, programId: PublicKey ) => { const [extraMetasAccount] = PublicKey.findProgramAddressSync( - [Buffer.from("extra-account-metas"), mint.toBuffer()], + [EXTRA_METAS_ACCOUNT, mint.toBuffer()], programId ); return extraMetasAccount; @@ -50,7 +62,7 @@ export const getExtraMetasAccountPda = ( export const getApproveAccountPda = (mint: PublicKey, programId: PublicKey) => { const [approveAccount] = PublicKey.findProgramAddressSync( - [Buffer.from("approve-account"), mint.toBuffer()], + [APPROVE_ACCOUNT_SEED, mint.toBuffer()], programId ); @@ -59,7 +71,7 @@ export const getApproveAccountPda = (mint: PublicKey, programId: PublicKey) => { export const getManagerAccountPda = (programId: PublicKey) => { const [managerAccount] = PublicKey.findProgramAddressSync( - [Buffer.from("manager")], + [MANAGER_SEED], programId ); return managerAccount; @@ -67,7 +79,7 @@ export const getManagerAccountPda = (programId: PublicKey) => { export const getGroupAccountPda = (mint: PublicKey, programId: PublicKey) => { const [groupAccount] = PublicKey.findProgramAddressSync( - [Buffer.from("group"), mint.toBuffer()], + [GROUP_ACCOUNT_SEED, mint.toBuffer()], programId ); return groupAccount; @@ -75,7 +87,7 @@ export const getGroupAccountPda = (mint: PublicKey, programId: PublicKey) => { export const getMemberAccountPda = (mint: PublicKey, programId: PublicKey) => { const [memberAccount] = PublicKey.findProgramAddressSync( - [Buffer.from("member"), mint.toBuffer()], + [MEMBER_ACCOUNT_SEED, mint.toBuffer()], programId ); return memberAccount; diff --git a/tests/wen_royalty_distribution.test.ts b/tests/wen_royalty_distribution.test.ts index 3d05952..297ef3a 100644 --- a/tests/wen_royalty_distribution.test.ts +++ b/tests/wen_royalty_distribution.test.ts @@ -3,6 +3,7 @@ import { faker } from "@faker-js/faker"; import { WenNewStandard } from "../target/types/wen_new_standard"; import { WenWnsMarketplace } from "./../target/types/wen_wns_marketplace"; import { WenRoyaltyDistribution } from "./../target/types/wen_royalty_distribution"; +import { WenTransferGuard } from "../target/types/wen_transfer_guard"; import { Keypair, LAMPORTS_PER_SOL, @@ -11,6 +12,7 @@ import { AccountInfo, ComputeBudgetProgram, Commitment, + SYSVAR_INSTRUCTIONS_PUBKEY, } from "@solana/web3.js"; import { @@ -20,6 +22,7 @@ import { getDistributionAccountPda, getExtraMetasAccountPda, getGroupAccountPda, + getGuardAccountPda, getListingAccountPda, getManagerAccountPda, getMemberAccountPda, @@ -48,10 +51,13 @@ describe("wen_royalty_distribution", () => { .WenRoyaltyDistribution as anchor.Program; const wenWnsMarketplace = anchor.workspace .WenWnsMarketplace as anchor.Program; + const wenTransferGuard = anchor.workspace + .WenTransferGuard as anchor.Program; const wnsProgramId = wnsProgram.programId; const wenDistributionProgramId = wenDistributionProgram.programId; const wenWnsMarketplaceId = wenWnsMarketplace.programId; + const wenTransferGuardId = wenTransferGuard.programId; const manager = getManagerAccountPda(wnsProgramId); const preflightConfig: { @@ -77,7 +83,946 @@ describe("wen_royalty_distribution", () => { } }); - describe("a sale", () => { + describe("a sale via Transfer Guard", () => { + const seller = Keypair.generate(); + const buyer = Keypair.generate(); + + const creator1 = Keypair.generate(); + const creator2 = Keypair.generate(); + const guardAuthority = Keypair.generate(); + + before(async () => { + await airdrop(connection, seller.publicKey, 10 * LAMPORTS_PER_SOL); + await airdrop(connection, buyer.publicKey, 10 * LAMPORTS_PER_SOL); + await airdrop(connection, creator1.publicKey, 1 * LAMPORTS_PER_SOL); + await airdrop(connection, creator2.publicKey, 1 * LAMPORTS_PER_SOL); + await airdrop(connection, guardAuthority.publicKey, 1 * LAMPORTS_PER_SOL); + }); + + describe("using SOL as payment", () => { + const name = faker.lorem.words({ max: 3, min: 2 }); + const symbol = faker.lorem.word(); + const uri = faker.internet.url(); + + const transferGuardMintKeypair = Keypair.generate(); + const groupMintKeypair = Keypair.generate(); + const memberMintKeypair = Keypair.generate(); + const groupMintPublicKey = groupMintKeypair.publicKey; + const memberMintPublickey = memberMintKeypair.publicKey; + const transferGuardMintPublicKey = transferGuardMintKeypair.publicKey; + + const groupMintAuthPublicKey = wallet.publicKey; + const memberMintAuthPublicKey = seller.publicKey; + const guardMintAuthPublicKey = guardAuthority.publicKey; + + const guardMintTokenAccount = getAssociatedTokenAddressSync( + transferGuardMintPublicKey, + guardMintAuthPublicKey, + false, + TOKEN_2022_PROGRAM_ID + ); + + const groupMintTokenAccount = getAssociatedTokenAddressSync( + groupMintPublicKey, + groupMintAuthPublicKey, + false, + TOKEN_2022_PROGRAM_ID + ); + + const sellerMemberMintTokenAccount = getAssociatedTokenAddressSync( + memberMintPublickey, + memberMintAuthPublicKey, + false, + TOKEN_2022_PROGRAM_ID + ); + + const buyerMemberMintTokenAccount = getAssociatedTokenAddressSync( + memberMintPublickey, + buyer.publicKey, + false, + TOKEN_2022_PROGRAM_ID + ); + + const group = getGroupAccountPda(groupMintPublicKey, wnsProgramId); + const guard = getGuardAccountPda( + transferGuardMintPublicKey, + wenTransferGuardId + ); + + const member = getMemberAccountPda(memberMintPublickey, wnsProgramId); + const extraMetasAccount = getExtraMetasAccountPda( + memberMintPublickey, + wenTransferGuardId + ); + + const listingAmount = new anchor.BN(2 * LAMPORTS_PER_SOL); + const royaltyBasisPoints = 1000; + const creator1SharePct = 60; + const creator2SharePct = 40; + + before(async () => { + // CREATE GROUP ACCOUNT + await wnsProgram.methods + .createGroupAccount({ + maxSize: 1, + name, + symbol, + uri, + }) + .accountsStrict({ + authority: groupMintAuthPublicKey, + group, + manager, + mint: groupMintPublicKey, + mintTokenAccount: groupMintTokenAccount, + payer: groupMintAuthPublicKey, + receiver: groupMintAuthPublicKey, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + tokenProgram: TOKEN_2022_PROGRAM_ID, + systemProgram: SystemProgram.programId, + }) + .signers([groupMintKeypair]) + .rpc(preflightConfig); + + // CREATE GUARD ACCOUNT + await wenTransferGuard.methods + .createGuard({ + name: "Guard SOL", + symbol: "GSOL", + uri: "https://guard.sol", + cpiRule: { deny: { 0: [] } }, + additionalFieldsRule: [], + transferAmountRule: null, + }) + .accountsStrict({ + guard, + guardAuthority: guardMintAuthPublicKey, + mint: transferGuardMintPublicKey, + mintTokenAccount: guardMintTokenAccount, + payer: guardMintAuthPublicKey, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + tokenProgram: TOKEN_2022_PROGRAM_ID, + systemProgram: SystemProgram.programId, + }) + .signers([guardAuthority, transferGuardMintKeypair]) + .rpc(preflightConfig); + }); + + before(async () => { + const name = faker.lorem.words({ max: 3, min: 2 }); + const symbol = faker.lorem.word(); + const uri = faker.internet.url(); + + // CREATE MINT ACCOUNT, ADD MINT TO GROUP, ADD ROYALTIES + const ixs = await Promise.all([ + wnsProgram.methods + .addMintToGroup() + .accountsStrict({ + authority: groupMintAuthPublicKey, + mint: memberMintPublickey, + payer: groupMintAuthPublicKey, + group, + manager, + member, + systemProgram: SystemProgram.programId, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }) + .instruction(), + wnsProgram.methods + .addRoyalties({ + creators: [ + { + address: creator1.publicKey, + share: creator1SharePct, + }, + { address: creator2.publicKey, share: creator2SharePct }, + ], + royaltyBasisPoints, + }) + .accountsStrict({ + extraMetasAccount, + authority: memberMintAuthPublicKey, + mint: memberMintPublickey, + payer: memberMintAuthPublicKey, + systemProgram: SystemProgram.programId, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }) + .remainingAccounts([ + { + isSigner: false, + isWritable: false, + pubkey: wenTransferGuardId, + }, + { isSigner: false, isWritable: false, pubkey: guard }, + ]) + .instruction(), + ]); + + await wnsProgram.methods + .createMintAccount({ name, symbol, permanentDelegate: null, uri }) + .accountsStrict({ + payer: groupMintAuthPublicKey, + manager, + mintTokenAccount: sellerMemberMintTokenAccount, + authority: memberMintAuthPublicKey, + mint: memberMintPublickey, + receiver: memberMintAuthPublicKey, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + tokenProgram: TOKEN_2022_PROGRAM_ID, + systemProgram: SystemProgram.programId, + }) + .preInstructions([ + ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }), + ]) + .postInstructions(ixs) + .signers([memberMintKeypair, seller]) + .rpc(preflightConfig); + }); + + describe("after listing for sale", () => { + const listing = getListingAccountPda( + seller.publicKey, + memberMintPublickey, + wenWnsMarketplaceId + ); + + let listingAccountInfo: AccountInfo; + let sellerTokenAccountData: Account; + let listingAccountData; + + before(async () => { + await wenWnsMarketplace.methods + .list({ + listingAmount, + paymentMint: PublicKey.default, + }) + .accountsStrict({ + listing, + manager, + payer: wallet.publicKey, + seller: seller.publicKey, + mint: memberMintPublickey, + sellerTokenAccount: sellerMemberMintTokenAccount, + systemProgram: SystemProgram.programId, + wnsProgram: wnsProgramId, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }) + .signers([seller]) + .rpc({ + skipPreflight: true, + preflightCommitment: "confirmed", + commitment: "confirmed", + }); + + listingAccountInfo = await connection.getAccountInfo( + listing, + "confirmed" + ); + listingAccountData = wenWnsMarketplace.coder.accounts.decode( + "listing", + listingAccountInfo.data + ); + sellerTokenAccountData = await getAccount( + connection, + sellerMemberMintTokenAccount, + "confirmed", + TOKEN_2022_PROGRAM_ID + ); + }); + + it("should have a listing account", () => { + expect(listingAccountData).to.not.be.undefined; + }); + + it("should be owned by sale program", () => { + expect(listingAccountInfo.owner.toBase58()).to.eql( + wenWnsMarketplaceId.toBase58() + ); + }); + + it("should point the listing account as delegate of NFT", () => { + expect(sellerTokenAccountData.delegate.toBase58()).to.eql( + listing.toBase58() + ); + }); + + it("should freeze the NFT", () => { + expect(sellerTokenAccountData.isFrozen).to.be.true; + }); + }); + + describe("after a sale", () => { + const listing = getListingAccountPda( + seller.publicKey, + memberMintPublickey, + wenWnsMarketplaceId + ); + + const royalty = listingAmount + .mul(new anchor.BN(royaltyBasisPoints)) + .div(new anchor.BN(10_000)); + + let creator1PreBalance: number; + let creator2PreBalance: number; + let sellerPreBalance: number; + let buyerPreBalance: number; + + let creator1PostBalance: number; + let creator2PostBalance: number; + let sellerPostBalance: number; + let buyerPostBalance: number; + + let sellerTokenAccountData: Account; + let buyerTokenAccountData: Account; + + before(async () => { + buyerPreBalance = await connection.getBalance( + buyer.publicKey, + "confirmed" + ); + sellerPreBalance = await connection.getBalance( + seller.publicKey, + "confirmed" + ); + creator1PreBalance = await connection.getBalance( + creator1.publicKey, + "confirmed" + ); + creator2PreBalance = await connection.getBalance( + creator2.publicKey, + "confirmed" + ); + + await wenWnsMarketplace.methods + .buyTransferGuard({ buyAmount: listingAmount }) + .accountsStrict({ + guardAccount: guard, + extraMetasAccount, + manager, + listing, + payer: wallet.publicKey, + buyer: buyer.publicKey, + seller: seller.publicKey, + buyerPaymentTokenAccount: null, + sellerPaymentTokenAccount: null, + mint: memberMintPublickey, + paymentMint: PublicKey.default, + buyerTokenAccount: buyerMemberMintTokenAccount, + sellerTokenAccount: sellerMemberMintTokenAccount, + wnsProgram: wnsProgramId, + wenTransferGuardProgram: wenTransferGuardId, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + tokenProgram: TOKEN_2022_PROGRAM_ID, + paymentTokenProgram: null, + systemProgram: SystemProgram.programId, + sysvarInstructions: SYSVAR_INSTRUCTIONS_PUBKEY, + }) + .remainingAccounts( + [creator1.publicKey, creator2.publicKey].map((c) => ({ + isSigner: false, + isWritable: true, + pubkey: c, + })) + ) + .preInstructions([ + ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }), + ]) + .signers([buyer]) + .rpc({ + skipPreflight: true, + preflightCommitment: "confirmed", + commitment: "confirmed", + }); + + buyerPostBalance = await connection.getBalance( + buyer.publicKey, + "confirmed" + ); + sellerPostBalance = + (await connection.getBalance(seller.publicKey, "confirmed")) - + sellerPreBalance; + creator1PostBalance = await connection.getBalance( + creator1.publicKey, + "confirmed" + ); + creator2PostBalance = await connection.getBalance( + creator2.publicKey, + "confirmed" + ); + + sellerTokenAccountData = await getAccount( + connection, + sellerMemberMintTokenAccount, + "confirmed", + TOKEN_2022_PROGRAM_ID + ); + buyerTokenAccountData = await getAccount( + connection, + buyerMemberMintTokenAccount, + "confirmed", + TOKEN_2022_PROGRAM_ID + ); + }); + + describe("royalties", () => { + const expectedCreator1Share = royalty + .mul(new anchor.BN(creator1SharePct)) + .div(new anchor.BN(100)); + + const expectedCreator2Share = royalty + .mul(new anchor.BN(creator2SharePct)) + .div(new anchor.BN(100)); + + it("should receive correct royalty funds", () => { + expect(creator1PostBalance).to.eql( + creator1PreBalance + expectedCreator1Share.toNumber() + ); + expect(creator2PostBalance).to.eql( + creator2PreBalance + expectedCreator2Share.toNumber() + ); + }); + }); + + describe("the seller", () => { + it("receive the payment minus royalties", () => { + expect(sellerPostBalance).to.eql( + listingAmount.sub(royalty).toNumber() + ); + }); + it("should not be the owner anymore", () => { + expect(sellerTokenAccountData.amount.toString()).to.eql("0"); + }); + }); + + describe("the buyer", () => { + it("sent the payment", () => { + expect(buyerPostBalance).to.eql( + buyerPreBalance - listingAmount.toNumber() + ); + }); + it("should be the owner", () => { + expect(buyerTokenAccountData.amount.toString()).to.eql("1"); + }); + }); + }); + }); + + describe("using SPL token as payment", () => { + const name = faker.lorem.words({ max: 3, min: 2 }); + const symbol = faker.lorem.word(); + const uri = faker.internet.url(); + + const transferGuardMintKeypair = Keypair.generate(); + const transferGuardMintPublicKey = transferGuardMintKeypair.publicKey; + const guardMintAuthPublicKey = guardAuthority.publicKey; + + const guardMintTokenAccount = getAssociatedTokenAddressSync( + transferGuardMintPublicKey, + guardMintAuthPublicKey, + false, + TOKEN_2022_PROGRAM_ID + ); + const guard = getGuardAccountPda( + transferGuardMintPublicKey, + wenTransferGuardId + ); + + const groupMintKeypair = Keypair.generate(); + const memberMintKeypair = Keypair.generate(); + const paymentMintKeypair = Keypair.generate(); + + const groupMintPublicKey = groupMintKeypair.publicKey; + const memberMintPublickey = memberMintKeypair.publicKey; + const paymentMintPublickey = paymentMintKeypair.publicKey; + + const groupMintAuthPublicKey = wallet.publicKey; + const memberMintAuthPublicKey = seller.publicKey; + const paymentMintAuthPublicKey = wallet.publicKey; + + const groupMintTokenAccount = getAssociatedTokenAddressSync( + groupMintPublicKey, + groupMintAuthPublicKey, + false, + TOKEN_2022_PROGRAM_ID + ); + + const sellerMemberMintTokenAccount = getAssociatedTokenAddressSync( + memberMintPublickey, + memberMintAuthPublicKey, + false, + TOKEN_2022_PROGRAM_ID + ); + + const sellerPaymentMintTokenAccount = getAssociatedTokenAddressSync( + paymentMintPublickey, + seller.publicKey, + false, + TOKEN_PROGRAM_ID + ); + + const buyerMemberMintTokenAccount = getAssociatedTokenAddressSync( + memberMintPublickey, + buyer.publicKey, + false, + TOKEN_2022_PROGRAM_ID + ); + + const buyerPaymentMintTokenAccount = getAssociatedTokenAddressSync( + paymentMintPublickey, + buyer.publicKey, + false, + TOKEN_PROGRAM_ID + ); + + const group = getGroupAccountPda(groupMintPublicKey, wnsProgramId); + const listing = getListingAccountPda( + seller.publicKey, + memberMintPublickey, + wenWnsMarketplaceId + ); + const member = getMemberAccountPda(memberMintPublickey, wnsProgramId); + const extraMetasAccount = getExtraMetasAccountPda( + memberMintPublickey, + wenTransferGuardId + ); + + const listingAmount = new anchor.BN(500 * 10 ** 6); + const royaltyBasisPoints = 1000; + const royalty = listingAmount + .mul(new anchor.BN(royaltyBasisPoints)) + .div(new anchor.BN(10_000)); + const creator1SharePct = 60; + const creator2SharePct = 40; + + before(async () => { + const { ixs: createMintIxs } = await createMintTokenKegIx( + connection, + paymentMintPublickey, + paymentMintAuthPublicKey, + paymentMintAuthPublicKey + ); + + await sendAndConfirmWNSTransaction( + connection, + createMintIxs, + provider, + true, + [paymentMintKeypair] + ); + + const { ixs: mintToIxs } = mintToBuyerSellerIx( + paymentMintPublickey, + paymentMintAuthPublicKey, + paymentMintAuthPublicKey, + buyer.publicKey, + buyerPaymentMintTokenAccount, + seller.publicKey, + sellerPaymentMintTokenAccount + ); + + await sendAndConfirmWNSTransaction( + connection, + mintToIxs, + provider, + true, + [] + ); + }); + + before(async () => { + // CREATE GROUP ACCOUNT + await wnsProgram.methods + .createGroupAccount({ + maxSize: 1, + name, + symbol, + uri, + }) + .accountsStrict({ + authority: groupMintAuthPublicKey, + group, + manager, + mint: groupMintPublicKey, + mintTokenAccount: groupMintTokenAccount, + payer: groupMintAuthPublicKey, + receiver: groupMintAuthPublicKey, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + tokenProgram: TOKEN_2022_PROGRAM_ID, + systemProgram: SystemProgram.programId, + }) + .signers([groupMintKeypair]) + .rpc(preflightConfig); + + // CREATE GUARD ACCOUNT + await wenTransferGuard.methods + .createGuard({ + name: "Guard SPL", + symbol: "GSPL", + uri: "https://guard.spl", + cpiRule: { deny: { 0: [] } }, + additionalFieldsRule: [], + transferAmountRule: null, + }) + .accountsStrict({ + guard, + guardAuthority: guardMintAuthPublicKey, + mint: transferGuardMintPublicKey, + mintTokenAccount: guardMintTokenAccount, + payer: guardMintAuthPublicKey, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + tokenProgram: TOKEN_2022_PROGRAM_ID, + systemProgram: SystemProgram.programId, + }) + .signers([guardAuthority, transferGuardMintKeypair]) + .rpc(preflightConfig); + }); + + before(async () => { + const name = faker.lorem.words({ max: 3, min: 2 }); + const symbol = faker.lorem.word(); + const uri = faker.internet.url(); + + // CREATE MINT ACCOUNT, ADD MINT TO GROUP, ADD ROYALTIES + const ixs = await Promise.all([ + wnsProgram.methods + .addMintToGroup() + .accountsStrict({ + authority: groupMintAuthPublicKey, + mint: memberMintPublickey, + payer: groupMintAuthPublicKey, + group, + manager, + member, + systemProgram: SystemProgram.programId, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }) + .instruction(), + wnsProgram.methods + .addRoyalties({ + creators: [ + { + address: creator1.publicKey, + share: creator1SharePct, + }, + { address: creator2.publicKey, share: creator2SharePct }, + ], + royaltyBasisPoints, + }) + .accountsStrict({ + extraMetasAccount, + authority: memberMintAuthPublicKey, + mint: memberMintPublickey, + payer: memberMintAuthPublicKey, + systemProgram: SystemProgram.programId, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }) + .remainingAccounts([ + { + isSigner: false, + isWritable: false, + pubkey: wenTransferGuardId, + }, + { isSigner: false, isWritable: false, pubkey: guard }, + ]) + .instruction(), + ]); + + await wnsProgram.methods + .createMintAccount({ name, symbol, permanentDelegate: null, uri }) + .accountsStrict({ + payer: groupMintAuthPublicKey, + manager, + mintTokenAccount: sellerMemberMintTokenAccount, + authority: memberMintAuthPublicKey, + mint: memberMintPublickey, + receiver: memberMintAuthPublicKey, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + tokenProgram: TOKEN_2022_PROGRAM_ID, + systemProgram: SystemProgram.programId, + }) + .preInstructions([ + ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }), + ]) + .postInstructions(ixs) + .signers([memberMintKeypair, seller]) + .rpc(preflightConfig); + }); + + describe("after listing for sale", () => { + let listingAccountInfo: AccountInfo; + let sellerTokenAccountData: Account; + let listingAccountData; + + before(async () => { + await wenWnsMarketplace.methods + .list({ + listingAmount, + paymentMint: paymentMintPublickey, + }) + .accountsStrict({ + listing, + manager, + payer: wallet.publicKey, + seller: seller.publicKey, + mint: memberMintPublickey, + sellerTokenAccount: sellerMemberMintTokenAccount, + systemProgram: SystemProgram.programId, + wnsProgram: wnsProgramId, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }) + .signers([seller]) + .rpc({ + skipPreflight: true, + preflightCommitment: "confirmed", + commitment: "confirmed", + }); + + listingAccountInfo = await connection.getAccountInfo( + listing, + "confirmed" + ); + listingAccountData = wenWnsMarketplace.coder.accounts.decode( + "listing", + listingAccountInfo.data + ); + sellerTokenAccountData = await getAccount( + connection, + sellerMemberMintTokenAccount, + "confirmed", + TOKEN_2022_PROGRAM_ID + ); + }); + + it("should have a listing account", () => { + expect(listingAccountData).to.not.be.undefined; + }); + + it("should be owned by sale program", () => { + expect(listingAccountInfo.owner).to.eql(wenWnsMarketplaceId); + }); + + it("should point the listing account as delegate of NFT", () => { + expect(sellerTokenAccountData.delegate.toBase58()).to.eql( + listing.toBase58() + ); + }); + + it("should freeze the NFT", () => { + expect(sellerTokenAccountData.isFrozen).to.be.true; + }); + }); + + describe("after a sale", () => { + const creator1PaymentMintTokenAccount = getAssociatedTokenAddressSync( + paymentMintPublickey, + creator1.publicKey, + false, + TOKEN_PROGRAM_ID + ); + + const creator2PaymentMintTokenAccount = getAssociatedTokenAddressSync( + paymentMintPublickey, + creator2.publicKey, + false, + TOKEN_PROGRAM_ID + ); + + const royalty = listingAmount + .mul(new anchor.BN(royaltyBasisPoints)) + .div(new anchor.BN(10_000)); + + let sellerPreBalance: number; + let buyerPreBalance: number; + + let creator1PostBalance: number; + let creator2PostBalance: number; + let sellerPostBalance: number; + let buyerPostBalance: number; + + let sellerTokenAccountData: Account; + let buyerTokenAccountData: Account; + + before(async () => { + buyerPreBalance = parseInt( + ( + await getAccount( + connection, + buyerPaymentMintTokenAccount, + "confirmed", + TOKEN_PROGRAM_ID + ) + ).amount.toString() + ); + sellerPreBalance = parseInt( + ( + await getAccount( + connection, + sellerPaymentMintTokenAccount, + "confirmed", + TOKEN_PROGRAM_ID + ) + ).amount.toString() + ); + + const ix = await wenWnsMarketplace.methods + .buyTransferGuard({ + buyAmount: listingAmount, + }) + .accountsStrict({ + guardAccount: guard, + extraMetasAccount, + manager, + listing, + payer: wallet.publicKey, + buyer: buyer.publicKey, + seller: seller.publicKey, + buyerPaymentTokenAccount: buyerPaymentMintTokenAccount, + sellerPaymentTokenAccount: sellerPaymentMintTokenAccount, + mint: memberMintPublickey, + paymentMint: paymentMintPublickey, + buyerTokenAccount: buyerMemberMintTokenAccount, + sellerTokenAccount: sellerMemberMintTokenAccount, + wnsProgram: wnsProgramId, + wenTransferGuardProgram: wenTransferGuardId, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + tokenProgram: TOKEN_2022_PROGRAM_ID, + paymentTokenProgram: TOKEN_PROGRAM_ID, + systemProgram: SystemProgram.programId, + sysvarInstructions: SYSVAR_INSTRUCTIONS_PUBKEY, + }) + .remainingAccounts([ + { + isSigner: false, + isWritable: true, + pubkey: creator1PaymentMintTokenAccount, + }, + { + isSigner: false, + isWritable: false, + pubkey: creator1.publicKey, + }, + { + isSigner: false, + isWritable: true, + pubkey: creator2PaymentMintTokenAccount, + }, + { + isSigner: false, + isWritable: false, + pubkey: creator2.publicKey, + }, + ]) + .preInstructions([ + ComputeBudgetProgram.setComputeUnitLimit({ units: 600_000 }), + ]) + .signers([buyer]) + .rpc({ + skipPreflight: true, + preflightCommitment: "confirmed", + commitment: "confirmed", + }); + + buyerPostBalance = parseInt( + ( + await getAccount( + connection, + buyerPaymentMintTokenAccount, + "confirmed", + TOKEN_PROGRAM_ID + ) + ).amount.toString() + ); + sellerPostBalance = + parseInt( + ( + await getAccount( + connection, + sellerPaymentMintTokenAccount, + "confirmed", + TOKEN_PROGRAM_ID + ) + ).amount.toString() + ) - sellerPreBalance; + creator1PostBalance = parseInt( + ( + await getAccount( + connection, + creator1PaymentMintTokenAccount, + "confirmed", + TOKEN_PROGRAM_ID + ) + ).amount.toString() + ); + creator2PostBalance = parseInt( + ( + await getAccount( + connection, + creator2PaymentMintTokenAccount, + "confirmed", + TOKEN_PROGRAM_ID + ) + ).amount.toString() + ); + + sellerTokenAccountData = await getAccount( + connection, + sellerMemberMintTokenAccount, + "confirmed", + TOKEN_2022_PROGRAM_ID + ); + buyerTokenAccountData = await getAccount( + connection, + buyerMemberMintTokenAccount, + "confirmed", + TOKEN_2022_PROGRAM_ID + ); + }); + + describe("royalties", () => { + const expectedCreator1Share = royalty + .mul(new anchor.BN(creator1SharePct)) + .div(new anchor.BN(100)); + + const expectedCreator2Share = royalty + .mul(new anchor.BN(creator2SharePct)) + .div(new anchor.BN(100)); + + it("should receive correct royalty funds", () => { + expect(creator1PostBalance).to.eql( + expectedCreator1Share.toNumber() + ); + expect(creator2PostBalance).to.eql( + expectedCreator2Share.toNumber() + ); + }); + }); + + describe("the seller", () => { + it("receive the payment minus royalties", () => { + expect(sellerPostBalance).to.eql( + listingAmount.sub(royalty).toNumber() + ); + }); + it("should not be the owner anymore", () => { + expect(sellerTokenAccountData.amount.toString()).to.eql("0"); + }); + }); + + describe("the buyer", () => { + it("sent the payment", () => { + expect(buyerPostBalance).to.eql( + buyerPreBalance - listingAmount.toNumber() + ); + }); + it("should be the owner", () => { + expect(buyerTokenAccountData.amount.toString()).to.eql("1"); + }); + }); + }); + }); + }); + + describe("a sale via WNS", () => { const seller = Keypair.generate(); const buyer = Keypair.generate(); @@ -375,6 +1320,10 @@ describe("wen_royalty_distribution", () => { let sellerPostBalance: number; let buyerPostBalance: number; + let distributionBeforeRent: number; + let distributionCurrentRent: number; + let distributionRentDiff: number; + let sellerTokenAccountData: Account; let buyerTokenAccountData: Account; @@ -391,6 +1340,13 @@ describe("wen_royalty_distribution", () => { seller.publicKey, "confirmed" ); + distributionBeforeRent = + await connection.getMinimumBalanceForRentExemption( + ( + await connection.getAccountInfo(distribution, "confirmed") + ).data.length, + "confirmed" + ); await wenWnsMarketplace.methods .buy({ @@ -429,9 +1385,10 @@ describe("wen_royalty_distribution", () => { commitment: "confirmed", }); - distributionPostBalance = - (await connection.getBalance(distribution, "confirmed")) - - distributionPreBalance; + distributionPostBalance = await connection.getBalance( + distribution, + "confirmed" + ); buyerPostBalance = await connection.getBalance( buyer.publicKey, "confirmed" @@ -440,6 +1397,16 @@ describe("wen_royalty_distribution", () => { (await connection.getBalance(seller.publicKey, "confirmed")) - sellerPreBalance; + distributionCurrentRent = + await connection.getMinimumBalanceForRentExemption( + ( + await connection.getAccountInfo(distribution, "confirmed") + ).data.length, + "confirmed" + ); + distributionRentDiff = + distributionCurrentRent - distributionBeforeRent; + sellerTokenAccountData = await getAccount( connection, sellerMemberMintTokenAccount, @@ -456,7 +1423,9 @@ describe("wen_royalty_distribution", () => { describe("royalties", () => { it("should be sent to the distribution vault", () => { - expect(distributionPostBalance).to.eql(royalty.toNumber() + 1197120); // extra for rent + expect(distributionPostBalance).to.eql( + royalty.toNumber() + distributionCurrentRent + ); // extra for rent }); }); @@ -474,7 +1443,7 @@ describe("wen_royalty_distribution", () => { describe("the buyer", () => { it("sent the payment", () => { expect(buyerPostBalance).to.eql( - buyerPreBalance - listingAmount.toNumber() - 1197120 + buyerPreBalance - listingAmount.toNumber() - distributionRentDiff ); }); it("should be the owner", () => {