diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 339a6128..e8b3ea22 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -131,5 +131,10 @@ jobs: cargo-cache-fallback-key: cargo-programs solana: true + # Main program tests depend on the test-transfer-hook .so file, + # so it's required to build prior to testing + - name: Build Programs + run: pnpm programs:build + - name: Test Programs run: pnpm programs:test diff --git a/Cargo.lock b/Cargo.lock index 2f0f14e0..58f91aa9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -227,9 +227,9 @@ checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" [[package]] name = "async-compression" -version = "0.4.18" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" +checksum = "310c9bcae737a48ef5cdee3174184e6d548b292739ede61a1f955ef76a738861" dependencies = [ "brotli", "flate2", @@ -306,15 +306,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "blake3" -version = "1.5.5" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e" +checksum = "675f87afced0413c9bb02843499dbbd3882a237645883f71a2b59644a6d2f753" dependencies = [ "arrayref", "arrayvec", @@ -382,10 +382,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8b668d39970baad5356d7c83a86fee3a539e6f93bf6764c97368243e17a0487" dependencies = [ "once_cell", - "proc-macro-crate 3.2.0", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -473,7 +473,7 @@ checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -484,15 +484,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.11" +version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4730490333d58093109dc02c23174c3f4d490998c3fed3cc8e82d57afedb9cf" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ "shlex", ] @@ -682,7 +682,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -730,7 +730,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -764,9 +764,9 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "encoding_rs" @@ -794,7 +794,7 @@ checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -812,9 +812,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "feature-probe" @@ -845,9 +845,9 @@ checksum = "94474d15a76982be62ca8a39570dccce148d98c238ebb7408b0a21b2c4bdddc4" [[package]] name = "flate2" -version = "1.0.35" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" dependencies = [ "crc32fast", "miniz_oxide", @@ -1087,9 +1087,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -1256,7 +1256,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -1292,9 +1292,9 @@ dependencies = [ [[package]] name = "inout" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ "generic-array", ] @@ -1325,9 +1325,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" @@ -1356,9 +1356,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.169" +version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" [[package]] name = "libsecp256k1" @@ -1422,9 +1422,9 @@ dependencies = [ [[package]] name = "litemap" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "lock_api" @@ -1438,9 +1438,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.25" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "memchr" @@ -1477,9 +1477,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", ] @@ -1619,7 +1619,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -1678,10 +1678,10 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate 3.2.0", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -1695,9 +1695,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "opaque-debug" @@ -1707,11 +1707,11 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.70" +version = "0.10.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" +checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cfg-if", "foreign-types", "libc", @@ -1728,14 +1728,14 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] name = "openssl-sys" -version = "0.9.105" +version = "0.9.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" +checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" dependencies = [ "cc", "libc", @@ -1810,9 +1810,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "polyval" @@ -1846,18 +1846,18 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] @@ -1879,14 +1879,14 @@ checksum = "9e2e25ee72f5b24d773cae88422baddefff7714f97aab68d96fe2b6fc4a28fb2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] name = "quote" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" dependencies = [ "proc-macro2", ] @@ -1964,11 +1964,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -2045,15 +2045,14 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.8" +version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee" dependencies = [ "cc", "cfg-if", "getrandom 0.2.15", "libc", - "spin", "untrusted", "windows-sys 0.52.0", ] @@ -2106,15 +2105,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "ryu" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "scopeguard" @@ -2134,44 +2133,44 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" dependencies = [ "serde_derive", ] [[package]] name = "serde_bytes" -version = "0.11.15" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +checksum = "364fec0df39c49a083c9a8a18a23a6bcfd9af130fe9fe321d18520a0d113e09e" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] name = "serde_json" -version = "1.0.138" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", @@ -2231,6 +2230,25 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "signature" version = "1.6.4" @@ -2248,9 +2266,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "socket2" @@ -2672,7 +2690,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "427f2d0d6dc0bb49f16cef5e7f975180d2e80aab9bdd3b2af68e2d029ec63f43" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "solana-account-info", "solana-instruction", "solana-program-error", @@ -2763,13 +2781,15 @@ dependencies = [ [[package]] name = "solana-logger" -version = "2.2.1" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "593dbcb81439d37b02757e90bd9ab56364de63f378c55db92a6fbd6a2e47ab36" +checksum = "db8e777ec1afd733939b532a42492d888ec7c88d8b4127a5d867eb45c6eb5cd5" dependencies = [ "env_logger", "lazy_static", + "libc", "log", + "signal-hook", ] [[package]] @@ -2866,7 +2886,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "004f2d2daf407b3ec1a1ca5ec34b3ccdfd6866dd2d3c7d0715004a96e4b6d127" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -3162,7 +3182,7 @@ dependencies = [ "bs58", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -3554,12 +3574,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - [[package]] name = "spl-discriminator" version = "0.4.1" @@ -3580,7 +3594,7 @@ checksum = "d9e8418ea6269dcfb01c712f0444d2c75542c04448b480e87de59d2865edc750" dependencies = [ "quote", "spl-discriminator-syn", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -3592,18 +3606,27 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.98", + "syn 2.0.99", "thiserror 1.0.69", ] [[package]] name = "spl-elgamal-registry" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce0f668975d2b0536e8a8fd60e56a05c467f06021dae037f1d0cfed0de2e231d" +source = "git+https://github.com/solana-program/token-2022?rev=00e0f4723c2606c0facbb4921e1b2e2e030d1fa6#00e0f4723c2606c0facbb4921e1b2e2e030d1fa6" dependencies = [ "bytemuck", - "solana-program", + "solana-account-info", + "solana-cpi", + "solana-instruction", + "solana-msg", + "solana-program-entrypoint", + "solana-program-error", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-system-interface", + "solana-sysvar", "solana-zk-sdk", "spl-pod", "spl-token-confidential-transfer-proof-extraction", @@ -3625,9 +3648,9 @@ dependencies = [ [[package]] name = "spl-pod" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a7d5950993e1ff2680bd989df298eeb169367fb2f9deeef1f132de6e4e8016" +checksum = "d994afaf86b779104b4a95ba9ca75b8ced3fdb17ee934e38cb69e72afbe17799" dependencies = [ "borsh 1.5.5", "bytemuck", @@ -3640,7 +3663,7 @@ dependencies = [ "solana-program-option", "solana-pubkey", "solana-zk-sdk", - "thiserror 1.0.69", + "thiserror 2.0.12", ] [[package]] @@ -3665,7 +3688,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -3708,16 +3731,31 @@ dependencies = [ [[package]] name = "spl-token-2022" version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9048b26b0df0290f929ff91317c83db28b3ef99af2b3493dd35baa146774924c" +source = "git+https://github.com/solana-program/token-2022?rev=00e0f4723c2606c0facbb4921e1b2e2e030d1fa6#00e0f4723c2606c0facbb4921e1b2e2e030d1fa6" dependencies = [ "arrayref", "bytemuck", "num-derive", "num-traits", "num_enum", - "solana-program", + "solana-account-info", + "solana-clock", + "solana-cpi", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-native-token", + "solana-program-entrypoint", + "solana-program-error", + "solana-program-memory", + "solana-program-option", + "solana-program-pack", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", "solana-security-txt", + "solana-system-interface", + "solana-sysvar", "solana-zk-sdk", "spl-elgamal-registry", "spl-memo", @@ -3736,8 +3774,7 @@ dependencies = [ [[package]] name = "spl-token-confidential-transfer-ciphertext-arithmetic" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "170378693c5516090f6d37ae9bad2b9b6125069be68d9acd4865bbe9fc8499fd" +source = "git+https://github.com/solana-program/token-2022?rev=00e0f4723c2606c0facbb4921e1b2e2e030d1fa6#00e0f4723c2606c0facbb4921e1b2e2e030d1fa6" dependencies = [ "base64 0.22.1", "bytemuck", @@ -3748,12 +3785,17 @@ dependencies = [ [[package]] name = "spl-token-confidential-transfer-proof-extraction" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff2d6a445a147c9d6dd77b8301b1e116c8299601794b558eafa409b342faf96" +source = "git+https://github.com/solana-program/token-2022?rev=00e0f4723c2606c0facbb4921e1b2e2e030d1fa6#00e0f4723c2606c0facbb4921e1b2e2e030d1fa6" dependencies = [ "bytemuck", + "solana-account-info", "solana-curve25519", - "solana-program", + "solana-instruction", + "solana-instructions-sysvar", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "solana-sdk-ids", "solana-zk-sdk", "spl-pod", "thiserror 2.0.12", @@ -3762,8 +3804,7 @@ dependencies = [ [[package]] name = "spl-token-confidential-transfer-proof-generation" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e3597628b0d2fe94e7900fd17cdb4cfbb31ee35c66f82809d27d86e44b2848b" +source = "git+https://github.com/solana-program/token-2022?rev=00e0f4723c2606c0facbb4921e1b2e2e030d1fa6#00e0f4723c2606c0facbb4921e1b2e2e030d1fa6" dependencies = [ "curve25519-dalek 4.1.3", "solana-zk-sdk", @@ -3835,8 +3876,11 @@ dependencies = [ "solana-system-interface", "solana-sysvar", "spl-pod", + "spl-tlv-account-resolution", "spl-token", "spl-token-2022", + "spl-transfer-hook-interface", + "test-transfer-hook", "thiserror 2.0.12", ] @@ -3908,9 +3952,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.98" +version = "2.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2" dependencies = [ "proc-macro2", "quote", @@ -3931,7 +3975,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -3964,6 +4008,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "test-transfer-hook" +version = "0.1.0" +dependencies = [ + "solana-account-info", + "solana-program-entrypoint", + "solana-program-error", + "solana-pubkey", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -3990,7 +4044,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -4001,7 +4055,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -4016,9 +4070,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" dependencies = [ "tinyvec_macros", ] @@ -4084,9 +4138,9 @@ checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.22.23" +version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap", "toml_datetime", @@ -4126,15 +4180,15 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicode-ident" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "universal-hash" @@ -4255,7 +4309,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", "wasm-bindgen-shared", ] @@ -4290,7 +4344,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4501,9 +4555,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e49d2d35d3fad69b39b94139037ecfb4f359f08958b9c11e7315ce770462419" +checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" dependencies = [ "memchr", ] @@ -4550,7 +4604,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", "synstructure", ] @@ -4572,27 +4626,27 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] name = "zerofrom" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", "synstructure", ] @@ -4613,7 +4667,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -4635,5 +4689,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] diff --git a/Cargo.toml b/Cargo.toml index 8eb2a473..d8504bbd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["program"] +members = ["program", "program/tests/programs/test-transfer-hook"] [workspace.metadata.cli] solana = "2.1.0" diff --git a/package.json b/package.json index e0f212f0..efe42f25 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": true, "scripts": { - "programs:build": "zx ./scripts/rust/build-sbf.mjs program", + "programs:build": "cargo-build-sbf", "programs:test": "zx ./scripts/rust/test-sbf.mjs program", "programs:format": "zx ./scripts/rust/format.mjs program", "programs:lint": "zx ./scripts/rust/lint.mjs program", diff --git a/program/Cargo.toml b/program/Cargo.toml index d948a41c..6d216149 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -28,16 +28,21 @@ solana-pubkey = "2.2.1" solana-rent = "2.2.1" solana-system-interface = { version = "1.0.0", features = ["bincode"] } solana-sysvar = "2.2.1" -spl-pod = "0.5.0" +spl-pod = "0.5.1" spl-token = { version = "7.0.0", features = ["no-entrypoint"] } -spl-token-2022 = { version = "7.0.0", features = ["no-entrypoint"] } +spl-transfer-hook-interface = "0.9.0" thiserror = "2.0.12" +# Should depend on the next crate version after 7.0.0 when https://github.com/solana-program/token-2022/pull/253 is deployed +spl-token-2022 = { git = "https://github.com/solana-program/token-2022", rev = "00e0f4723c2606c0facbb4921e1b2e2e030d1fa6", features = ["no-entrypoint"] } + [dev-dependencies] mollusk-svm = "0.1.1" mollusk-svm-programs-token = "0.1.1" +spl-tlv-account-resolution = "0.9.0" solana-account = "2.2.1" solana-sdk-ids = "2.2.1" +test-transfer-hook = { path = "tests/programs/test-transfer-hook", features = ["no-entrypoint"] } [lib] crate-type = ["cdylib", "lib"] diff --git a/program/src/processor.rs b/program/src/processor.rs index 4afe22ab..d400a522 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -20,6 +20,7 @@ use { spl_token_2022::{ extension::PodStateWithExtensions, instruction::initialize_mint2, + onchain::{extract_multisig_accounts, invoke_transfer_checked}, pod::{PodAccount, PodMint}, }, }; @@ -180,7 +181,6 @@ pub fn process_wrap(accounts: &[AccountInfo], amount: u64) -> ProgramResult { let unwrapped_mint = next_account_info(account_info_iter)?; let unwrapped_escrow = next_account_info(account_info_iter)?; let transfer_authority = next_account_info(account_info_iter)?; - let multisig_signer_accounts = account_info_iter.as_slice(); // Validate accounts @@ -207,24 +207,16 @@ pub fn process_wrap(accounts: &[AccountInfo], amount: u64) -> ProgramResult { let unwrapped_mint_data = unwrapped_mint.try_borrow_data()?; let unwrapped_mint_state = PodStateWithExtensions::::unpack(&unwrapped_mint_data)?; - - let multisig_signer_pubkeys = multisig_signer_accounts - .iter() - .map(|account| account.key) - .collect::>(); - - invoke( - &spl_token_2022::instruction::transfer_checked( - unwrapped_token_program.key, - unwrapped_token_account.key, - unwrapped_mint.key, - unwrapped_escrow.key, - transfer_authority.key, - &multisig_signer_pubkeys, - amount, - unwrapped_mint_state.base.decimals, - )?, - &accounts[5..], + invoke_transfer_checked( + unwrapped_token_program.key, + unwrapped_token_account.clone(), + unwrapped_mint.clone(), + unwrapped_escrow.clone(), + transfer_authority.clone(), + &accounts[9..], + amount, + unwrapped_mint_state.base.decimals, + &[], )?; // Mint wrapped tokens to recipient @@ -268,7 +260,7 @@ pub fn process_unwrap(accounts: &[AccountInfo], amount: u64) -> ProgramResult { let wrapped_token_account = next_account_info(account_info_iter)?; let wrapped_mint = next_account_info(account_info_iter)?; let transfer_authority = next_account_info(account_info_iter)?; - let multisig_signer_accounts = account_info_iter.as_slice(); + let additional_accounts = account_info_iter.as_slice(); // Validate accounts @@ -285,9 +277,9 @@ pub fn process_unwrap(accounts: &[AccountInfo], amount: u64) -> ProgramResult { // Burn wrapped tokens - let multisig_signer_pubkeys = multisig_signer_accounts + let multisig_signer_keys = extract_multisig_accounts(transfer_authority, additional_accounts)? .iter() - .map(|account| account.key) + .map(|a| a.key) .collect::>(); invoke( @@ -296,7 +288,7 @@ pub fn process_unwrap(accounts: &[AccountInfo], amount: u64) -> ProgramResult { wrapped_token_account.key, wrapped_mint.key, transfer_authority.key, - &multisig_signer_pubkeys, + &multisig_signer_keys, amount, )?, &accounts[6..], @@ -309,23 +301,15 @@ pub fn process_unwrap(accounts: &[AccountInfo], amount: u64) -> ProgramResult { let bump_seed = [bump]; let signer_seeds = get_wrapped_mint_authority_signer_seeds(wrapped_mint.key, &bump_seed); - invoke_signed( - &spl_token_2022::instruction::transfer_checked( - unwrapped_token_program.key, - unwrapped_escrow.key, - unwrapped_mint.key, - recipient_unwrapped_token.key, - wrapped_mint_authority.key, - &[], - amount, - unwrapped_mint_state.base.decimals, - )?, - &[ - unwrapped_escrow.clone(), - unwrapped_mint.clone(), - recipient_unwrapped_token.clone(), - wrapped_mint_authority.clone(), - ], + invoke_transfer_checked( + unwrapped_token_program.key, + unwrapped_escrow.clone(), + unwrapped_mint.clone(), + recipient_unwrapped_token.clone(), + wrapped_mint_authority.clone(), + additional_accounts, + amount, + unwrapped_mint_state.base.decimals, &[&signer_seeds], )?; diff --git a/program/tests/helpers/common.rs b/program/tests/helpers/common.rs index cf9d5317..70d90a6c 100644 --- a/program/tests/helpers/common.rs +++ b/program/tests/helpers/common.rs @@ -13,12 +13,20 @@ use { optional_keys::OptionalNonZeroPubkey, primitives::{PodBool, PodU64}, }, + spl_tlv_account_resolution::{account::ExtraAccountMeta, state::ExtraAccountMetaList}, spl_token_2022::{ extension::{ - mint_close_authority::MintCloseAuthority, transfer_fee::TransferFeeConfig, + mint_close_authority::MintCloseAuthority, + transfer_fee::TransferFeeConfig, + transfer_hook::{TransferHook, TransferHookAccount}, BaseStateWithExtensionsMut, ExtensionType, PodStateWithExtensionsMut, + StateWithExtensionsMut, }, pod::{PodCOption, PodMint}, + state::{AccountState, Mint}, + }, + spl_transfer_hook_interface::{ + get_extra_account_metas_address, instruction::ExecuteInstruction, }, std::convert::TryFrom, }; @@ -27,6 +35,11 @@ pub fn init_mollusk() -> Mollusk { let mut mollusk = Mollusk::new(&spl_token_wrap::id(), "spl_token_wrap"); mollusk_svm_programs_token::token::add_program(&mut mollusk); mollusk_svm_programs_token::token2022::add_program(&mut mollusk); + mollusk.add_program( + &test_transfer_hook::id(), + "test_transfer_hook", + &mollusk_svm::program::loader_keys::LOADER_V3, + ); mollusk } @@ -133,3 +146,104 @@ pub fn setup_multisig(program: TokenProgram) -> TransferAuthority { signers: vec![signer0_key, signer1_key, signer2_key], } } + +pub fn setup_counter(hook_program_id: Pubkey) -> KeyedAccount { + let account = Account { + lamports: Rent::default().minimum_balance(1), + owner: hook_program_id, + data: vec![0], + executable: false, + rent_epoch: 0, + }; + KeyedAccount { + key: Pubkey::new_unique(), + account, + } +} + +pub fn unwrapped_mint_with_transfer_hook(hook_program_id: Pubkey) -> KeyedAccount { + let mint_len = + ExtensionType::try_calculate_account_len::(&[ExtensionType::TransferHook]).unwrap(); + let mut data = vec![0u8; mint_len]; + let mut mint = PodStateWithExtensionsMut::::unpack_uninitialized(&mut data).unwrap(); + + let extension = mint.init_extension::(true).unwrap(); + extension.program_id = OptionalNonZeroPubkey(hook_program_id); + + mint.base.mint_authority = PodCOption::some(Pubkey::new_unique()); + mint.base.decimals = MINT_DECIMALS; + mint.base.supply = MINT_SUPPLY.into(); + mint.base.freeze_authority = PodCOption::none(); + mint.base.is_initialized = PodBool::from_bool(true); + + mint.init_account_type().unwrap(); + + KeyedAccount { + key: Pubkey::new_unique(), + account: Account { + lamports: Rent::default().minimum_balance(Mint::LEN), + data, + owner: spl_token_2022::id(), + ..Default::default() + }, + } +} + +pub fn setup_transfer_hook_account(owner: &Pubkey, mint: &KeyedAccount, amount: u64) -> Account { + let account_size = + ExtensionType::try_calculate_account_len::(&[ + ExtensionType::TransferHookAccount, + ]) + .unwrap(); + let mut account_data = vec![0; account_size]; + let mut state = StateWithExtensionsMut::::unpack_uninitialized( + &mut account_data, + ) + .unwrap(); + + let extension = state.init_extension::(true).unwrap(); + extension.transferring = false.into(); + + state.base = spl_token_2022::state::Account { + mint: mint.key, + amount, + owner: *owner, + state: AccountState::Initialized, + ..Default::default() + }; + state.pack_base(); + state.init_account_type().unwrap(); + + Account { + lamports: Rent::default().minimum_balance(Mint::LEN), + data: account_data, + owner: spl_token_2022::id(), + ..Default::default() + } +} + +pub fn setup_validation_state_account( + hook_program_id: &Pubkey, + counter: &KeyedAccount, + unwrapped_mint: &KeyedAccount, +) -> KeyedAccount { + let validation_state_pubkey = + get_extra_account_metas_address(&unwrapped_mint.key, hook_program_id); + let extra_account_metas = + vec![ExtraAccountMeta::new_with_pubkey(&counter.key, false, true).unwrap()]; + let account_size = ExtraAccountMetaList::size_of(extra_account_metas.len()).unwrap(); + let mut validation_data = vec![0; account_size]; + ExtraAccountMetaList::init::(&mut validation_data, &extra_account_metas) + .unwrap(); + + KeyedAccount { + key: validation_state_pubkey, + account: Account { + lamports: Rent::default().minimum_balance(account_size), + data: validation_data, + owner: *hook_program_id, + executable: false, + rent_epoch: 0, + }, + } +} diff --git a/program/tests/helpers/unwrap_builder.rs b/program/tests/helpers/unwrap_builder.rs index bccaf13e..4f81ac87 100644 --- a/program/tests/helpers/unwrap_builder.rs +++ b/program/tests/helpers/unwrap_builder.rs @@ -6,6 +6,7 @@ use { }, mollusk_svm::{result::Check, Mollusk}, solana_account::Account, + solana_instruction::AccountMeta, solana_pubkey::Pubkey, spl_token_2022::{ extension::{ @@ -30,6 +31,10 @@ pub struct UnwrapBuilder<'a> { unwrapped_token_program: Option, wrapped_token_program: Option, transfer_authority: Option, + unwrapped_mint: Option, + unwrapped_escrow_account: Option, + extra_accounts: Vec, + recipient_token_account: Option, } impl Default for UnwrapBuilder<'_> { @@ -47,6 +52,10 @@ impl Default for UnwrapBuilder<'_> { unwrapped_token_program: None, wrapped_token_program: None, transfer_authority: None, + unwrapped_mint: None, + unwrapped_escrow_account: None, + extra_accounts: vec![], + recipient_token_account: None, } } } @@ -72,6 +81,19 @@ impl<'a> UnwrapBuilder<'a> { self } + pub fn unwrapped_escrow_account(mut self, account: Account) -> Self { + self.unwrapped_escrow_account = Some(KeyedAccount { + key: Pubkey::new_unique(), + account, + }); + self + } + + pub fn unwrapped_mint(mut self, account: KeyedAccount) -> Self { + self.unwrapped_mint = Some(account); + self + } + pub fn wrapped_mint(mut self, account: KeyedAccount) -> Self { self.wrapped_mint = Some(account); self @@ -97,11 +119,24 @@ impl<'a> UnwrapBuilder<'a> { self } + pub fn recipient_token_account(mut self, account: Account) -> Self { + self.recipient_token_account = Some(KeyedAccount { + key: Pubkey::new_unique(), + account, + }); + self + } + pub fn transfer_authority(mut self, auth: TransferAuthority) -> Self { self.transfer_authority = Some(auth); self } + pub fn add_extra_account(mut self, keyed_account: KeyedAccount) -> Self { + self.extra_accounts.push(keyed_account); + self + } + pub fn check(mut self, check: Check<'a>) -> Self { self.checks.push(check); self @@ -180,14 +215,14 @@ impl<'a> UnwrapBuilder<'a> { .unwrapped_token_program .unwrap_or(TokenProgram::SplToken); - let unwrapped_mint = KeyedAccount { + let unwrapped_mint = self.unwrapped_mint.clone().unwrap_or(KeyedAccount { key: Pubkey::new_unique(), account: setup_mint( unwrapped_token_program, &self.mollusk.sysvars.rent, Pubkey::new_unique(), ), - }; + }); let wrapped_token_program = self .wrapped_token_program @@ -211,24 +246,28 @@ impl<'a> UnwrapBuilder<'a> { ); // Setup escrow account - let escrow = self.setup_token_account( - unwrapped_token_program, - &unwrapped_mint, - &self - .unwrapped_escrow_owner - .unwrap_or(wrapped_mint_authority), - self.escrow_starting_amount.unwrap_or(100_000), + let escrow = self.unwrapped_escrow_account.clone().unwrap_or( + self.setup_token_account( + unwrapped_token_program, + &unwrapped_mint, + &self + .unwrapped_escrow_owner + .unwrap_or(wrapped_mint_authority), + self.escrow_starting_amount.unwrap_or(100_000), + ), ); // Setup recipient account for unwrapped tokens - let recipient = self.setup_token_account( - unwrapped_token_program, - &unwrapped_mint, - &Pubkey::new_unique(), - self.recipient_starting_amount.unwrap_or(0), - ); + let recipient = self.recipient_token_account.clone().unwrap_or_else(|| { + self.setup_token_account( + unwrapped_token_program, + &unwrapped_mint, + &Pubkey::new_unique(), + self.recipient_starting_amount.unwrap_or(0), + ) + }); - let instruction = unwrap( + let mut instruction = unwrap( &spl_token_wrap::id(), &escrow.key, &recipient.key, @@ -259,6 +298,13 @@ impl<'a> UnwrapBuilder<'a> { accounts.push((*signer_key, Account::default())); } + for extra_account in &self.extra_accounts { + instruction + .accounts + .push(AccountMeta::new(extra_account.key, false)); + accounts.push(extra_account.pair()); + } + if self.checks.is_empty() { self.checks.push(Check::success()); } @@ -287,6 +333,14 @@ impl<'a> UnwrapBuilder<'a> { key: recipient.key, account: result.get_account(&recipient.key).unwrap().clone(), }, + extra_accounts: self + .extra_accounts + .iter() + .map(|keyed_account| KeyedAccount { + key: keyed_account.key, + account: result.get_account(&keyed_account.key).unwrap().clone(), + }) + .collect(), } } } @@ -296,4 +350,5 @@ pub struct UnwrapResult { pub unwrapped_escrow: KeyedAccount, pub wrapped_mint: KeyedAccount, pub recipient_unwrapped_token: KeyedAccount, + pub extra_accounts: Vec, } diff --git a/program/tests/helpers/wrap_builder.rs b/program/tests/helpers/wrap_builder.rs index cc8ea8e6..4187c496 100644 --- a/program/tests/helpers/wrap_builder.rs +++ b/program/tests/helpers/wrap_builder.rs @@ -5,6 +5,7 @@ use { }, mollusk_svm::{result::Check, Mollusk}, solana_account::Account, + solana_instruction::AccountMeta, solana_program_pack::Pack, solana_pubkey::Pubkey, spl_token_2022::{ @@ -29,6 +30,7 @@ pub struct WrapBuilder<'a> { recipient: Option, checks: Vec>, wrapped_mint: Option, + unwrapped_mint: Option, unwrapped_escrow_addr: Option, wrapped_mint_authority: Option, unwrapped_escrow_owner: Option, @@ -38,6 +40,8 @@ pub struct WrapBuilder<'a> { unwrapped_token_program: Option, wrapped_token_program: Option, transfer_authority: Option, + extra_accounts: Vec, + unwrapped_token_account: Option, } impl Default for WrapBuilder<'_> { @@ -48,6 +52,7 @@ impl Default for WrapBuilder<'_> { recipient: None, checks: vec![], wrapped_mint: None, + unwrapped_mint: None, unwrapped_escrow_addr: None, wrapped_mint_authority: None, unwrapped_escrow_owner: None, @@ -57,6 +62,8 @@ impl Default for WrapBuilder<'_> { unwrapped_token_program: None, wrapped_token_program: None, transfer_authority: None, + extra_accounts: vec![], + unwrapped_token_account: None, } } } @@ -72,6 +79,16 @@ impl<'a> WrapBuilder<'a> { self } + pub fn unwrapped_token_account(mut self, account: Account) -> Self { + self.unwrapped_token_account = Some(account); + self + } + + pub fn unwrapped_mint(mut self, account: KeyedAccount) -> Self { + self.unwrapped_mint = Some(account); + self + } + pub fn wrapped_mint(mut self, account: KeyedAccount) -> Self { self.wrapped_mint = Some(account); self @@ -117,6 +134,11 @@ impl<'a> WrapBuilder<'a> { self } + pub fn add_extra_account(mut self, keyed_account: KeyedAccount) -> Self { + self.extra_accounts.push(keyed_account); + self + } + pub fn check(mut self, check: Check<'a>) -> Self { self.checks.push(check); self @@ -197,35 +219,38 @@ impl<'a> WrapBuilder<'a> { .unwrapped_token_program .unwrap_or(TokenProgram::SplToken); - let unwrapped_mint = KeyedAccount { + let unwrapped_mint = self.unwrapped_mint.clone().unwrap_or(KeyedAccount { key: Pubkey::new_unique(), account: setup_mint( unwrapped_token_program, &self.mollusk.sysvars.rent, Pubkey::new_unique(), ), - }; - - let mut unwrapped_token_account = Account { - lamports: 100_000_000, - owner: unwrapped_mint.account.owner, - data: vec![0; spl_token::state::Account::LEN], - ..Default::default() - }; + }); let wrap_amount = self.wrap_amount.unwrap_or(500); - let token = spl_token::state::Account { - mint: unwrapped_mint.key, - owner: unwrapped_token_account_authority.keyed_account.key, - amount: self.unwrapped_token_starting_amount.unwrap_or(wrap_amount), - delegate: None.into(), - state: spl_token::state::AccountState::Initialized, - is_native: None.into(), - delegated_amount: 0, - close_authority: None.into(), - }; - spl_token::state::Account::pack(token, &mut unwrapped_token_account.data).unwrap(); + let unwrapped_token_account = self.unwrapped_token_account.clone().unwrap_or_else(|| { + let mut account = Account { + lamports: 100_000_000, + owner: unwrapped_mint.account.owner, + data: vec![0; spl_token::state::Account::LEN], + ..Default::default() + }; + + let token = spl_token::state::Account { + mint: unwrapped_mint.key, + owner: unwrapped_token_account_authority.keyed_account.key, + amount: self.unwrapped_token_starting_amount.unwrap_or(wrap_amount), + delegate: None.into(), + state: spl_token::state::AccountState::Initialized, + is_native: None.into(), + delegated_amount: 0, + close_authority: None.into(), + }; + spl_token::state::Account::pack(token, &mut account.data).unwrap(); + account + }); let wrapped_token_program = self .wrapped_token_program @@ -269,7 +294,7 @@ impl<'a> WrapBuilder<'a> { .unwrapped_escrow_addr .unwrap_or_else(Pubkey::new_unique); - let instruction = wrap( + let mut instruction = wrap( &spl_token_wrap::id(), &recipient.key, &wrapped_mint.key, @@ -307,6 +332,13 @@ impl<'a> WrapBuilder<'a> { accounts.push((*signer_key, Account::default())); } + for extra_account in &self.extra_accounts { + instruction + .accounts + .push(AccountMeta::new(extra_account.key, false)); + accounts.push(extra_account.pair()); + } + if self.checks.is_empty() { self.checks.push(Check::success()); } @@ -338,6 +370,14 @@ impl<'a> WrapBuilder<'a> { key: recipient.key, account: result.get_account(&recipient.key).unwrap().clone(), }, + extra_accounts: self + .extra_accounts + .iter() + .map(|keyed_account| KeyedAccount { + key: keyed_account.key, + account: result.get_account(&keyed_account.key).unwrap().clone(), + }) + .collect(), } } } @@ -347,4 +387,5 @@ pub struct WrapResult { pub unwrapped_escrow: KeyedAccount, pub wrapped_mint: KeyedAccount, pub recipient_wrapped_token: KeyedAccount, + pub extra_accounts: Vec, } diff --git a/program/tests/programs/test-transfer-hook/Cargo.toml b/program/tests/programs/test-transfer-hook/Cargo.toml new file mode 100644 index 00000000..97d7defc --- /dev/null +++ b/program/tests/programs/test-transfer-hook/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "test-transfer-hook" +version = "0.1.0" +edition = "2021" +publish = false + +[features] +no-entrypoint = [] + +[dependencies] +solana-account-info = "2.2.1" +solana-program-entrypoint = "2.2.1" +solana-program-error = "2.2.1" +solana-pubkey = "2.2.1" + +[lib] +crate-type = ["cdylib", "lib"] + +[lints] +workspace = true diff --git a/program/tests/programs/test-transfer-hook/src/entrypoint.rs b/program/tests/programs/test-transfer-hook/src/entrypoint.rs new file mode 100644 index 00000000..cc9e27eb --- /dev/null +++ b/program/tests/programs/test-transfer-hook/src/entrypoint.rs @@ -0,0 +1,17 @@ +//! Program entrypoint + +#![cfg(all(target_os = "solana", not(feature = "no-entrypoint")))] + +use { + solana_account_info::AccountInfo, solana_program_error::ProgramResult, solana_pubkey::Pubkey, +}; + +solana_program_entrypoint::entrypoint!(process_instruction); + +fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8], +) -> ProgramResult { + crate::processor::process_instruction(program_id, accounts, instruction_data) +} diff --git a/program/tests/programs/test-transfer-hook/src/lib.rs b/program/tests/programs/test-transfer-hook/src/lib.rs new file mode 100644 index 00000000..08eb3429 --- /dev/null +++ b/program/tests/programs/test-transfer-hook/src/lib.rs @@ -0,0 +1,6 @@ +//! Program entrypoint + +pub mod entrypoint; +pub mod processor; + +solana_pubkey::declare_id!("HookipyvZYC9h3AG1x7qfdXmE3TnqvqV9ZK43ErZfBrW"); diff --git a/program/tests/programs/test-transfer-hook/src/processor.rs b/program/tests/programs/test-transfer-hook/src/processor.rs new file mode 100644 index 00000000..71abc1f0 --- /dev/null +++ b/program/tests/programs/test-transfer-hook/src/processor.rs @@ -0,0 +1,28 @@ +//! Program state processor + +use { + solana_account_info::{next_account_info, AccountInfo}, + solana_program_error::ProgramResult, + solana_pubkey::Pubkey, +}; + +pub fn process_instruction( + _program_id: &Pubkey, + accounts: &[AccountInfo], + _instruction_data: &[u8], +) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + + let _source = next_account_info(account_info_iter)?; + let _mint = next_account_info(account_info_iter)?; + let _destination = next_account_info(account_info_iter)?; + let _authority = next_account_info(account_info_iter)?; + let _validation_state = next_account_info(account_info_iter)?; + let counter_account = next_account_info(account_info_iter)?; + + // Increment the counter + let mut counter_data = counter_account.try_borrow_mut_data()?; + counter_data[0] = counter_data[0].checked_add(1).unwrap(); + + Ok(()) +} diff --git a/program/tests/test_unwrap.rs b/program/tests/test_unwrap.rs index b8ee6685..45f75d4c 100644 --- a/program/tests/test_unwrap.rs +++ b/program/tests/test_unwrap.rs @@ -1,17 +1,21 @@ use { crate::helpers::{ - common::{setup_multisig, MINT_SUPPLY}, + common::{ + setup_counter, setup_multisig, setup_transfer_hook_account, + setup_validation_state_account, unwrapped_mint_with_transfer_hook, MINT_SUPPLY, + }, create_mint_builder::{CreateMintBuilder, KeyedAccount, TokenProgram}, unwrap_builder::{UnwrapBuilder, UnwrapResult}, + wrap_builder::TransferAuthority, }, - mollusk_svm::result::Check, + mollusk_svm::{program::create_program_account_loader_v3, result::Check}, solana_pubkey::Pubkey, spl_token_2022::{ error::TokenError, extension::PodStateWithExtensions, pod::{PodAccount, PodMint}, }, - spl_token_wrap::error::TokenWrapError, + spl_token_wrap::{error::TokenWrapError, get_wrapped_mint_address, get_wrapped_mint_authority}, }; pub mod helpers; @@ -245,3 +249,73 @@ fn test_unwrap_with_spl_token_2022_multisig() { &wrap_result, ); } + +#[test] +fn test_unwrap_with_transfer_hook() { + let hook_program_id = test_transfer_hook::id(); + + // Testing if counter account is incremented via transfer hook + let counter = setup_counter(hook_program_id); + let unwrapped_mint = unwrapped_mint_with_transfer_hook(hook_program_id); + + let source_starting_amount = 50_000; + let recipient_starting_amount = 50_000; + let escrow_starting_amount = 150_000; + let unwrap_amount = 12_555; + + // Escrow & unwrapped token account need to have TransferHook extension as well + let transfer_authority = TransferAuthority { + keyed_account: Default::default(), + signers: vec![], + }; + let recipient_token_account = setup_transfer_hook_account( + &transfer_authority.keyed_account.key, + &unwrapped_mint, + recipient_starting_amount, + ); + + let escrow_account = { + let wrapped_mint_addr = + get_wrapped_mint_address(&unwrapped_mint.key, &spl_token_2022::id()); + let mint_authority = get_wrapped_mint_authority(&wrapped_mint_addr); + setup_transfer_hook_account(&mint_authority, &unwrapped_mint, escrow_starting_amount) + }; + + // Validation state account required in order for counter account to be passed + // in transfer hook + let validation_state_account = + setup_validation_state_account(&hook_program_id, &counter, &unwrapped_mint); + + // Execute the unwrap instruction using our UnwrapBuilder. + let unwrap_result = UnwrapBuilder::default() + .unwrapped_token_program(TokenProgram::SplToken2022) + .wrapped_token_program(TokenProgram::SplToken2022) + .wrapped_token_starting_amount(source_starting_amount) + .recipient_starting_amount(recipient_starting_amount) + .recipient_token_account(recipient_token_account) + .transfer_authority(transfer_authority) + .escrow_starting_amount(escrow_starting_amount) + .unwrap_amount(unwrap_amount) + .unwrapped_mint(unwrapped_mint) + .unwrapped_escrow_account(escrow_account) + .add_extra_account(counter) + .add_extra_account(KeyedAccount { + key: hook_program_id, + account: create_program_account_loader_v3(&hook_program_id), + }) + .add_extra_account(validation_state_account) + .check(Check::success()) + .execute(); + + assert_unwrap_result( + source_starting_amount, + recipient_starting_amount, + escrow_starting_amount, + unwrap_amount, + &unwrap_result, + ); + + // Verify counter was incremented + let count = unwrap_result.extra_accounts[0].clone().account.data[0]; + assert_eq!(count, 1) +} diff --git a/program/tests/test_wrap.rs b/program/tests/test_wrap.rs index 29c4999b..899456e9 100644 --- a/program/tests/test_wrap.rs +++ b/program/tests/test_wrap.rs @@ -1,10 +1,13 @@ use { crate::helpers::{ - common::{setup_multisig, MINT_SUPPLY}, + common::{ + setup_counter, setup_multisig, setup_transfer_hook_account, + setup_validation_state_account, unwrapped_mint_with_transfer_hook, MINT_SUPPLY, + }, create_mint_builder::{CreateMintBuilder, KeyedAccount, TokenProgram}, - wrap_builder::{WrapBuilder, WrapResult}, + wrap_builder::{TransferAuthority, WrapBuilder, WrapResult}, }, - mollusk_svm::result::Check, + mollusk_svm::{program::create_program_account_loader_v3, result::Check}, solana_account::Account, solana_program_error::ProgramError, solana_program_pack::Pack, @@ -13,7 +16,7 @@ use { extension::PodStateWithExtensions, pod::{PodAccount, PodMint}, }, - spl_token_wrap::error::TokenWrapError, + spl_token_wrap::{error::TokenWrapError, get_wrapped_mint_address, get_wrapped_mint_authority}, }; pub mod helpers; @@ -62,13 +65,15 @@ fn test_incorrect_escrow_owner() { fn assert_wrap_result(starting_amount: u64, wrap_amount: u64, wrap_result: &WrapResult) { // Verify the unwrapped tokens were transferred to escrow let escrow_token = - spl_token::state::Account::unpack(&wrap_result.unwrapped_escrow.account.data).unwrap(); - assert_eq!(escrow_token.amount, wrap_amount); + PodStateWithExtensions::::unpack(&wrap_result.unwrapped_escrow.account.data) + .unwrap(); + assert_eq!(u64::from(escrow_token.base.amount), wrap_amount); // Verify the source account was debited let unwrapped_token = - spl_token::state::Account::unpack(&wrap_result.unwrapped_token.account.data).unwrap(); - assert_eq!(unwrapped_token.amount, 0); + PodStateWithExtensions::::unpack(&wrap_result.unwrapped_token.account.data) + .unwrap(); + assert_eq!(u64::from(unwrapped_token.base.amount), 0); // Verify wrapped tokens were minted to recipient let recipient_token = PodStateWithExtensions::::unpack( @@ -194,3 +199,62 @@ fn test_wrap_with_token_2022_multisig() { assert_wrap_result(starting_amount, wrap_amount, &wrap_result); } + +#[test] +fn test_wrap_with_transfer_hook() { + let hook_program_id = test_transfer_hook::id(); + + // Testing if counter account is incremented via transfer hook + let counter = setup_counter(hook_program_id); + let unwrapped_mint = unwrapped_mint_with_transfer_hook(hook_program_id); + + // Escrow & unwrapped token account need to have TransferHook extension as well + let wrap_amount = 12_555; + let transfer_authority = TransferAuthority { + keyed_account: Default::default(), + signers: vec![], + }; + let unwrapped_token_account = setup_transfer_hook_account( + &transfer_authority.keyed_account.key, + &unwrapped_mint, + wrap_amount, + ); + + let escrow_account = { + let wrapped_mint_addr = + get_wrapped_mint_address(&unwrapped_mint.key, &spl_token_2022::id()); + let mint_authority = get_wrapped_mint_authority(&wrapped_mint_addr); + setup_transfer_hook_account(&mint_authority, &unwrapped_mint, 0) + }; + + // Validation state account required in order for counter account to be passed + // in transfer hook + let validation_state_account = + setup_validation_state_account(&hook_program_id, &counter, &unwrapped_mint); + + let starting_amount = 50_000; + + let wrap_result = WrapBuilder::default() + .unwrapped_token_program(TokenProgram::SplToken2022) + .wrapped_token_program(TokenProgram::SplToken2022) + .recipient_starting_amount(starting_amount) + .wrap_amount(wrap_amount) + .unwrapped_mint(unwrapped_mint) + .transfer_authority(transfer_authority) + .unwrapped_token_account(unwrapped_token_account.clone()) + .unwrapped_escrow_account(escrow_account) + .add_extra_account(counter) + .add_extra_account(KeyedAccount { + key: hook_program_id, + account: create_program_account_loader_v3(&hook_program_id), + }) + .add_extra_account(validation_state_account) + .execute(); + + // Verify results + assert_wrap_result(starting_amount, wrap_amount, &wrap_result); + + // Verify counter was incremented + let count = wrap_result.extra_accounts[0].clone().account.data[0]; + assert_eq!(count, 1) +} diff --git a/scripts/rust/build-sbf.mjs b/scripts/rust/build-sbf.mjs deleted file mode 100644 index 64a80d34..00000000 --- a/scripts/rust/build-sbf.mjs +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env zx -import 'zx/globals'; -import { - cliArguments, - workingDirectory, -} from '../utils.mjs'; - -const [folder, ...args] = cliArguments(); -const manifestPath = path.join(workingDirectory, folder, 'Cargo.toml'); -await $`cargo-build-sbf --manifest-path ${manifestPath} ${args}`; diff --git a/scripts/rust/test-sbf.mjs b/scripts/rust/test-sbf.mjs index 971448c6..aedd358e 100644 --- a/scripts/rust/test-sbf.mjs +++ b/scripts/rust/test-sbf.mjs @@ -1,8 +1,8 @@ #!/usr/bin/env zx import 'zx/globals'; import { - cliArguments, - workingDirectory, + cliArguments, + workingDirectory, } from '../utils.mjs'; const [folder, ...args] = cliArguments();