From 83d49e4975fea5b9575b57e4808a43ea261211bd Mon Sep 17 00:00:00 2001 From: BlockyJ Date: Thu, 6 Feb 2025 23:06:41 +0100 Subject: [PATCH] [contract] feat: auth contracts (#131) --- apps/contract/.gitignore | 1 + apps/contract/Cargo.lock | 1906 +++++++++++++++++ apps/contract/Cargo.toml | 8 +- apps/contract/README.md | 50 +- .../auth-contracts/account-factory/Cargo.toml | 14 + .../account-factory/src/events.rs | 14 + .../auth-contracts/account-factory/src/lib.rs | 68 + .../account-factory/src/test.rs | 100 + .../auth-contracts/account/Cargo.toml | 20 + .../auth-contracts/account/src/base64_url.rs | 78 + .../auth-contracts/account/src/errors.rs | 15 + .../auth-contracts/account/src/events.rs | 49 + .../auth-contracts/account/src/lib.rs | 246 +++ .../auth-contracts/account/src/test.rs | 198 ++ .../auth-contracts/auth-controller/Cargo.toml | 16 + .../auth-controller/src/errors.rs | 21 + .../auth-controller/src/events.rs | 85 + .../auth-contracts/auth-controller/src/lib.rs | 360 ++++ .../auth-controller/src/test.rs | 349 +++ apps/contract/contracts/nft/Cargo.toml | 2 +- apps/contract/contracts/nft/nft.rs | 56 - apps/contract/contracts/nft/src/contract.rs | 56 + apps/contract/contracts/nft/src/errors.rs | 17 +- apps/contract/contracts/nft/src/events.rs | 14 +- apps/contract/contracts/nft/src/lib.rs | 6 +- apps/contract/contracts/nft/src/storage.rs | 8 +- apps/contract/contracts/nft/src/types.rs | 2 +- apps/contract/rust-toolchain.toml | 4 + 28 files changed, 3676 insertions(+), 87 deletions(-) create mode 100644 apps/contract/.gitignore create mode 100644 apps/contract/Cargo.lock create mode 100644 apps/contract/contracts/auth-contracts/account-factory/Cargo.toml create mode 100644 apps/contract/contracts/auth-contracts/account-factory/src/events.rs create mode 100644 apps/contract/contracts/auth-contracts/account-factory/src/lib.rs create mode 100644 apps/contract/contracts/auth-contracts/account-factory/src/test.rs create mode 100644 apps/contract/contracts/auth-contracts/account/Cargo.toml create mode 100644 apps/contract/contracts/auth-contracts/account/src/base64_url.rs create mode 100644 apps/contract/contracts/auth-contracts/account/src/errors.rs create mode 100644 apps/contract/contracts/auth-contracts/account/src/events.rs create mode 100644 apps/contract/contracts/auth-contracts/account/src/lib.rs create mode 100644 apps/contract/contracts/auth-contracts/account/src/test.rs create mode 100644 apps/contract/contracts/auth-contracts/auth-controller/Cargo.toml create mode 100644 apps/contract/contracts/auth-contracts/auth-controller/src/errors.rs create mode 100644 apps/contract/contracts/auth-contracts/auth-controller/src/events.rs create mode 100644 apps/contract/contracts/auth-contracts/auth-controller/src/lib.rs create mode 100644 apps/contract/contracts/auth-contracts/auth-controller/src/test.rs delete mode 100644 apps/contract/contracts/nft/nft.rs create mode 100644 apps/contract/contracts/nft/src/contract.rs create mode 100644 apps/contract/rust-toolchain.toml diff --git a/apps/contract/.gitignore b/apps/contract/.gitignore new file mode 100644 index 00000000..c41cc9e3 --- /dev/null +++ b/apps/contract/.gitignore @@ -0,0 +1 @@ +/target \ No newline at end of file diff --git a/apps/contract/Cargo.lock b/apps/contract/Cargo.lock new file mode 100644 index 00000000..bf74b97a --- /dev/null +++ b/apps/contract/Cargo.lock @@ -0,0 +1,1906 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "account_contract" +version = "0.1.0" +dependencies = [ + "hex", + "p256", + "rand 0.9.0", + "serde", + "serde-json-core", + "soroban-sdk", +] + +[[package]] +name = "account_factory" +version = "0.1.0" +dependencies = [ + "rand 0.8.5", + "soroban-sdk", +] + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy 0.7.35", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest 0.10.7", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "auth_controller" +version = "0.1.0" +dependencies = [ + "ed25519-dalek 1.0.1", + "hex", + "rand 0.7.3", + "soroban-sdk", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bitflags" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes-lit" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0adabf37211a5276e46335feabcbb1530c95eb3fdf85f324c7db942770aa025d" +dependencies = [ + "num-bigint", + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "cc" +version = "1.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "755717a7de9ec452bf7f3f1a3099085deabd7f2962b861dae91ecd7a365903d2" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crate-git-revision" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c521bf1f43d31ed2f73441775ed31935d77901cb3451e44b38a1c1612fcbaf98" +dependencies = [ + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctor" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" +dependencies = [ + "quote", + "syn 2.0.98", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.98", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "data-encoding" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature 2.2.0", + "spki", +] + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature 1.6.4", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature 2.2.0", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek 3.2.0", + "ed25519 1.5.3", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek 4.1.3", + "ed25519 2.2.3", + "rand_core 0.6.4", + "serde", + "sha2 0.10.8", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "escape-bytes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfcf67fea2815c2fc3b90873fae90957be12ff417335dfadc7f52927feb03b2" + +[[package]] +name = "ethnum" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c" + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", + "serde", +] + +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2 0.10.8", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "kindfi-nft" +version = "0.1.0" +dependencies = [ + "soroban-sdk", +] + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + +[[package]] +name = "log" +version = "0.4.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.8", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy 0.7.35", +] + +[[package]] +name = "prettyplease" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" +dependencies = [ + "proc-macro2", + "syn 2.0.98", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.0", + "zerocopy 0.8.17", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.0", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff" +dependencies = [ + "getrandom 0.3.1", + "zerocopy 0.8.17", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + +[[package]] +name = "ryu" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-json-core" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b81787e655bd59cecadc91f7b6b8651330b2be6c33246039a65e5cd6f4e0828" +dependencies = [ + "ryu", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "serde_json" +version = "1.0.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.7.1", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "soroban-builtin-sdk-macros" +version = "22.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2e42bf80fcdefb3aae6ff3c7101a62cf942e95320ed5b518a1705bc11c6b2f" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "soroban-env-common" +version = "22.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "027cd856171bfd6ad2c0ffb3b7dfe55ad7080fb3050c36ad20970f80da634472" +dependencies = [ + "arbitrary", + "crate-git-revision", + "ethnum", + "num-derive", + "num-traits", + "serde", + "soroban-env-macros", + "soroban-wasmi", + "static_assertions", + "stellar-xdr", + "wasmparser", +] + +[[package]] +name = "soroban-env-guest" +version = "22.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a07dda1ae5220d975979b19ad4fd56bc86ec7ec1b4b25bc1c5d403f934e592e" +dependencies = [ + "soroban-env-common", + "static_assertions", +] + +[[package]] +name = "soroban-env-host" +version = "22.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66e8b03a4191d485eab03f066336112b2a50541a7553179553dc838b986b94dd" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-serialize", + "curve25519-dalek 4.1.3", + "ecdsa", + "ed25519-dalek 2.1.1", + "elliptic-curve", + "generic-array", + "getrandom 0.2.15", + "hex-literal", + "hmac", + "k256", + "num-derive", + "num-integer", + "num-traits", + "p256", + "rand 0.8.5", + "rand_chacha 0.3.1", + "sec1", + "sha2 0.10.8", + "sha3", + "soroban-builtin-sdk-macros", + "soroban-env-common", + "soroban-wasmi", + "static_assertions", + "stellar-strkey", + "wasmparser", +] + +[[package]] +name = "soroban-env-macros" +version = "22.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00eff744764ade3bc480e4909e3a581a240091f3d262acdce80b41f7069b2bd9" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "serde", + "serde_json", + "stellar-xdr", + "syn 2.0.98", +] + +[[package]] +name = "soroban-ledger-snapshot" +version = "22.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08e0570df0224a1742567d418d2f2f926dc8d3580cf568454b7c0990bd003845" +dependencies = [ + "serde", + "serde_json", + "serde_with", + "soroban-env-common", + "soroban-env-host", + "thiserror", +] + +[[package]] +name = "soroban-sdk" +version = "22.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0ea22f82d9709e222fedc1cf777cf80b0fef9d62e78ce00134bde9f94865c8" +dependencies = [ + "arbitrary", + "bytes-lit", + "ctor", + "derive_arbitrary", + "ed25519-dalek 2.1.1", + "rand 0.8.5", + "rustc_version", + "serde", + "serde_json", + "soroban-env-guest", + "soroban-env-host", + "soroban-ledger-snapshot", + "soroban-sdk-macros", + "stellar-strkey", +] + +[[package]] +name = "soroban-sdk-macros" +version = "22.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0160d40380e20b8618c449d457f06a40e16dd2fc5d92422b379c3d59519f9da5" +dependencies = [ + "crate-git-revision", + "darling", + "itertools", + "proc-macro2", + "quote", + "rustc_version", + "sha2 0.10.8", + "soroban-env-common", + "soroban-spec", + "soroban-spec-rust", + "stellar-xdr", + "syn 2.0.98", +] + +[[package]] +name = "soroban-spec" +version = "22.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05a2d6d7898703dd96c48fe865ebbd42bbdb6637007cefe27494e579c954faf9" +dependencies = [ + "base64 0.13.1", + "stellar-xdr", + "thiserror", + "wasmparser", +] + +[[package]] +name = "soroban-spec-rust" +version = "22.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1439c0446625e545bb08a848e0fee89d3f9afb568375263342e27d00b8548c8f" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "sha2 0.10.8", + "soroban-spec", + "stellar-xdr", + "syn 2.0.98", + "thiserror", +] + +[[package]] +name = "soroban-wasmi" +version = "0.31.1-soroban.20.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710403de32d0e0c35375518cb995d4fc056d0d48966f2e56ea471b8cb8fc9719" +dependencies = [ + "smallvec", + "spin", + "wasmi_arena", + "wasmi_core", + "wasmparser-nostd", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "stellar-strkey" +version = "0.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e3aa3ed00e70082cb43febc1c2afa5056b9bb3e348bbb43d0cd0aa88a611144" +dependencies = [ + "crate-git-revision", + "data-encoding", + "thiserror", +] + +[[package]] +name = "stellar-xdr" +version = "22.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ce69db907e64d1e70a3dce8d4824655d154749426a6132b25395c49136013e4" +dependencies = [ + "arbitrary", + "base64 0.13.1", + "crate-git-revision", + "escape-bytes", + "hex", + "serde", + "serde_with", + "stellar-strkey", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "time" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.98", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasmi_arena" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" + +[[package]] +name = "wasmi_core" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" +dependencies = [ + "downcast-rs", + "libm", + "num-traits", + "paste", +] + +[[package]] +name = "wasmparser" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" +dependencies = [ + "indexmap 2.7.1", + "semver", +] + +[[package]] +name = "wasmparser-nostd" +version = "0.100.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" +dependencies = [ + "indexmap-nostd", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa91407dacce3a68c56de03abe2760159582b846c6a4acd2f456618087f12713" +dependencies = [ + "zerocopy-derive 0.8.17", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06718a168365cad3d5ff0bb133aad346959a2074bd4a85c121255a11304a8626" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] diff --git a/apps/contract/Cargo.toml b/apps/contract/Cargo.toml index 2077fbec..b466fbde 100644 --- a/apps/contract/Cargo.toml +++ b/apps/contract/Cargo.toml @@ -1,11 +1,9 @@ [workspace] +members = ["contracts/auth-contracts/*", "contracts/nft"] resolver = "2" -members = [ - "contracts/*", -] [workspace.dependencies] -soroban-sdk = "20.3.2" +soroban-sdk = "22.0.6" [profile.release] opt-level = "z" @@ -19,4 +17,4 @@ lto = true [profile.release-with-logs] inherits = "release" -debug-assertions = true \ No newline at end of file +debug-assertions = true diff --git a/apps/contract/README.md b/apps/contract/README.md index f7cd1dba..b3ebd777 100644 --- a/apps/contract/README.md +++ b/apps/contract/README.md @@ -1,4 +1,52 @@ -# KindFi NFT Contract Deployment Guide +# KINDFI CONTRACTS + +## 1. Auth Contracts + +This repository contains Rust-based smart contracts for a decentralized authentication system. The contracts facilitate account creation, authentication, and account management using smart contract-based logic. + +### Overview +The system consists of three main contracts: + +#### 1. **Account Factory** + + - Responsible for deploying new account contracts in a deterministic manner. + - Uses a provided WebAssembly (WASM) hash to deploy contract instances. + - Ensures only authorized entities can initiate deployments. + - Emits events upon successful contract deployments. + +#### 2. **Auth Controller** + + - Handles multi-signature authentication and permission management. + - Manages a dynamic set of signers, enforcing authentication thresholds. + - Supports account and factory management through stored contexts. + - Implements flexible authorization rules for external contract calls. + - Allows addition and removal of signers, accounts, and factories. + - Ensures authentication by verifying **Ed25519** cryptographic signatures. + - Emits events for key contract updates such as signer additions and removals. + +#### 3. **Account Contract** + + - Represents an individual user account. + - Ensures authentication by verifying **Secp256r1** cryptographic signatures, in line with WebAuthn specifications.. + - Provides methods for account recovery, device management, and security updates. + - Uses a multi-device authentication model with public keys tied to registered devices. + - Supports recovery mechanisms through a designated recovery address. + - Implements a custom authentication interface to validate digital signatures and verify ownership. + - Emits events for device additions, removals, and security updates. + +### How It Works + +1. The **Account Factory** deploys a new **Account Contract** when a user registers. +2. The **Auth Controller** manages authentication flows, including key validation and session authorization. +3. The **Account Contract** holds user credentials, allowing secure and decentralized identity management. + +### Dependencies + +- Rust toolchain +- Cargo package manager +- Blockchain runtime (depends on deployment environment) + +## 2. KindFi NFT Contract ## Prerequisites diff --git a/apps/contract/contracts/auth-contracts/account-factory/Cargo.toml b/apps/contract/contracts/auth-contracts/account-factory/Cargo.toml new file mode 100644 index 00000000..c4b4b571 --- /dev/null +++ b/apps/contract/contracts/auth-contracts/account-factory/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "account_factory" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +soroban-sdk = { workspace = true } + +[dev-dependencies] +soroban-sdk = { workspace = true, features = ["testutils"] } +rand = { version = "0.8.3" } diff --git a/apps/contract/contracts/auth-contracts/account-factory/src/events.rs b/apps/contract/contracts/auth-contracts/account-factory/src/events.rs new file mode 100644 index 00000000..9b201508 --- /dev/null +++ b/apps/contract/contracts/auth-contracts/account-factory/src/events.rs @@ -0,0 +1,14 @@ +use soroban_sdk::{contracttype, symbol_short, Address, Symbol}; + +// Symbol for events related to account operations. +pub const ACCOUNT: Symbol = symbol_short!("ACCOUNT"); + +// Symbol for account deployment events. +pub const DEPLOY: Symbol = symbol_short!("DEPLOY"); + +// Event data emitted when a new account is deployed. +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AccountDeployEventData { + pub account: Address, +} diff --git a/apps/contract/contracts/auth-contracts/account-factory/src/lib.rs b/apps/contract/contracts/auth-contracts/account-factory/src/lib.rs new file mode 100644 index 00000000..7f1aef4d --- /dev/null +++ b/apps/contract/contracts/auth-contracts/account-factory/src/lib.rs @@ -0,0 +1,68 @@ +#![no_std] +use soroban_sdk::{ + contract, contracterror, contractimpl, symbol_short, vec, Address, BytesN, Env, Symbol, +}; + +mod events; + +use crate::events::{AccountDeployEventData, ACCOUNT, DEPLOY}; + +#[contract] +pub struct AccountFactory; + +#[contracterror] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum Error { + StorageKeyError = 1, +} + +const STORAGE_KEY_WASM_HASH: Symbol = symbol_short!("hash"); +const AUTH_CONTRACT: Symbol = symbol_short!("auth"); + +#[contractimpl] +impl AccountFactory { + pub fn __constructor(env: Env, auth_contract: Address, wasm_hash: BytesN<32>) { + env.storage() + .instance() + .set(&STORAGE_KEY_WASM_HASH, &wasm_hash); + + env.storage().instance().set(&AUTH_CONTRACT, &auth_contract); + } + + pub fn deploy( + env: Env, + salt: BytesN<32>, + id: BytesN<32>, + pk: BytesN<65>, + ) -> Result { + let auth_contract = env + .storage() + .instance() + .get::(&AUTH_CONTRACT) + .unwrap(); + + auth_contract.require_auth(); + + let wasm_hash = env + .storage() + .instance() + .get::>(&STORAGE_KEY_WASM_HASH) + .ok_or(Error::StorageKeyError)?; + + let address = env.deployer().with_current_contract(salt).deploy_v2( + wasm_hash, + vec![&env, id.to_val(), pk.to_val(), auth_contract.to_val()], + ); + + env.events().publish( + (ACCOUNT, DEPLOY), + AccountDeployEventData { + account: address.clone(), + }, + ); + + Ok(address) + } +} + +mod test; diff --git a/apps/contract/contracts/auth-contracts/account-factory/src/test.rs b/apps/contract/contracts/auth-contracts/account-factory/src/test.rs new file mode 100644 index 00000000..68cf7d6e --- /dev/null +++ b/apps/contract/contracts/auth-contracts/account-factory/src/test.rs @@ -0,0 +1,100 @@ +#![cfg(test)] +extern crate alloc; +extern crate std; + +use rand::rngs::OsRng; +use rand::RngCore; + +use alloc::vec; +use soroban_sdk::{ + symbol_short, + testutils::{Address as _, AuthorizedFunction, AuthorizedInvocation}, + Address, BytesN, Env, IntoVal, +}; + +use crate::{AccountFactory, AccountFactoryClient}; + +fn gen_random_bytes(env: &Env) -> BytesN { + let mut rng = OsRng; + let mut random_bytes = [0u8; N]; + rng.try_fill_bytes(&mut random_bytes) + .expect("unable to fill bytes"); + + BytesN::from_array(env, &random_bytes) +} + +// The contract that will be deployed by the deployer contract. +mod contract { + soroban_sdk::contractimport!( + file = "../../../target/wasm32-unknown-unknown/release/account_contract.wasm" + ); +} + +#[test] +fn test_deploy() { + let env = Env::default(); + let auth_contract = Address::generate(&env); + + env.cost_estimate().budget().reset_unlimited(); + env.mock_all_auths(); + + // Upload the Wasm to be deployed from the deployer contract. + // This can also be called from within a contract if needed. + let wasm_hash = env.deployer().upload_contract_wasm(contract::WASM); + + let factory_client = AccountFactoryClient::new( + &env, + &env.register(AccountFactory, (&auth_contract, &wasm_hash)), + ); + + let id = gen_random_bytes::<32>(&env); + let pk = gen_random_bytes::<65>(&env); + + // Deploy contract using deployer, and include an init function to call. + let salt = gen_random_bytes::<32>(&env); + + let contract_id = factory_client.deploy(&salt, &id, &pk); + + // An authorization from the auth_contract is required. + let expected_auth = AuthorizedInvocation { + // Top-level authorized function is `deploy` with all the arguments. + function: AuthorizedFunction::Contract(( + factory_client.address, + symbol_short!("deploy"), + (salt, id, pk).into_val(&env), + )), + sub_invocations: vec![], + }; + assert_eq!(env.auths(), vec![(auth_contract, expected_auth)]); + + // Invoke contract to check that it is initialized. + let client = contract::Client::new(&env, &contract_id); + let devices = client.get_devices(); + assert_eq!(devices.len(), 1); +} + +#[test] +#[should_panic] +fn test_duplicate_deployment() { + let env = Env::default(); + + env.cost_estimate().budget().reset_unlimited(); + env.mock_all_auths(); + + let auth_contract = Address::generate(&env); + let wasm_hash = env.deployer().upload_contract_wasm(contract::WASM); + + let factory_client = AccountFactoryClient::new( + &env, + &env.register(AccountFactory, (&auth_contract, &wasm_hash)), + ); + + let id = gen_random_bytes::<32>(&env); + let pk = gen_random_bytes::<65>(&env); + let salt = BytesN::from_array(&env, &[0; 32]); + + // First deployment + factory_client.deploy(&salt, &id, &pk); + // Should fail on duplicate deployment + factory_client.deploy(&salt, &id, &pk); +} diff --git a/apps/contract/contracts/auth-contracts/account/Cargo.toml b/apps/contract/contracts/auth-contracts/account/Cargo.toml new file mode 100644 index 00000000..0a679f11 --- /dev/null +++ b/apps/contract/contracts/auth-contracts/account/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "account_contract" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] +doctest = false + +[dependencies] +soroban-sdk = { workspace = true } +serde = { version = "1.0.217", default-features = false, features = ["derive"] } +serde-json-core = { version = "0.6.0", default-features = false } + +[dev-dependencies] +p256 = { version = "0.13.2" } +soroban-sdk = { workspace = true, features = ["testutils"] } +rand = { version = "0.9.0" } +hex = { version = "0.4.3" } diff --git a/apps/contract/contracts/auth-contracts/account/src/base64_url.rs b/apps/contract/contracts/auth-contracts/account/src/base64_url.rs new file mode 100644 index 00000000..3392901a --- /dev/null +++ b/apps/contract/contracts/auth-contracts/account/src/base64_url.rs @@ -0,0 +1,78 @@ +// Ported from https://github.com/golang/go/blob/26b5783b72376acd0386f78295e678b9a6bff30e/src/encoding/base64/base64.go#L53-L192 +// +// Modifications: +// * Removed logic supporting padding. +// * Hardcoded the Base64 URL alphabet. +// * Use a fixed length pre-allocated destination. +// * Ported to Rust. +// +// Original Copyright notice: +// +// Copyright (c) 2009 The Go Authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +const ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + +pub fn encode(dst: &mut [u8], src: &[u8]) { + let required_len = (src.len() + 2) / 3 * 4; + if dst.len() < required_len { + panic!("destination buffer too small"); + } + + let mut di: usize = 0; + let mut si: usize = 0; + let n = (src.len() / 3) * 3; + + while si < n { + let val = (src[si] as usize) << 16 | (src[si + 1] as usize) << 8 | (src[si + 2] as usize); + dst[di] = ALPHABET[val >> 18 & 0x3F]; + dst[di + 1] = ALPHABET[val >> 12 & 0x3F]; + dst[di + 2] = ALPHABET[val >> 6 & 0x3F]; + dst[di + 3] = ALPHABET[val & 0x3F]; + si += 3; + di += 4; + } + + let remain = src.len() - si; + + if remain == 0 { + return; + } + + let mut val = (src[si] as usize) << 16; + + if remain == 2 { + val |= (src[si + 1] as usize) << 8; + } + + dst[di] = ALPHABET[val >> 18 & 0x3F]; + dst[di + 1] = ALPHABET[val >> 12 & 0x3F]; + + if remain == 2 { + dst[di + 2] = ALPHABET[val >> 6 & 0x3F]; + } +} diff --git a/apps/contract/contracts/auth-contracts/account/src/errors.rs b/apps/contract/contracts/auth-contracts/account/src/errors.rs new file mode 100644 index 00000000..f29a9d21 --- /dev/null +++ b/apps/contract/contracts/auth-contracts/account/src/errors.rs @@ -0,0 +1,15 @@ +use soroban_sdk::contracterror; + +#[contracterror] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum Error { + ClientDataJsonChallengeIncorrect = 201, + Secp256r1VerifyFailed = 202, + JsonParseError = 203, + DeviceAlreadySet = 204, + DeviceNotFound = 205, + NotInitiated = 206, + RecoveryAddressSet = 207, + RecoveryAddressNotSet = 208, + DeviceCannotBeEmpty = 209, +} diff --git a/apps/contract/contracts/auth-contracts/account/src/events.rs b/apps/contract/contracts/auth-contracts/account/src/events.rs new file mode 100644 index 00000000..6b90545f --- /dev/null +++ b/apps/contract/contracts/auth-contracts/account/src/events.rs @@ -0,0 +1,49 @@ +use soroban_sdk::{contracttype, symbol_short, Address, BytesN, Symbol}; + +// Symbol representing account-related events. +pub const ACCOUNT: Symbol = symbol_short!("ACCOUNT"); + +// Symbol representing multisig security-related events. +pub const SECURITY: Symbol = symbol_short!("SECURITY"); + +// Symbol representing device related events. +pub const DEVICE: Symbol = symbol_short!("DEVICE"); + +// Symbol representing an added event. +pub const ADDED: Symbol = symbol_short!("ADDED"); + +// Symbol representing an added event. +pub const UPDATED: Symbol = symbol_short!("UPDATED"); + +// Symbol representing a removed event. +pub const REMOVED: Symbol = symbol_short!("REMOVED"); + +// Event data for when a device is added. +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DeviceAddedEventData { + pub device_id: BytesN<32>, + pub public_key: BytesN<65>, +} + +// Event data for when a device is removed. +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DeviceRemovedEventData { + pub device_id: BytesN<32>, +} + +// Event data for recovery addresss action. +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct RecoveryAddressEventData { + pub address: Address, +} + +// Event data for when a recovery is recovered. +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AccountRecoveredEventData { + pub device_id: BytesN<32>, + pub public_key: BytesN<65>, +} diff --git a/apps/contract/contracts/auth-contracts/account/src/lib.rs b/apps/contract/contracts/auth-contracts/account/src/lib.rs new file mode 100644 index 00000000..e1e46082 --- /dev/null +++ b/apps/contract/contracts/auth-contracts/account/src/lib.rs @@ -0,0 +1,246 @@ +#![no_std] +use soroban_sdk::{ + auth::{Context, CustomAccountInterface}, + contract, contractimpl, contracttype, + crypto::Hash, + panic_with_error, symbol_short, Address, Bytes, BytesN, Env, Symbol, Vec, +}; +mod base64_url; +mod errors; +mod events; + +use crate::events::{ + AccountRecoveredEventData, DeviceAddedEventData, DeviceRemovedEventData, + RecoveryAddressEventData, ACCOUNT, ADDED, DEVICE, REMOVED, SECURITY, UPDATED, +}; + +use crate::errors::Error; + +#[contract] +pub struct AccountContract; + +#[contracttype] +#[derive(Clone, Eq, PartialEq, Debug)] +pub struct Signature { + pub authenticator_data: Bytes, + pub client_data_json: Bytes, + pub device_id: BytesN<32>, + pub signature: BytesN<64>, +} + +#[contracttype] +#[derive(Clone, Eq, PartialEq, Debug)] +pub struct DevicePublicKey { + pub device_id: BytesN<32>, + pub public_key: BytesN<65>, +} + +#[derive(serde::Deserialize)] +struct ClientDataJson<'a> { + challenge: &'a str, +} + +const STORAGE_KEY_DEVICES: Symbol = symbol_short!("devices"); +const RECOVERY_ADDRESS: Symbol = symbol_short!("recovery"); +const AUTH_CONTRACT: Symbol = symbol_short!("auth"); + +#[contractimpl] +impl AccountContract { + pub fn __constructor( + env: Env, + device_id: BytesN<32>, + public_key: BytesN<65>, + auth_contract: Address, + ) { + let mut devices = Vec::new(&env); + devices.push_back(DevicePublicKey { + device_id, + public_key, + }); + env.storage().instance().set(&STORAGE_KEY_DEVICES, &devices); + env.storage().instance().set(&AUTH_CONTRACT, &auth_contract); + } + + pub fn add_device(env: Env, device_id: BytesN<32>, public_key: BytesN<65>) { + env.current_contract_address().require_auth(); + let mut devices: Vec = env + .storage() + .instance() + .get(&STORAGE_KEY_DEVICES) + .unwrap_or(Vec::new(&env)); + + if devices.iter().any(|device| device.device_id == device_id) { + panic_with_error!(&env, Error::DeviceAlreadySet); + } + + devices.push_back(DevicePublicKey { + device_id: device_id.clone(), + public_key: public_key.clone(), + }); + + env.storage().instance().set(&STORAGE_KEY_DEVICES, &devices); + + env.events().publish( + (DEVICE, ADDED), + DeviceAddedEventData { + device_id, + public_key, + }, + ); + } + + pub fn remove_device(env: Env, device_id: BytesN<32>) { + env.current_contract_address().require_auth(); + + let mut devices: Vec = + env.storage().instance().get(&STORAGE_KEY_DEVICES).unwrap(); + + if let Some(device_) = devices.iter().find(|device| device.device_id == device_id) { + if let Some(index) = devices.first_index_of(&device_) { + devices.remove(index); + } + } else { + panic_with_error!(&env, Error::DeviceNotFound); + } + + if devices.len() < 1 { + panic_with_error!(&env, Error::DeviceCannotBeEmpty); + } + + env.storage().instance().set(&STORAGE_KEY_DEVICES, &devices); + + env.events() + .publish((DEVICE, REMOVED), DeviceRemovedEventData { device_id }); + } + + pub fn get_devices(env: Env) -> Vec { + env.storage() + .instance() + .get(&STORAGE_KEY_DEVICES) + .unwrap_or_else(|| Vec::new(&env)) + } + + pub fn get_auth(env: Env) -> Address { + env.storage().instance().get(&AUTH_CONTRACT).unwrap() + } + + pub fn add_recovery_address(env: Env, address: Address) { + env.current_contract_address().require_auth(); + + if env.storage().instance().has(&RECOVERY_ADDRESS) { + panic_with_error!(&env, Error::RecoveryAddressSet); + } + + env.storage().instance().set(&RECOVERY_ADDRESS, &address); + + env.events().publish( + (ACCOUNT, SECURITY, ADDED), + RecoveryAddressEventData { address }, + ); + } + + pub fn update_recovery_address(env: Env, address: Address) { + env.current_contract_address().require_auth(); + + // add multisig authentication from parent auth contract for recovery update + let auth_contract = env + .storage() + .instance() + .get::(&AUTH_CONTRACT) + .unwrap(); + + auth_contract.require_auth(); + + env.storage().instance().set(&RECOVERY_ADDRESS, &address); + + env.events().publish( + (ACCOUNT, SECURITY, UPDATED), + RecoveryAddressEventData { address }, + ); + } + + pub fn recover_account(env: Env, new_device_id: BytesN<32>, new_public_key: BytesN<65>) { + // add multisig authentication from parent auth contract for recovery update + let auth_contract = env + .storage() + .instance() + .get::(&AUTH_CONTRACT) + .unwrap(); + + auth_contract.require_auth(); + + let recovery_address = env + .storage() + .instance() + .get::(&RECOVERY_ADDRESS) + .unwrap(); + + recovery_address.require_auth(); + + let mut devices = Vec::new(&env); + devices.push_back(DevicePublicKey { + device_id: new_device_id.clone(), + public_key: new_public_key.clone(), + }); + + env.storage().instance().set(&STORAGE_KEY_DEVICES, &devices); + + env.events().publish( + (ACCOUNT, SECURITY), + AccountRecoveredEventData { + device_id: new_device_id, + public_key: new_public_key, + }, + ); + } +} + +#[contractimpl] +impl CustomAccountInterface for AccountContract { + type Error = Error; + type Signature = Signature; + + fn __check_auth( + env: Env, + signature_payload: Hash<32>, + signature: Signature, + _auth_contexts: Vec, + ) -> Result<(), Error> { + let devices: Vec = env + .storage() + .instance() + .get(&STORAGE_KEY_DEVICES) + .ok_or(Error::NotInitiated)?; + + if let Some(device_) = devices + .iter() + .find(|device| device.device_id == signature.device_id) + { + let mut payload = Bytes::new(&env); + payload.append(&signature.authenticator_data); + payload.extend_from_array(&env.crypto().sha256(&signature.client_data_json).to_array()); + let payload = env.crypto().sha256(&payload); + + env.crypto() + .secp256r1_verify(&device_.public_key, &payload, &signature.signature); + } else { + return Err(Error::DeviceNotFound); + } + + let client_data_json = signature.client_data_json.to_buffer::<1024>(); + let client_data_json = client_data_json.as_slice(); + let (client_data, _): (ClientDataJson, _) = + serde_json_core::de::from_slice(client_data_json).map_err(|_| Error::JsonParseError)?; + + let mut expected_challenge = *b"___________________________________________"; + base64_url::encode(&mut expected_challenge, &signature_payload.to_array()); + + if client_data.challenge.as_bytes() != expected_challenge { + return Err(Error::ClientDataJsonChallengeIncorrect); + } + + Ok(()) + } +} + +mod test; diff --git a/apps/contract/contracts/auth-contracts/account/src/test.rs b/apps/contract/contracts/auth-contracts/account/src/test.rs new file mode 100644 index 00000000..b15f3802 --- /dev/null +++ b/apps/contract/contracts/auth-contracts/account/src/test.rs @@ -0,0 +1,198 @@ +#![cfg(test)] + +extern crate std; + +use p256::ecdsa::{signature::Signer, Signature as P256Signature, SigningKey, VerifyingKey}; +use rand::rngs::OsRng; +use rand::RngCore; +use soroban_sdk::{log, symbol_short}; + +// use soroban_sdk::auth::{Context, ContractContext}; +use soroban_sdk::testutils::{Address as _, BytesN as _, Events as _}; +use soroban_sdk::{Address, Bytes, BytesN, Env, IntoVal, Val}; + +// use crate::errors::Error; +use crate::{AccountContract, AccountContractClient, Signature}; + +fn sign(env: &Env, device_id: BytesN<32>, signing_key: SigningKey) -> Val { + let authenticator_data = Bytes::from_slice(&env, b"auth_data"); + let client_json_data = Bytes::from_slice(&env, b"client_data"); + + let hashed_client_data = env.crypto().sha256(&client_json_data); + + let mut payload = Bytes::new(&env); + + payload.append(&authenticator_data); + payload.extend_from_array(&hashed_client_data.to_array()); + + let hashed_payload = env.crypto().sha256(&payload); + + let signature: P256Signature = signing_key.sign(&hashed_payload.to_array()); + + let mut signature_bytes = [0u8; 64]; + signature_bytes.copy_from_slice(&signature.to_bytes()); + + Signature { + authenticator_data: authenticator_data.clone(), + client_data_json: client_json_data.clone(), + device_id: device_id.clone(), + signature: BytesN::from_array(&env, &signature_bytes), + } + .into_val(env) +} + +fn generate_keypair(env: &Env) -> (SigningKey, VerifyingKey, BytesN<65>) { + let signing_key = SigningKey::random(&mut OsRng); + let verifying_key = VerifyingKey::from(&signing_key); + let vk_bytes = verifying_key.to_sec1_bytes(); + + let mut pubkey_bytes = [0u8; 65]; + pubkey_bytes.copy_from_slice(&vk_bytes); + + let public_key = BytesN::from_array(&env, &pubkey_bytes); + + (signing_key, verifying_key, public_key) +} + +fn generate_device_id(env: &Env) -> BytesN<32> { + let mut rng = OsRng; + let mut random_bytes = [0u8; 32]; + rng.try_fill_bytes(&mut random_bytes) + .expect("unable to fill bytes"); + + BytesN::from_array(env, &random_bytes) +} + +struct Account { + env: Env, + account_address: Address, + client: AccountContractClient<'static>, + init_device_id: BytesN<32>, + init_signing_key: SigningKey, + init_verifying_key: VerifyingKey, + init_public_key: BytesN<65>, + auth_contract: Address, +} + +impl Account { + fn new() -> Self { + let env = Env::default(); + let auth_contract = Address::generate(&env); + let init_device_id = generate_device_id(&env); + + env.cost_estimate().budget().reset_unlimited(); + env.mock_all_auths(); + + let (sk, vk, pk) = generate_keypair(&env); + log!(&env, "a log entry", pk, symbol_short!("another")); + // let account_address = env.register(AccountContract, (&init_device_id, &pk, &auth_contract)); + let account_address = env.register(AccountContract, (&init_device_id, &pk, &auth_contract)); + + let client = AccountContractClient::new(&env, &account_address); + + Account { + env, + account_address, + client, + init_device_id, + init_signing_key: sk, + init_verifying_key: vk, + init_public_key: pk, + auth_contract, + } + } +} + +#[test] +fn test_constructor() { + let Account { client, .. } = Account::new(); + assert_eq!(client.get_devices().len(), 1); +} + +#[test] +fn test_add_device() { + let Account { env, client, .. } = Account::new(); + + assert_eq!(client.get_devices().len(), 1); + + let device_id: BytesN<32> = generate_device_id(&env); // Fake device ID + + let (_, _, public_key) = generate_keypair(&env); + + client.add_device(&device_id, &public_key); + + assert_eq!(client.get_devices().len(), 2); +} + +#[test] +#[should_panic(expected = "#204")] +fn test_add_device_fails_if_add_same_device() { + let Account { + client, + init_public_key, + init_device_id, + .. + } = Account::new(); + + assert_eq!(client.get_devices().len(), 1); + + client.add_device(&init_device_id, &init_public_key); +} + +#[test] +fn test_remove_device() { + let Account { + env, + client, + init_device_id, + .. + } = Account::new(); + + assert_eq!(client.get_devices().len(), 1); + + let device_id = generate_device_id(&env); + + let (_, _, public_key) = generate_keypair(&env); + + client.add_device(&device_id, &public_key); + + assert_eq!(client.get_devices().len(), 2); + + client.remove_device(&init_device_id); + + assert_eq!(client.get_devices().len(), 1); +} + +#[test] +#[should_panic(expected = "#209")] +fn test_remove_device_fails_if_only_one_device() { + let Account { + client, + init_device_id, + .. + } = Account::new(); + assert_eq!(client.get_devices().len(), 1); + client.remove_device(&init_device_id); +} + +#[test] +#[should_panic(expected = "#205")] +fn test_remove_device_fails_for_unknown_device() { + let Account { env, client, .. } = Account::new(); + assert_eq!(client.get_devices().len(), 1); + let device_id = generate_device_id(&env); + client.remove_device(&device_id); +} + +#[test] +fn test_add_recovery_address() { + let Account { env, client, .. } = Account::new(); + + assert_eq!(client.get_devices().len(), 1); + + let recovery = Address::generate(&env); + + client.add_recovery_address(&recovery); + + assert_eq!(env.events().all().len(), 1); +} diff --git a/apps/contract/contracts/auth-contracts/auth-controller/Cargo.toml b/apps/contract/contracts/auth-contracts/auth-controller/Cargo.toml new file mode 100644 index 00000000..00e650eb --- /dev/null +++ b/apps/contract/contracts/auth-contracts/auth-controller/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "auth_controller" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +soroban-sdk = { workspace = true } + +[dev-dependencies] +soroban-sdk = { workspace = true, features = ["testutils"] } +ed25519-dalek = { version = "1.0.1" } +rand = { version = "0.7.3" } +hex = { version = "0.4.3" } diff --git a/apps/contract/contracts/auth-contracts/auth-controller/src/errors.rs b/apps/contract/contracts/auth-contracts/auth-controller/src/errors.rs new file mode 100644 index 00000000..3b0334f2 --- /dev/null +++ b/apps/contract/contracts/auth-contracts/auth-controller/src/errors.rs @@ -0,0 +1,21 @@ +use soroban_sdk::contracterror; + +#[contracterror] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[repr(u32)] +pub enum Error { + SignerLimitExceeded = 100, + UnknownSigner = 101, + DefaultThresholdNotMet = 102, + SignerDoesNotExist = 103, + AlreadyInitialized = 104, + InvalidThreshold = 105, + SignerAlreadyAdded = 106, + AccountThresholdNotMet = 107, + DuplicateSignature = 108, + AccountExists = 109, + AccountDoesNotExist = 1010, + FactoryExists = 1011, + FactoryDoesNotExist = 1012, + NotAllowedContract = 1013 +} \ No newline at end of file diff --git a/apps/contract/contracts/auth-contracts/auth-controller/src/events.rs b/apps/contract/contracts/auth-contracts/auth-controller/src/events.rs new file mode 100644 index 00000000..88035d37 --- /dev/null +++ b/apps/contract/contracts/auth-contracts/auth-controller/src/events.rs @@ -0,0 +1,85 @@ +use soroban_sdk::{contracttype, symbol_short, Address, BytesN, Symbol, Vec}; + +// Symbol representing the core contract init. +pub const INIT: Symbol = symbol_short!("init"); + +// Symbol representing account-related events. +pub const ACCOUNT: Symbol = symbol_short!("ACCOUNT"); + +// Symbol representing factory-related events. +pub const FACTORY: Symbol = symbol_short!("FACTORY"); + +// Symbol representing signer-related events. +pub const SIGNER: Symbol = symbol_short!("SIGNER"); + +// Symbol representing multisig security-related events. +pub const SECURITY: Symbol = symbol_short!("SECURITY"); + +// Symbol representing an add event. +pub const ADDED: Symbol = symbol_short!("Added"); + +// Symbol representing a removal event. +pub const REMOVED: Symbol = symbol_short!("Removed"); + +// Symbol representing a changed event. +pub const UPDATE: Symbol = symbol_short!("Update"); + +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct InitEventData { + pub threshold: u32, + pub signers: Vec>, +} + +// Event data for when a signer is added. +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct SignerAddedEventData { + pub signer: BytesN<32>, +} + +// Event data for when a signer is removed. +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct SignerRemovedEventData { + pub signer: BytesN<32>, +} + +// Event data for when a factory is added along with its associated context. +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct FactoryAddedEventData { + pub factory: Address, + pub context: Vec
, +} + +// Event data for when a factory is added along with its associated context. +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct FactoryRemovedEventData { + pub factory: Address, + pub context: Vec
, +} + +// Event data for when an account is added along with its associated context. +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AccountAddedEventData { + pub account: Address, + pub context: Vec
, +} + +// Event data for when an account is removed along with its associated context. +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AccountRemovedEventData { + pub account: Address, + pub context: Vec
, +} + +// Event data for when the default threshold is changed. +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DefaultThresholdChangedEventData { + pub threshold: u32, +} diff --git a/apps/contract/contracts/auth-contracts/auth-controller/src/lib.rs b/apps/contract/contracts/auth-contracts/auth-controller/src/lib.rs new file mode 100644 index 00000000..d6e3e1f4 --- /dev/null +++ b/apps/contract/contracts/auth-contracts/auth-controller/src/lib.rs @@ -0,0 +1,360 @@ +#![no_std] +use core::cmp::min; +use soroban_sdk::auth::Context; +use soroban_sdk::{ + contract, contractimpl, contracttype, panic_with_error, Address, BytesN, Env, IntoVal, Val, Vec, +}; + +mod errors; +mod events; + +use crate::events::{ + AccountAddedEventData, AccountRemovedEventData, DefaultThresholdChangedEventData, + FactoryAddedEventData, FactoryRemovedEventData, InitEventData, SignerAddedEventData, + SignerRemovedEventData, ACCOUNT, ADDED, FACTORY, INIT, REMOVED, SECURITY, SIGNER, UPDATE, +}; + +use crate::errors::Error; + +/// Declares the SignedMessage structure, containing the public key and signature. +#[contracttype] +#[derive(Clone)] +pub struct SignedMessage { + pub public_key: BytesN<32>, + pub signature: BytesN<64>, +} + +/// Enum to represent different keys used in storage for the contract. +#[contracttype] +#[derive(Clone)] +enum DataKey { + DefaultThreshold, + Signers, + Account(Address), + Factory(Address), +} + +pub const THRESHOLD_LIMIT: u32 = 5; + +#[contract] +pub struct AuthController; + +#[contractimpl] +impl AuthController { + pub fn init(env: Env, signers: Vec>, default_threshold: u32) { + if env + .storage() + .instance() + .has::(&DataKey::Signers.into_val(&env)) + { + panic_with_error!(&env, Error::AlreadyInitialized); + } + + if signers.len() > THRESHOLD_LIMIT { + panic_with_error!(&env, Error::SignerLimitExceeded); + } + + let valid_thresholds = 0..signers.len() + 1; + if !valid_thresholds.contains(&default_threshold) { + panic_with_error!(&env, Error::InvalidThreshold); + } + + env.storage() + .instance() + .set::>>(&DataKey::Signers.into_val(&env), &signers); + + env.storage().instance().set::( + &DataKey::DefaultThreshold.into_val(&env), + &default_threshold, + ); + + env.events().publish( + (SECURITY, INIT), + InitEventData { + threshold: default_threshold, + signers, + }, + ); + } + + pub fn add_signer(env: Env, signer: BytesN<32>) { + env.current_contract_address().require_auth(); + let mut signers = env + .storage() + .instance() + .get::>>(&DataKey::Signers.into_val(&env)) + .unwrap(); + + if signers.contains(&signer) { + panic_with_error!(&env, Error::SignerAlreadyAdded); + } + + if signers.len() == THRESHOLD_LIMIT { + panic_with_error!(&env, Error::SignerLimitExceeded); + } + + signers.push_back(signer.clone()); + env.storage() + .instance() + .set::>>(&DataKey::Signers.into_val(&env), &signers); + + env.events() + .publish((SIGNER, ADDED), SignerAddedEventData { signer }); + } + + pub fn remove_signer(env: Env, signer: BytesN<32>) { + env.current_contract_address().require_auth(); + + let mut signers = env + .storage() + .instance() + .get::>>(&DataKey::Signers.into_val(&env)) + .unwrap(); + + let threshold = env + .storage() + .instance() + .get::(&DataKey::DefaultThreshold.into_val(&env)) + .unwrap_or(0); + + if signers.len() == threshold { + panic_with_error!(&env, Error::InvalidThreshold); + } + + match signers.first_index_of(&signer) { + None => panic_with_error!(&env, Error::SignerDoesNotExist), + Some(index) => signers.remove(index), + }; + + env.storage() + .instance() + .set::>>(&DataKey::Signers.into_val(&env), &signers); + + env.events() + .publish((SIGNER, REMOVED), SignerRemovedEventData { signer }); + } + + pub fn get_signers(env: Env) -> Vec> { + env.storage() + .instance() + .get::>>(&DataKey::Signers.into_val(&env)) + .unwrap() + } + + pub fn set_default_threshold(env: Env, threshold: u32) { + env.current_contract_address().require_auth(); + + let signers = env + .storage() + .instance() + .get::>>(&DataKey::Signers.into_val(&env)) + .unwrap(); + let valid_thresholds = 0..min(signers.len() + 1, THRESHOLD_LIMIT + 1); + + if !valid_thresholds.contains(&threshold) { + panic_with_error!(&env, Error::InvalidThreshold); + } + + env.storage() + .instance() + .set::(&DataKey::DefaultThreshold.into_val(&env), &threshold); + + env.events().publish( + (SECURITY, UPDATE), + DefaultThresholdChangedEventData { threshold }, + ); + } + + pub fn get_default_threshold(env: Env) -> u32 { + env.storage() + .instance() + .get::(&DataKey::DefaultThreshold.into_val(&env)) + .unwrap_or(0) + } + + pub fn add_factory(env: Env, factory: Address, context: Vec
) { + env.current_contract_address().require_auth(); + for ctx in context.iter() { + if env + .storage() + .instance() + .has::(&DataKey::Factory(ctx.clone()).into_val(&env)) + { + panic_with_error!(&env, Error::FactoryExists); + } + env.storage() + .instance() + .set::(&DataKey::Factory(ctx).into_val(&env), &factory); + } + + env.events() + .publish((FACTORY, ADDED), FactoryAddedEventData { factory, context }); + } + + pub fn remove_factory(env: Env, factory: Address, context: Vec
) { + env.current_contract_address().require_auth(); + for ctx in context.iter() { + if !env + .storage() + .instance() + .has::(&DataKey::Factory(ctx.clone()).into_val(&env)) + { + panic_with_error!(&env, Error::FactoryDoesNotExist); + } + env.storage() + .instance() + .remove::(&DataKey::Factory(ctx).into_val(&env)); + } + + env.events().publish( + (FACTORY, REMOVED), + FactoryRemovedEventData { factory, context }, + ); + } + + pub fn add_account(env: Env, account: Address, context: Vec
) { + env.current_contract_address().require_auth(); + for ctx in context.iter() { + if env + .storage() + .instance() + .has::(&DataKey::Account(ctx.clone()).into_val(&env)) + { + panic_with_error!(&env, Error::AccountExists); + } + env.storage() + .instance() + .set::(&DataKey::Account(ctx).into_val(&env), &account); + } + + env.events() + .publish((ACCOUNT, ADDED), AccountAddedEventData { account, context }); + } + + pub fn remove_account(env: Env, account: Address, context: Vec
) { + env.current_contract_address().require_auth(); + for ctx in context.iter() { + if !env + .storage() + .instance() + .has::(&DataKey::Account(ctx.clone()).into_val(&env)) + { + panic_with_error!(&env, Error::AccountDoesNotExist); + } + env.storage() + .instance() + .remove::(&DataKey::Account(ctx).into_val(&env)); + } + + env.events().publish( + (ACCOUNT, REMOVED), + AccountRemovedEventData { account, context }, + ); + } + + pub fn get_accounts(env: Env, context: Vec
) -> Vec
{ + let mut accounts = Vec::new(&env); + for ctx in context.iter() { + if env + .storage() + .instance() + .has::(&DataKey::Account(ctx.clone()).into_val(&env)) + { + accounts.push_back( + env.storage() + .instance() + .get::(&DataKey::Account(ctx.clone()).into_val(&env)) + .unwrap(), + ); + } + } + + return accounts; + } + + #[allow(non_snake_case)] + fn __check_auth( + env: Env, + signature_payload: BytesN<32>, + signed_messages: Vec, + auth_context: Vec, + ) -> Result<(), Error> { + let signers = env + .storage() + .instance() + .get::>>(&DataKey::Signers.into_val(&env)) + .unwrap(); + + let mut prevSig: Option> = None; + + for i in 0..signed_messages.len() { + let signature = signed_messages.get_unchecked(i); + + if signers.first_index_of(&signature.public_key).is_none() { + panic_with_error!(&env, Error::UnknownSigner); + } + + // Ensure the signature is unique and sequential + if let Some(prev) = &prevSig { + if prev == &signature.signature { + panic_with_error!(&env, Error::DuplicateSignature); + } + } + + env.crypto().ed25519_verify( + &signature.public_key, + &signature_payload.clone().into(), + &signature.signature, + ); + + prevSig = Some(signature.signature.clone()); + } + + let num_signers = signed_messages.len(); + + let default_threshold = env + .storage() + .instance() + .get::(&DataKey::DefaultThreshold.into_val(&env)) + .unwrap(); + + if default_threshold >= num_signers { + panic_with_error!(&env, Error::DefaultThresholdNotMet); + } + + for ctx in auth_context.iter() { + match ctx.clone() { + Context::Contract(contract_ctx) => { + let is_account = env + .storage() + .instance() + .get::( + &DataKey::Account(contract_ctx.clone().contract).into_val(&env), + ) + .is_some(); + + let is_factory = env + .storage() + .instance() + .get::( + &DataKey::Factory(contract_ctx.clone().contract).into_val(&env), + ) + .is_some(); + + let is_current = + contract_ctx.clone().contract == env.current_contract_address(); + + match (is_account, is_factory, is_current) { + (false, false, false) => panic_with_error!(&env, Error::NotAllowedContract), + _ => (), + } + } + Context::CreateContractHostFn(_) => (), + Context::CreateContractWithCtorHostFn(_) => (), + } + } + Ok(()) + } +} + +mod test; diff --git a/apps/contract/contracts/auth-contracts/auth-controller/src/test.rs b/apps/contract/contracts/auth-contracts/auth-controller/src/test.rs new file mode 100644 index 00000000..20b24c60 --- /dev/null +++ b/apps/contract/contracts/auth-contracts/auth-controller/src/test.rs @@ -0,0 +1,349 @@ +#![cfg(test)] +extern crate std; + +use ed25519_dalek::{Signature, Signer, SigningKey, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH}; +use rand::rngs::OsRng; +use rand::RngCore; +use soroban_sdk::auth::{Context, ContractContext}; +use soroban_sdk::testutils::{Address as _, Logs}; +use soroban_sdk::{log, vec, Address, BytesN, Env, IntoVal, Symbol, Val, Vec}; + +use crate::errors::Error; +use crate::{AuthController, AuthControllerClient, SignedMessage}; + +fn generate_keypair() -> SigningKey { + let mut csprng = OsRng; + SigningKey::generate(&mut csprng) +} + +fn gen_random_bytes(env: &Env) -> BytesN { + let mut rng = OsRng; + let mut random_bytes = [0u8; N]; + rng.try_fill_bytes(&mut random_bytes) + .expect("unable to fill bytes"); + + BytesN::from_array(env, &random_bytes) +} + +fn signer_public_key(e: &Env, signing_key: &SigningKey) -> BytesN<32> { + let verifying_key_bytes: [u8; PUBLIC_KEY_LENGTH] = signing_key.verifying_key().to_bytes(); + BytesN::from_array(e, &verifying_key_bytes) +} + +fn get_signature_bytes(e: &Env, signature: &Signature) -> BytesN<64> { + let signature_bytes: [u8; SIGNATURE_LENGTH] = signature.to_bytes(); + BytesN::from_array(e, &signature_bytes) +} + +fn sign(e: &Env, signing_key: &SigningKey, payload: &BytesN<32>) -> Val { + SignedMessage { + public_key: signer_public_key(e, signing_key), + signature: get_signature_bytes(e, &signing_key.sign(payload.to_array().as_slice())), + } + .into_val(e) +} + +struct Controller { + controller_address: Address, + env: Env, + client: AuthControllerClient<'static>, + threshold: u32, + signers: [SigningKey; 2], + signers_bytes: Vec>, +} + +impl Controller { + fn new(threshold: u32) -> Self { + let env = Env::default(); + env.cost_estimate().budget().reset_unlimited(); + env.mock_all_auths(); + + let controller_address = env.register(AuthController, ()); + let client = AuthControllerClient::new(&env, &controller_address); + let mut signers = [generate_keypair(), generate_keypair()]; + + if signers[0].verifying_key().as_bytes() > signers[1].verifying_key().as_bytes() { + signers.swap(0, 1); + } + + let signers_bytes = vec![ + &env, + signer_public_key(&env, &signers[0]), + signer_public_key(&env, &signers[1]), + ]; + + client.init(&signers_bytes, &threshold); + + Controller { + env, + client, + threshold, + controller_address, + signers_bytes, + signers, + } + } +} + +#[test] +#[should_panic(expected = "#104")] +fn init_only_once() { + let Controller { + client, + signers_bytes, + threshold, + .. + } = Controller::new(2); + client.init(&signers_bytes, &threshold); +} + +#[test] +fn test_default_threshold_not_met() { + let controller = Controller::new(2); + let env = controller.env; + let payload = gen_random_bytes::<32>(&env); + + let new_signer = generate_keypair(); + let new_signer_pk = signer_public_key(&env, &new_signer); + + let invocation = env.try_invoke_contract_check_auth::( + &controller.controller_address, + &payload, + vec![&env, sign(&env, &controller.signers[0], &payload)].into(), + &vec![ + &env, + Context::Contract(ContractContext { + contract: controller.controller_address.clone(), + fn_name: Symbol::new(&env, "add_signer"), + args: (new_signer_pk,).into_val(&env), + }), + ], + ); + + match invocation { + Ok(()) => log!(&env, "Log message: Success"), + Err(inner_error) => match inner_error { + Ok(e) => log!(&env, "Inner error: {:?}", e), // E variant + Err(_) => log!(&env, "Invocation failed: {:?}", "invoke err"), // InvokeError variant + }, + } + + env.logs().print(); + + // assert!(invocation.is_ok()); +} + +#[test] +fn test_default_threshold_met_but_wrong_signer() { + let controller = Controller::new(2); + let env = controller.env; + let payload = gen_random_bytes::<32>(&env); + + let wrong_signer = generate_keypair(); + let new_signer = generate_keypair(); + + let invocation = env.try_invoke_contract_check_auth::( + &controller.controller_address, + &payload, + vec![ + &env, + sign(&env, &controller.signers[0], &payload), + sign(&env, &wrong_signer, &payload), + ] + .into(), + &vec![ + &env, + Context::Contract(ContractContext { + contract: controller.controller_address.clone(), + fn_name: Symbol::new(&env, "add_signer"), + args: (signer_public_key(&env, &new_signer),).into_val(&env), + }), + ], + ); + + match invocation { + Ok(()) => log!(&env, "Log message: Success"), + Err(inner_error) => match inner_error { + Ok(e) => log!(&env, "Inner error: {:?}", e), // E variant + Err(_) => log!(&env, "Invocation failed: {:?}", "invoke err"), // InvokeError variant + }, + } + + env.logs().print(); + + // assert!(invocation.is_ok()); +} + +#[test] +fn test_default_threshold_met() { + let controller = Controller::new(2); + let env = controller.env; + + let payload = gen_random_bytes::<32>(&env); + + let new_signer = generate_keypair(); + + let invocation = env.try_invoke_contract_check_auth::( + &controller.controller_address, + &payload, + vec![ + &env, + sign(&env, &controller.signers[0], &payload), + sign(&env, &controller.signers[1], &payload), + ] + .into(), + &vec![ + &env, + Context::Contract(ContractContext { + contract: controller.controller_address.clone(), + fn_name: Symbol::new(&env, "add_signer"), + args: (signer_public_key(&env, &new_signer),).into_val(&env), + }), + ], + ); + + match invocation { + Ok(()) => log!(&env, "Log message: Success"), + Err(inner_error) => match inner_error { + Ok(e) => log!(&env, "Inner error: {:?}", e), // E variant + Err(_) => log!(&env, "Invocation failed: {:?}", "invoke err"), // InvokeError variant + }, + } + + env.logs().print(); + + // assert!(invocation.is_ok()); +} + +#[test] +fn test_default_threshold_set_on_init() { + let Controller { + client, threshold, .. + } = Controller::new(2); + assert_eq!(client.get_default_threshold(), threshold); +} + +#[test] +fn test_add_signer() { + let Controller { client, env, .. } = Controller::new(2); + assert_eq!(client.get_signers().len(), 2); + let new_signer = generate_keypair(); + let signer_pubkey = signer_public_key(&env, &new_signer); + client.add_signer(&signer_pubkey); + assert_eq!(client.get_signers().len(), 3); +} + +#[test] +fn test_remove_signer() { + let Controller { + client, + env, + signers, + .. + } = Controller::new(1); + assert_eq!(client.get_signers().len(), 2); + let signer = signer_public_key(&env, &signers[0]); + client.remove_signer(&signer); + assert_eq!(client.get_signers().len(), 1); +} + +#[test] +#[should_panic(expected = "#103")] +fn test_remove_signer_fails_if_not_exists() { + let Controller { client, env, .. } = Controller::new(1); + let signer = generate_keypair(); + let signer_pubkey = signer_public_key(&env, &signer); + client.remove_signer(&signer_pubkey); +} + +#[test] +fn test_add_account() { + let Controller { client, env, .. } = Controller::new(2); + let account = Address::generate(&env); + let context = vec![&env, Address::generate(&env)]; + assert_eq!(client.get_accounts(&context).len(), 0); + client.add_account(&account, &context); + assert_eq!(client.get_accounts(&context).len(), 1); +} + +#[test] +#[should_panic(expected = "#109")] +fn test_add_account_fails_if_already_exists() { + let Controller { client, env, .. } = Controller::new(2); + let account = Address::generate(&env); + let other = Address::generate(&env); + let context = vec![&env, Address::generate(&env)]; + client.add_account(&account, &context); + client.add_account(&other, &context); +} + +#[test] +fn test_remove_account() { + let Controller { client, env, .. } = Controller::new(2); + let account = Address::generate(&env); + let context = vec![&env, Address::generate(&env)]; + assert_eq!(client.get_accounts(&context).len(), 0); + client.add_account(&account, &context); + assert_eq!(client.get_accounts(&context).len(), 1); + client.remove_account(&account, &context); + assert_eq!(client.get_accounts(&context).len(), 0); +} + +#[test] +#[should_panic(expected = "#1010")] +fn test_remove_account_fails_if_not_exists() { + let Controller { client, env, .. } = Controller::new(2); + let account = Address::generate(&env); + let context = vec![&env, Address::generate(&env)]; + client.remove_account(&account, &context); +} + +#[test] +#[should_panic(expected = "#105")] +fn test_invalid_threshold_on_init_fails() { + Controller::new(10); +} + +#[test] +#[should_panic(expected = "#105")] +fn test_invalid_threshold_on_update_fails() { + let Controller { client, .. } = Controller::new(2); + client.set_default_threshold(&10); +} + +#[test] +#[should_panic(expected = "#100")] +fn test_exceeding_signer_limit_on_update_fails() { + let Controller { client, env, .. } = Controller::new(2); + for _ in 0..15 { + let signer = generate_keypair(); + let signer_pubkey = signer_public_key(&env, &signer); + client.add_signer(&signer_pubkey); + } +} + +#[test] +#[should_panic(expected = "#106")] +fn test_signers_cannot_be_added_multiple_times() { + let Controller { + client, + env, + signers, + .. + } = Controller::new(2); + let signer_pubkey = signer_public_key(&env, &signers[0]); + client.add_signer(&signer_pubkey); +} + +#[test] +#[should_panic(expected = "#105")] +fn test_signers_cannot_be_removed_if_threshold_not_reduced() { + let Controller { + client, + env, + signers, + .. + } = Controller::new(2); + let signer_pubkey = signer_public_key(&env, &signers[0]); + client.remove_signer(&signer_pubkey); +} diff --git a/apps/contract/contracts/nft/Cargo.toml b/apps/contract/contracts/nft/Cargo.toml index 1f6e8f41..1325cdae 100644 --- a/apps/contract/contracts/nft/Cargo.toml +++ b/apps/contract/contracts/nft/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "kindfi-nft" version = "0.1.0" -edition = "2025" +edition = "2021" [lib] crate-type = ["cdylib"] diff --git a/apps/contract/contracts/nft/nft.rs b/apps/contract/contracts/nft/nft.rs deleted file mode 100644 index 6de0b1db..00000000 --- a/apps/contract/contracts/nft/nft.rs +++ /dev/null @@ -1,56 +0,0 @@ - -use soroban_sdk::{contract, contractimpl, Address, Env, String}; - -use crate::{ - types::TokenMetadata, - storage::NFTStorage, - errors::NFTError, - events::NFTEvents, -}; - -#[contract] -pub struct NFTContract; - -#[contractimpl] -impl NFTContract { - pub fn initialize(env: Env, admin: Address) -> Result<(), NFTError> { - if env.storage().has(&DataKey::Admin) { - return Err(NFTError::AlreadyInitialized); - } - - NFTStorage::set_admin(&env, &admin); - Ok(()) - } - - pub fn mint( - env: Env, - to: Address, - token_id: u32, - name: String, - symbol: String, - uri: String, - ) -> Result<(), NFTError> { - // Check if the sender is the admin - } - - pub fn transfer( - env: Env, - from: Address, - to: Address, - token_id: u32, - ) -> Result<(), NFTError> { - // Check if the sender is the owner of the token - } - - pub fn owner_of(env: Env, token_id: u32) -> Result { - // Check if the token exists - } - - pub fn balance_of(env: Env, owner: Address) -> u32 { - // Get the balance of the owner - } - - pub fn token_metadata(env: Env, token_id: u32) -> Result { - // Get the metadata of the token - } -} diff --git a/apps/contract/contracts/nft/src/contract.rs b/apps/contract/contracts/nft/src/contract.rs new file mode 100644 index 00000000..84567521 --- /dev/null +++ b/apps/contract/contracts/nft/src/contract.rs @@ -0,0 +1,56 @@ +use soroban_sdk::{contract, contractimpl, contracttype, Address, Env, String}; + +use crate::{errors::NFTError, storage::NFTStorage, types::TokenMetadata}; + +#[contract] +pub struct NFTContract; + +#[contracttype] +#[derive(Clone)] +enum DataKey { + Admin, +} + +#[contractimpl] +impl NFTContract { + pub fn initialize(env: Env, admin: Address) -> Result<(), NFTError> { + if env.storage().instance().has(&DataKey::Admin) { + return Err(NFTError::AlreadyInitialized); + } + + // NFTStorage::set_admin(&env, &admin); + Ok(()) + } + + pub fn mint( + env: Env, + to: Address, + token_id: u32, + name: String, + symbol: String, + uri: String, + ) -> Result<(), NFTError> { + // Check if the sender is the admin + Ok(()) + } + + pub fn transfer(env: Env, from: Address, to: Address, token_id: u32) -> Result<(), NFTError> { + // Check if the sender is the owner of the token + Ok(()) + } + + // pub fn owner_of(env: Env, token_id: u32) -> Result { + // // Check if the token exists + // Ok(()) + // } + + // pub fn balance_of(env: Env, owner: Address) -> u32 { + // // Get the balance of the owner + // 5 + // } + + // pub fn token_metadata(env: Env, token_id: u32) -> Result { + // // Get the metadata of the token + // Ok(()) + // } +} diff --git a/apps/contract/contracts/nft/src/errors.rs b/apps/contract/contracts/nft/src/errors.rs index 3488700d..213e96d2 100644 --- a/apps/contract/contracts/nft/src/errors.rs +++ b/apps/contract/contracts/nft/src/errors.rs @@ -1,8 +1,11 @@ -#[derive(Debug)] +use soroban_sdk::contracterror; + +#[contracterror] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum NFTError { - AlreadyInitialized, - NotAuthorized, - TokenAlreadyExists, - TokenNotFound, - NotTokenOwner, -} \ No newline at end of file + AlreadyInitialized = 100, + NotAuthorized = 101, + TokenAlreadyExists = 102, + TokenNotFound = 103, + NotTokenOwner = 104, +} diff --git a/apps/contract/contracts/nft/src/events.rs b/apps/contract/contracts/nft/src/events.rs index 7a0482a7..bd5f6b5d 100644 --- a/apps/contract/contracts/nft/src/events.rs +++ b/apps/contract/contracts/nft/src/events.rs @@ -1,13 +1,13 @@ -use soroban_sdk::{symbol_short, Symbol}; +use soroban_sdk::{Address, Env}; pub struct NFTEvents; impl NFTEvents { - pub fn mint(env: &Env, to: &Address, token_id: u32) { - // Publish the MINT event - } + pub fn mint(env: &Env, to: &Address, token_id: u32) { + // Publish the MINT event + } - pub fn transfer(env: &Env, from: &Address, to: &Address, token_id: u32) { - // Publish the TRANSFER event - } + pub fn transfer(env: &Env, from: &Address, to: &Address, token_id: u32) { + // Publish the TRANSFER event + } } diff --git a/apps/contract/contracts/nft/src/lib.rs b/apps/contract/contracts/nft/src/lib.rs index c9daeb25..dd0e5864 100644 --- a/apps/contract/contracts/nft/src/lib.rs +++ b/apps/contract/contracts/nft/src/lib.rs @@ -1,11 +1,11 @@ #![no_std] mod contract; -mod storage; -mod types; mod errors; mod events; +mod storage; +mod types; pub use contract::NFTContract; +pub use errors::*; pub use types::*; -pub use errors::*; \ No newline at end of file diff --git a/apps/contract/contracts/nft/src/storage.rs b/apps/contract/contracts/nft/src/storage.rs index 0b472f5e..769bee0f 100644 --- a/apps/contract/contracts/nft/src/storage.rs +++ b/apps/contract/contracts/nft/src/storage.rs @@ -1,9 +1,5 @@ -use soroban_sdk::{Env, Address}; -use crate::types::{DataKey, TokenMetadata}; -use crate::errors::NFTError; - pub struct NFTStorage; impl NFTStorage { - // Set the admin of the contract in the storage -} \ No newline at end of file + // Set the admin of the contract in the storage +} diff --git a/apps/contract/contracts/nft/src/types.rs b/apps/contract/contracts/nft/src/types.rs index ecaf057a..b45b4d9a 100644 --- a/apps/contract/contracts/nft/src/types.rs +++ b/apps/contract/contracts/nft/src/types.rs @@ -16,4 +16,4 @@ pub enum DataKey { TokenMetadata(u32), TokenUri(u32), UserBalance(Address), -} \ No newline at end of file +} diff --git a/apps/contract/rust-toolchain.toml b/apps/contract/rust-toolchain.toml new file mode 100644 index 00000000..e340b764 --- /dev/null +++ b/apps/contract/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "stable" +targets = ["wasm32-unknown-unknown"] +components = ["rustc", "cargo", "rustfmt", "clippy", "rust-src"]