From 331844dcf278ccdf96ce3b63fb3e5f2c78970561 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Sat, 21 Jan 2023 00:19:38 +0100 Subject: [PATCH 001/246] fix: link (#545) --- docs/modules/ROOT/pages/udc.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/udc.adoc b/docs/modules/ROOT/pages/udc.adoc index 70a95bf70..61783690b 100644 --- a/docs/modules/ROOT/pages/udc.adoc +++ b/docs/modules/ROOT/pages/udc.adoc @@ -2,7 +2,7 @@ The Universal Deployer Contract (UDC) is a singleton smart contract that wraps the https://www.cairo-lang.org/docs/hello_starknet/deploying_from_contracts.html#the-deploy-system-call[`deploy syscall`] to expose it to any contract that doesn't implement it, such as account contracts. You can think of it as a **standardized generic factory for StarkNet contracts**. -And since StarkNet has no deployment transaction type, it offers a canonical way to deploy smart contracts by following the [standard deployer interface](https://community.starknet.io/t/snip-deployer-contract-interface/2772) and emitting a `ContractDeployed` event. +And since StarkNet has no deployment transaction type, it offers a canonical way to deploy smart contracts by following the https://community.starknet.io/t/snip-deployer-contract-interface/2772[standard deployer interface] and emitting a `ContractDeployed` event. TIP: For information on design decisions, see the https://community.starknet.io/t/universal-deployer-contract-proposal/1864[Universal Deployer Contract Proposal]. From b9ec20b8e2ccff982f85875aa884f2c68e76feee Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 17 Feb 2023 19:43:59 -0500 Subject: [PATCH 002/246] add submodule --- .gitmodules | 3 +++ cairo | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 cairo diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..269eb8546 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "cairo"] + path = cairo + url = https://github.com/starkware-libs/cairo.git diff --git a/cairo b/cairo new file mode 160000 index 000000000..b89f575b6 --- /dev/null +++ b/cairo @@ -0,0 +1 @@ +Subproject commit b89f575b6d3873b5474af3731d68b7ac2614043e From bb401246d53f3e4555ed8ee13579dec409799103 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 17 Feb 2023 19:44:37 -0500 Subject: [PATCH 003/246] update cairo --- cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cairo b/cairo index b89f575b6..1991669a0 160000 --- a/cairo +++ b/cairo @@ -1 +1 @@ -Subproject commit b89f575b6d3873b5474af3731d68b7ac2614043e +Subproject commit 1991669a0453bd65541ff81a12772bac05130052 From a0c3157791d71a7b3e4e256c36754edc853f77e9 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 17 Feb 2023 19:45:32 -0500 Subject: [PATCH 004/246] add Cargo and Makefile --- Cargo.lock | 3190 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 89 ++ Makefile | 28 + 3 files changed, 3307 insertions(+) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 Makefile diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..696ce9451 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,3190 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[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 = "anyhow" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" + +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits 0.2.15", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.0-alpha.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cf4b42c978bd9b967f6a2ba54a4cc57f34780f9d0cf86916f8f7e922b4f80fe" +dependencies = [ + "ark-ff-asm 0.4.0-alpha.7", + "ark-ff-macros 0.4.0-alpha.7", + "ark-serialize 0.4.0-alpha.7", + "ark-std 0.4.0-alpha", + "derivative", + "digest 0.10.6", + "itertools", + "num-bigint", + "num-traits 0.2.15", + "paste", + "rustc_version 0.4.0", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.0-alpha.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3ba0a3cf584a8ede533a1749b86040e9018cf752265fc39a71c69fe1fafaba5" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits 0.2.15", + "quote", + "syn", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.0-alpha.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9989a01ec42d2f31232796831077ef730d4cd0d0931f5ef6962a43ebddd215fa" +dependencies = [ + "num-bigint", + "num-traits 0.2.15", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.0-alpha.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84aeb60f7c6792ae71e9e2225412ecd59d7c85cfec4183a8c497f2a3a4cdb36e" +dependencies = [ + "ark-std 0.4.0-alpha", + "digest 0.10.6", + "num-bigint", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits 0.2.15", + "rand", +] + +[[package]] +name = "ark-std" +version = "0.4.0-alpha" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc5b16d734e9b2e43886ff586755219df7fb9639cc04ab00c7e636708a5ba06a" +dependencies = [ + "num-traits 0.2.15", + "rand", +] + +[[package]] +name = "ascii-canvas" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +dependencies = [ + "term", +] + +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + +[[package]] +name = "async-trait" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "auto_impl" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7862e21c893d65a1650125d157eaeec691439379a1cee17ee49031b79236ada4" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bigdecimal" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aaf33151a6429fe9211d1b276eafdf70cdff28b071e76c0b0e1503221ea3744" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits 0.2.15", +] + +[[package]] +name = "bimap" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0455254eb5c6964c4545d8bac815e1a1be4f3afe0ae695ea539c12d728d44b" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45ea9b00a7b3f2988e9a65ad3917e62123c38dba709b666506207be96d1790b" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" + +[[package]] +name = "bytes" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" + +[[package]] +name = "cairo-felt" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a792f409586c42c2d62cff582989e573d45b0855be95e022421d25f608364cc1" +dependencies = [ + "lazy_static", + "num-bigint", + "num-integer", + "num-traits 0.2.15", + "serde", +] + +[[package]] +name = "cairo-lang-casm" +version = "1.0.0-alpha.2" +dependencies = [ + "cairo-lang-utils", + "env_logger", + "indoc", + "itertools", + "num-bigint", + "num-traits 0.2.15", + "pretty_assertions", + "test-case", + "test-log", + "thiserror", +] + +[[package]] +name = "cairo-lang-compiler" +version = "1.0.0-alpha.2" +dependencies = [ + "anyhow", + "cairo-lang-defs", + "cairo-lang-diagnostics", + "cairo-lang-filesystem", + "cairo-lang-lowering", + "cairo-lang-parser", + "cairo-lang-plugins", + "cairo-lang-project", + "cairo-lang-semantic", + "cairo-lang-sierra", + "cairo-lang-sierra-generator", + "cairo-lang-syntax", + "cairo-lang-utils", + "clap 4.0.26", + "log", + "salsa", + "test-log", + "thiserror", +] + +[[package]] +name = "cairo-lang-debug" +version = "1.0.0-alpha.2" +dependencies = [ + "cairo-lang-proc-macros", + "cairo-lang-utils", + "env_logger", + "salsa", + "test-log", +] + +[[package]] +name = "cairo-lang-defs" +version = "1.0.0-alpha.2" +dependencies = [ + "cairo-lang-debug", + "cairo-lang-diagnostics", + "cairo-lang-filesystem", + "cairo-lang-parser", + "cairo-lang-syntax", + "cairo-lang-test-utils", + "cairo-lang-utils", + "env_logger", + "indexmap", + "indoc", + "itertools", + "pretty_assertions", + "salsa", + "smol_str", + "test-log", +] + +[[package]] +name = "cairo-lang-diagnostics" +version = "1.0.0-alpha.2" +dependencies = [ + "cairo-lang-filesystem", + "cairo-lang-proc-macros", + "cairo-lang-utils", + "env_logger", + "indoc", + "itertools", + "pretty_assertions", + "salsa", + "test-log", +] + +[[package]] +name = "cairo-lang-eq-solver" +version = "1.0.0-alpha.2" +dependencies = [ + "cairo-lang-utils", + "env_logger", + "good_lp", + "indexmap", + "indoc", + "itertools", + "test-case", + "test-log", +] + +[[package]] +name = "cairo-lang-filesystem" +version = "1.0.0-alpha.2" +dependencies = [ + "cairo-lang-debug", + "cairo-lang-utils", + "env_logger", + "path-clean", + "salsa", + "smol_str", + "test-log", +] + +[[package]] +name = "cairo-lang-formatter" +version = "1.0.0-alpha.2" +dependencies = [ + "anyhow", + "cairo-lang-diagnostics", + "cairo-lang-filesystem", + "cairo-lang-parser", + "cairo-lang-syntax", + "cairo-lang-utils", + "clap 4.0.26", + "colored", + "diffy", + "ignore", + "itertools", + "log", + "pretty_assertions", + "salsa", + "smol_str", + "test-case", + "test-log", +] + +[[package]] +name = "cairo-lang-language-server" +version = "1.0.0-alpha.2" +dependencies = [ + "cairo-lang-compiler", + "cairo-lang-debug", + "cairo-lang-defs", + "cairo-lang-diagnostics", + "cairo-lang-filesystem", + "cairo-lang-formatter", + "cairo-lang-lowering", + "cairo-lang-parser", + "cairo-lang-plugins", + "cairo-lang-project", + "cairo-lang-semantic", + "cairo-lang-starknet", + "cairo-lang-syntax", + "cairo-lang-utils", + "lsp-types", + "salsa", + "serde", + "serde_json", + "test-log", + "tokio", + "tower-lsp", +] + +[[package]] +name = "cairo-lang-lowering" +version = "1.0.0-alpha.2" +dependencies = [ + "cairo-lang-debug", + "cairo-lang-defs", + "cairo-lang-diagnostics", + "cairo-lang-filesystem", + "cairo-lang-parser", + "cairo-lang-plugins", + "cairo-lang-proc-macros", + "cairo-lang-semantic", + "cairo-lang-syntax", + "cairo-lang-test-utils", + "cairo-lang-utils", + "env_logger", + "id-arena", + "indoc", + "itertools", + "log", + "num-bigint", + "num-traits 0.2.15", + "pretty_assertions", + "salsa", + "smol_str", + "test-log", +] + +[[package]] +name = "cairo-lang-parser" +version = "1.0.0-alpha.2" +dependencies = [ + "cairo-lang-diagnostics", + "cairo-lang-filesystem", + "cairo-lang-syntax", + "cairo-lang-syntax-codegen", + "cairo-lang-test-utils", + "cairo-lang-utils", + "colored", + "env_logger", + "itertools", + "log", + "pretty_assertions", + "salsa", + "smol_str", + "test-case", + "test-log", +] + +[[package]] +name = "cairo-lang-plugins" +version = "1.0.0-alpha.2" +dependencies = [ + "cairo-lang-debug", + "cairo-lang-defs", + "cairo-lang-diagnostics", + "cairo-lang-filesystem", + "cairo-lang-parser", + "cairo-lang-semantic", + "cairo-lang-syntax", + "cairo-lang-test-utils", + "cairo-lang-utils", + "env_logger", + "indoc", + "itertools", + "pretty_assertions", + "salsa", + "smol_str", + "test-case", + "test-log", +] + +[[package]] +name = "cairo-lang-proc-macros" +version = "1.0.0-alpha.2" +dependencies = [ + "cairo-lang-debug", + "quote", + "syn", +] + +[[package]] +name = "cairo-lang-project" +version = "1.0.0-alpha.2" +dependencies = [ + "cairo-lang-filesystem", + "indoc", + "serde", + "smol_str", + "test-log", + "thiserror", + "toml", +] + +[[package]] +name = "cairo-lang-runner" +version = "1.0.0-alpha.2" +dependencies = [ + "anyhow", + "ark-ff 0.4.0-alpha.7", + "ark-std 0.3.0", + "cairo-felt", + "cairo-lang-casm", + "cairo-lang-compiler", + "cairo-lang-diagnostics", + "cairo-lang-sierra", + "cairo-lang-sierra-ap-change", + "cairo-lang-sierra-gas", + "cairo-lang-sierra-generator", + "cairo-lang-sierra-to-casm", + "cairo-lang-utils", + "cairo-vm", + "clap 4.0.26", + "itertools", + "num-bigint", + "num-traits 0.2.15", + "pretty_assertions", + "salsa", + "test-case", + "thiserror", +] + +[[package]] +name = "cairo-lang-semantic" +version = "1.0.0-alpha.2" +dependencies = [ + "assert_matches", + "cairo-lang-debug", + "cairo-lang-defs", + "cairo-lang-diagnostics", + "cairo-lang-filesystem", + "cairo-lang-parser", + "cairo-lang-plugins", + "cairo-lang-proc-macros", + "cairo-lang-syntax", + "cairo-lang-test-utils", + "cairo-lang-utils", + "env_logger", + "id-arena", + "indoc", + "itertools", + "log", + "num-bigint", + "num-traits 0.2.15", + "pretty_assertions", + "salsa", + "smol_str", + "test-case", + "test-log", + "unescaper", +] + +[[package]] +name = "cairo-lang-sierra" +version = "1.0.0-alpha.2" +dependencies = [ + "assert_matches", + "bimap", + "cairo-lang-utils", + "const-fnv1a-hash", + "convert_case", + "derivative", + "env_logger", + "indoc", + "itertools", + "lalrpop", + "lalrpop-util", + "num-bigint", + "num-traits 0.2.15", + "pretty_assertions", + "regex", + "salsa", + "serde", + "sha3", + "smol_str", + "test-case", + "test-log", + "thiserror", +] + +[[package]] +name = "cairo-lang-sierra-ap-change" +version = "1.0.0-alpha.2" +dependencies = [ + "cairo-lang-eq-solver", + "cairo-lang-sierra", + "cairo-lang-utils", + "env_logger", + "indoc", + "itertools", + "test-case", + "test-log", + "thiserror", +] + +[[package]] +name = "cairo-lang-sierra-gas" +version = "1.0.0-alpha.2" +dependencies = [ + "cairo-lang-eq-solver", + "cairo-lang-sierra", + "cairo-lang-test-utils", + "cairo-lang-utils", + "env_logger", + "indoc", + "itertools", + "pretty_assertions", + "test-case", + "test-log", + "thiserror", +] + +[[package]] +name = "cairo-lang-sierra-generator" +version = "1.0.0-alpha.2" +dependencies = [ + "cairo-lang-debug", + "cairo-lang-defs", + "cairo-lang-diagnostics", + "cairo-lang-filesystem", + "cairo-lang-lowering", + "cairo-lang-parser", + "cairo-lang-plugins", + "cairo-lang-proc-macros", + "cairo-lang-semantic", + "cairo-lang-sierra", + "cairo-lang-syntax", + "cairo-lang-test-utils", + "cairo-lang-utils", + "env_logger", + "id-arena", + "indexmap", + "indoc", + "itertools", + "log", + "num-bigint", + "pretty_assertions", + "salsa", + "smol_str", + "test-case", + "test-log", +] + +[[package]] +name = "cairo-lang-sierra-to-casm" +version = "1.0.0-alpha.2" +dependencies = [ + "assert_matches", + "cairo-felt", + "cairo-lang-casm", + "cairo-lang-sierra", + "cairo-lang-sierra-ap-change", + "cairo-lang-sierra-gas", + "cairo-lang-utils", + "clap 4.0.26", + "env_logger", + "indoc", + "itertools", + "log", + "num-bigint", + "num-traits 0.2.15", + "pretty_assertions", + "test-case", + "test-log", + "thiserror", +] + +[[package]] +name = "cairo-lang-starknet" +version = "1.0.0-alpha.2" +dependencies = [ + "anyhow", + "cairo-lang-compiler", + "cairo-lang-defs", + "cairo-lang-diagnostics", + "cairo-lang-filesystem", + "cairo-lang-lowering", + "cairo-lang-parser", + "cairo-lang-plugins", + "cairo-lang-semantic", + "cairo-lang-sierra", + "cairo-lang-sierra-ap-change", + "cairo-lang-sierra-gas", + "cairo-lang-sierra-generator", + "cairo-lang-sierra-to-casm", + "cairo-lang-syntax", + "cairo-lang-test-utils", + "cairo-lang-utils", + "clap 4.0.26", + "convert_case", + "env_logger", + "genco", + "indoc", + "itertools", + "lazy_static", + "log", + "num-bigint", + "num-integer", + "num-traits 0.2.15", + "pretty_assertions", + "serde", + "serde_json", + "sha3", + "smol_str", + "test-case", + "test-log", + "thiserror", +] + +[[package]] +name = "cairo-lang-syntax" +version = "1.0.0-alpha.2" +dependencies = [ + "cairo-lang-debug", + "cairo-lang-filesystem", + "cairo-lang-utils", + "env_logger", + "pretty_assertions", + "salsa", + "smol_str", + "test-log", +] + +[[package]] +name = "cairo-lang-syntax-codegen" +version = "1.0.0-alpha.2" +dependencies = [ + "cairo-lang-utils", + "env_logger", + "genco", + "log", + "test-log", + "xshell", +] + +[[package]] +name = "cairo-lang-test-runner" +version = "1.0.0-alpha.2" +dependencies = [ + "anyhow", + "cairo-felt", + "cairo-lang-casm", + "cairo-lang-compiler", + "cairo-lang-debug", + "cairo-lang-defs", + "cairo-lang-diagnostics", + "cairo-lang-filesystem", + "cairo-lang-plugins", + "cairo-lang-project", + "cairo-lang-runner", + "cairo-lang-semantic", + "cairo-lang-sierra", + "cairo-lang-sierra-generator", + "cairo-lang-sierra-to-casm", + "cairo-lang-starknet", + "cairo-lang-syntax", + "cairo-lang-utils", + "cairo-vm", + "clap 4.0.26", + "colored", + "itertools", + "num-bigint", + "rayon", + "salsa", + "thiserror", +] + +[[package]] +name = "cairo-lang-test-utils" +version = "1.0.0-alpha.2" +dependencies = [ + "cairo-lang-utils", + "env_logger", + "log", + "pretty_assertions", + "test-log", +] + +[[package]] +name = "cairo-lang-utils" +version = "1.0.0-alpha.2" +dependencies = [ + "chrono", + "env_logger", + "indexmap", + "itertools", + "log", + "test-case", + "test-log", +] + +[[package]] +name = "cairo-vm" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4fa1ade23dd2a6e671a923e748406c2c5f6c27fc186950ed04fae392b552a51" +dependencies = [ + "bincode", + "cairo-felt", + "clap 3.2.23", + "generic-array", + "hex", + "keccak", + "lazy_static", + "mimalloc", + "nom", + "num-bigint", + "num-integer", + "num-traits 0.2.15", + "parse-hyperlinks", + "rand_core", + "serde", + "serde_bytes", + "serde_json", + "sha2 0.10.6", + "sha3", + "starknet-crypto", + "thiserror", +] + +[[package]] +name = "cc" +version = "1.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" + +[[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.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits 0.2.15", + "time", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "clap" +version = "3.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +dependencies = [ + "atty", + "bitflags", + "clap_derive 3.2.18", + "clap_lex 0.2.4", + "indexmap", + "once_cell", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap" +version = "4.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2148adefda54e14492fb9bddcc600b4344c5d1a3123bd666dcb939c6f0e0e57e" +dependencies = [ + "atty", + "bitflags", + "clap_derive 4.0.21", + "clap_lex 0.3.0", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "3.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +dependencies = [ + "heck 0.4.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_derive" +version = "4.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" +dependencies = [ + "heck 0.4.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "clap_lex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "colored" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +dependencies = [ + "atty", + "lazy_static", + "winapi", +] + +[[package]] +name = "const-fnv1a-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b13ea120a812beba79e34316b3942a857c86ec1593cb34f27bb28272ce2cca" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +dependencies = [ + "generic-array", + "rand_core", + "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 = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "ctor" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "cxx" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a41a86530d0fe7f5d9ea779916b7cadd2d4f9add748b99c2c029cbbdfaf453" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06416d667ff3e3ad2df1cd8cd8afae5da26cf9cec4d0825040f88b5ca659a2f0" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "820a9a2af1669deeef27cb271f476ffd196a2c4b6731336011e0ba63e2c7cf71" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08a6e2fcc370a089ad3b4aaf54db3b1b4cee38ddabce5896b33eb693275f470" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dashmap" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core 0.9.4", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "diffy" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e616e59155c92257e84970156f506287853355f58cd4a6eb167385722c32b790" +dependencies = [ + "nu-ansi-term", +] + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.3", + "crypto-common", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + +[[package]] +name = "ena" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +dependencies = [ + "log", +] + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" + +[[package]] +name = "futures-io" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" + +[[package]] +name = "futures-macro" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" + +[[package]] +name = "futures-task" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" + +[[package]] +name = "futures-util" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "genco" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8598ff0782dbc5231cf9eb727c1c5e398515b7b62ee68761c2c73950a1de1f4" +dependencies = [ + "genco-macros", + "relative-path", + "smallvec", +] + +[[package]] +name = "genco-macros" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40803f2757f84c877f088e62420931f6e05a72514f1f03630384aa30b91d667b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "globset" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "good_lp" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b51d78cbb7b734379eea7f811ddb33b2b13defefa1dab50068d7bc7f781a3056" +dependencies = [ + "fnv", + "minilp", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + +[[package]] +name = "html-escape" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" +dependencies = [ + "utf8-width", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "ignore" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" +dependencies = [ + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "indoc" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3" + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" + +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lalrpop" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b30455341b0e18f276fa64540aff54deafb54c589de6aca68659c63dd2d5d823" +dependencies = [ + "ascii-canvas", + "atty", + "bit-set", + "diff", + "ena", + "itertools", + "lalrpop-util", + "petgraph", + "pico-args", + "regex", + "regex-syntax", + "string_cache", + "term", + "tiny-keccak", + "unicode-xid", +] + +[[package]] +name = "lalrpop-util" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf796c978e9b4d983414f4caedc9273aa33ee214c5b887bd55fde84c85d2dc4" +dependencies = [ + "regex", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" + +[[package]] +name = "libmimalloc-sys" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04d1c67deb83e6b75fa4fe3309e09cfeade12e7721d95322af500d3814ea60c9" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "lsp-types" +version = "0.93.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be6e9c7e2d18f651974370d7aff703f9513e0df6e464fd795660edc77e6ca51" +dependencies = [ + "bitflags", + "serde", + "serde_json", + "serde_repr", + "url", +] + +[[package]] +name = "matrixmultiply" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916806ba0031cd542105d916a97c8572e1fa6dd79c9c51e7eb43a09ec2dd84c1" +dependencies = [ + "rawpointer", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mimalloc" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2374e2999959a7b583e1811a1ddbf1d3a4b9496eceb9746f1192a59d871eca" +dependencies = [ + "libmimalloc-sys", +] + +[[package]] +name = "minilp" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82a7750a9e5076c660b7bec5e6457b4dbff402b9863c8d112891434e18fd5385" +dependencies = [ + "log", + "sprs", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + +[[package]] +name = "ndarray" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac06db03ec2f46ee0ecdca1a1c34a99c0d188a0d83439b84bf0cb4b386e4ab09" +dependencies = [ + "matrixmultiply", + "num-complex", + "num-integer", + "num-traits 0.2.15", + "rawpointer", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" + +[[package]] +name = "nom" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits 0.2.15", + "serde", +] + +[[package]] +name = "num-complex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" +dependencies = [ + "autocfg", + "num-traits 0.2.15", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits 0.2.15", +] + +[[package]] +name = "num-traits" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" +dependencies = [ + "num-traits 0.2.15", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "os_str_bytes" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + +[[package]] +name = "output_vt100" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" +dependencies = [ + "winapi", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.5", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.4", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "parse-hyperlinks" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0181d37c4d5ae35cc8be7cf823c1a933005661da6a08bcb2855aa392c9a54b8e" +dependencies = [ + "html-escape", + "nom", + "percent-encoding", + "thiserror", +] + +[[package]] +name = "paste" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" + +[[package]] +name = "path-clean" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecba01bf2678719532c5e3059e0b5f0811273d94b397088b82e3bd0a78c78fdd" + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pest" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a528564cc62c19a7acac4d81e01f39e53e25e17b934878f4c6d25cc2836e62f8" +dependencies = [ + "thiserror", + "ucd-trie", +] + +[[package]] +name = "petgraph" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pico-args" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" + +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "pretty_assertions" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" +dependencies = [ + "ctor", + "diff", + "output_vt100", + "yansi", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[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", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rayon" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed02d09394c94ffbdfdc755ad62a132e94c3224a8354e78a1200ced34df12edf" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "relative-path" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0df32d82cedd1499386877b062ebe8721f806de80b08d183c70184ef17dd1d42" + +[[package]] +name = "rfc6979" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" +dependencies = [ + "crypto-bigint", + "hmac", + "zeroize", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.16", +] + +[[package]] +name = "rustversion" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" + +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + +[[package]] +name = "salsa" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b84d9f96071f3f3be0dc818eae3327625d8ebc95b58da37d6850724f31d3403" +dependencies = [ + "crossbeam-utils", + "indexmap", + "lock_api", + "log", + "oorandom", + "parking_lot 0.11.2", + "rustc-hash", + "salsa-macros", + "smallvec", +] + +[[package]] +name = "salsa-macros" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3904a4ba0a9d0211816177fd34b04c7095443f8cdacd11175064fe541c8fe2" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scratch" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfc50e8183eeeb6178dcb167ae34a8051d63535023ae38b5d8d12beae193d37b" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sha3" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +dependencies = [ + "digest 0.10.6", + "keccak", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "smol_str" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7475118a28b7e3a2e157ce0131ba8c5526ea96e90ee601d9f6bb2e286a35ab44" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "sprs" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec63571489873d4506683915840eeb1bb16b3198ee4894cc6f2fe3013d505e56" +dependencies = [ + "ndarray", + "num-complex", + "num-traits 0.1.43", +] + +[[package]] +name = "starknet-crypto" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be7d6b2c959fde2a10dbc31d54bdd0307eecb7ef6c05c23a0263e65b57b3e18a" +dependencies = [ + "crypto-bigint", + "hex", + "hmac", + "num-bigint", + "num-integer", + "num-traits 0.2.15", + "rfc6979", + "sha2 0.9.9", + "starknet-crypto-codegen", + "starknet-curve", + "starknet-ff", + "thiserror", + "zeroize", +] + +[[package]] +name = "starknet-crypto-codegen" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6569d70430f0f6edc41f6820d00acf63356e6308046ca01e57eeac22ad258c47" +dependencies = [ + "starknet-curve", + "starknet-ff", + "syn", +] + +[[package]] +name = "starknet-curve" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84be6079d3060fdbd8b5335574fef3d3783fa2f7ee6474d08ae0c1e4b0a29ba4" +dependencies = [ + "starknet-ff", +] + +[[package]] +name = "starknet-ff" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5874510620214ebeac50915b01d67437d8ca10a6682b1de85b93cd01157b58eb" +dependencies = [ + "ark-ff 0.3.0", + "bigdecimal", + "crypto-bigint", + "getrandom", + "hex", + "num-bigint", + "serde", + "thiserror", +] + +[[package]] +name = "string_cache" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213494b7a2b503146286049378ce02b482200519accc31872ee8be91fa820a08" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot 0.12.1", + "phf_shared", + "precomputed-hash", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "test-case" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21d6cf5a7dffb3f9dceec8e6b8ca528d9bd71d36c9f074defb548ce161f598c0" +dependencies = [ + "test-case-macros", +] + +[[package]] +name = "test-case-macros" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e45b7bf6e19353ddd832745c8fcf77a17a93171df7151187f26623f2b75b5b26" +dependencies = [ + "cfg-if", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "test-log" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f0c854faeb68a048f0f2dc410c5ddae3bf83854ef0e4977d58306a5edef50e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tests" +version = "1.0.0-alpha.2" +dependencies = [ + "assert_matches", + "cairo-felt", + "cairo-lang-casm", + "cairo-lang-compiler", + "cairo-lang-defs", + "cairo-lang-diagnostics", + "cairo-lang-filesystem", + "cairo-lang-parser", + "cairo-lang-plugins", + "cairo-lang-runner", + "cairo-lang-semantic", + "cairo-lang-sierra", + "cairo-lang-sierra-gas", + "cairo-lang-sierra-generator", + "cairo-lang-sierra-to-casm", + "cairo-lang-syntax", + "cairo-lang-test-utils", + "cairo-lang-utils", + "env_logger", + "itertools", + "log", + "num-bigint", + "pretty_assertions", + "salsa", + "test-case", + "test-log", +] + +[[package]] +name = "textwrap" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" + +[[package]] +name = "thiserror" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot 0.12.1", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-util" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" +dependencies = [ + "serde", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-lsp" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43e094780b4447366c59f79acfd65b1375ecaa84e61dddbde1421aa506334024" +dependencies = [ + "async-trait", + "auto_impl", + "bytes", + "dashmap", + "futures", + "httparse", + "log", + "lsp-types", + "memchr", + "serde", + "serde_json", + "tokio", + "tokio-util", + "tower", + "tower-lsp-macros", +] + +[[package]] +name = "tower-lsp-macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebd99eec668d0a450c177acbc4d05e0d0d13b1f8d3db13cd706c52cbec4ac04" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "ucd-trie" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" + +[[package]] +name = "unescaper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "995483205de764db1185c9461a000fff73fa4b9ee2bbe4c8b4027a94692700fe" +dependencies = [ + "thiserror", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8-width" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5190c9442dcdaf0ddd50f37420417d219ae5261bbf5db120d0f9bab996c9cba1" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + +[[package]] +name = "xshell" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d47097dc5c85234b1e41851b3422dd6d19b3befdd35b4ae5ce386724aeca981" +dependencies = [ + "xshell-macros", +] + +[[package]] +name = "xshell-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88301b56c26dd9bf5c43d858538f82d6f3f7764767defbc5d34e59459901c41a" + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..1179081f2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,89 @@ +[workspace] + +members = [ + "cairo/crates/cairo-lang-casm", + "cairo/crates/cairo-lang-compiler", + "cairo/crates/cairo-lang-utils", + "cairo/crates/cairo-lang-debug", + "cairo/crates/cairo-lang-defs", + "cairo/crates/cairo-lang-proc-macros", + "cairo/crates/cairo-lang-diagnostics", + "cairo/crates/cairo-lang-eq-solver", + "cairo/crates/cairo-lang-filesystem", + "cairo/crates/cairo-lang-formatter", + "cairo/crates/cairo-lang-language-server", + "cairo/crates/cairo-lang-lowering", + "cairo/crates/cairo-lang-parser", + "cairo/crates/cairo-lang-plugins", + "cairo/crates/cairo-lang-project", + "cairo/crates/cairo-lang-runner", + "cairo/crates/cairo-lang-semantic", + "cairo/crates/cairo-lang-sierra-ap-change", + "cairo/crates/cairo-lang-sierra-gas", + "cairo/crates/cairo-lang-sierra-generator", + "cairo/crates/cairo-lang-sierra-to-casm", + "cairo/crates/cairo-lang-sierra", + "cairo/crates/cairo-lang-starknet", + "cairo/crates/cairo-lang-syntax-codegen", + "cairo/crates/cairo-lang-syntax", + "cairo/crates/cairo-lang-test-runner", + "cairo/tests", +] + +[workspace.package] +version = "1.0.0-alpha.2" +edition = "2021" +repository = "https://github.com/starkware-libs/cairo/" +license = "Apache-2.0" +license-file = "LICENSE" + +[workspace.dependencies] +anyhow = "1.0.66" +ark-ff = "0.4.0-alpha.7" +ark-std = "0.3.0" +assert_matches = "1.5" +bimap = "0.6.2" +cairo-felt = "0.1.1" +cairo-vm = "0.1.2" +chrono = "0.4.23" +clap = { version = "4.0", features = ["derive"] } +colored = "2" +const-fnv1a-hash = "1.1.0" +convert_case = "0.6.0" +derivative = "2.2.0" +diffy = "0.3.0" +env_logger = "0.9.3" +genco = "0.17.0" +good_lp = { version = "1.3.2", features = ["minilp"], default-features = false } +id-arena = "2.2.1" +ignore = "0.4.20" +indexmap = "1.9.1" +indoc = "1.0.7" +itertools = "0.10.3" +lalrpop-util = { version = "0.19.8", features = ["lexer"] } +lazy_static = "1.4.0" +log = "0.4" +lsp = { version = "0.93", package = "lsp-types" } +num-bigint = "0.4" +num-integer = "0.1" +num-traits = "0.2" +path-clean = "0.1.0" +pretty_assertions = "1.2.1" +proc-macro2 = "1.0" +quote = "1.0.21" +rayon = "0.9.0" +salsa = "0.16.1" +serde = { version = "1.0.130", features = ["derive"] } +serde_json = "1.0" +sha3 = "0.10.6" +smol_str = "0.1.23" +syn = { version = "1.0.99", features = ["full", "extra-traits"] } +test-case = "2.2.2" +test-case-macros = "2.2.2" +test-log = "0.2.11" +thiserror = "1.0.32" +tokio = { version = "1.18.2", features = ["full", "sync"] } +toml = "0.4.2" +tower-lsp = "0.17.0" +unescaper = "0.1.1" +xshell = "0.2.2" diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..020e49482 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +.SILENT: +.PHONY: compile +SOURCE_FOLDER=./src/openzeppelin + +install: + git submodule init && git submodule update && cp -rf cairo/corelib . + +update: + git submodule update && cp -rf cairo/corelib . + +build: + cargo build + +test: + cargo run --bin cairo-test -- --starknet --path $(dir) + +format: + cargo run --bin cairo-format -- --recursive $(SOURCE_FOLDER) --print-parsing-errors + +check-format: + cargo run --bin cairo-format -- --check --recursive $(SOURCE_FOLDER) + +starknet-compile: + mkdir -p artifacts && \ + cargo run --bin starknet-compile -- ${dir} artifacts/$(shell basename $(dir)).json --allowed-libfuncs-list-name experimental_v0.1.0 + +language-server: + cargo build --bin cairo-language-server --release From 3a6e20dd7897158258e0d9a7049d22822142b2ba Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 18 Feb 2023 09:58:52 -0500 Subject: [PATCH 005/246] update cairo --- cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cairo b/cairo index 1991669a0..7d27aee2c 160000 --- a/cairo +++ b/cairo @@ -1 +1 @@ -Subproject commit 1991669a0453bd65541ff81a12772bac05130052 +Subproject commit 7d27aee2c7832592f3d091d01c3ceb39e45484e1 From 96431dedb001fb4adf822bcc5a7f4a5ad54e8943 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 18 Feb 2023 10:00:44 -0500 Subject: [PATCH 006/246] add deps in cairo_project, create lib --- src/openzeppelin/token/erc20/cairo_project.toml | 3 +++ src/openzeppelin/token/erc20/lib.cairo | 6 ++++++ 2 files changed, 9 insertions(+) create mode 100644 src/openzeppelin/token/erc20/cairo_project.toml create mode 100644 src/openzeppelin/token/erc20/lib.cairo diff --git a/src/openzeppelin/token/erc20/cairo_project.toml b/src/openzeppelin/token/erc20/cairo_project.toml new file mode 100644 index 000000000..ffd670876 --- /dev/null +++ b/src/openzeppelin/token/erc20/cairo_project.toml @@ -0,0 +1,3 @@ +[crate_roots] +erc20 = "." +presets = "presets" diff --git a/src/openzeppelin/token/erc20/lib.cairo b/src/openzeppelin/token/erc20/lib.cairo new file mode 100644 index 000000000..e87b836c1 --- /dev/null +++ b/src/openzeppelin/token/erc20/lib.cairo @@ -0,0 +1,6 @@ +mod erc20; +use erc20::ERC20Library; + +mod tests; + +mod presets; From 7b387068c30da1652d3cacf433d46e3072eb26e6 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 18 Feb 2023 10:01:26 -0500 Subject: [PATCH 007/246] add presets --- src/openzeppelin/token/erc20/presets.cairo | 3 + .../token/erc20/presets/ERC20.cairo | 169 ++++++++---------- .../token/erc20/presets/ERC20Burnable.cairo | 118 ------------ .../token/erc20/presets/ERC20Mintable.cairo | 130 -------------- .../token/erc20/presets/ERC20Pausable.cairo | 146 --------------- .../erc20/presets/ERC20Upgradeable.cairo | 121 ------------- .../token/erc20/presets/cairo_project.toml | 2 + .../token/erc20/presets/erc20_burnable.cairo | 85 +++++++++ .../token/erc20/presets/erc20_mintable.cairo | 76 ++++++++ .../token/erc20/presets/lib.cairo | 8 + 10 files changed, 244 insertions(+), 614 deletions(-) create mode 100644 src/openzeppelin/token/erc20/presets.cairo delete mode 100644 src/openzeppelin/token/erc20/presets/ERC20Burnable.cairo delete mode 100644 src/openzeppelin/token/erc20/presets/ERC20Mintable.cairo delete mode 100644 src/openzeppelin/token/erc20/presets/ERC20Pausable.cairo delete mode 100644 src/openzeppelin/token/erc20/presets/ERC20Upgradeable.cairo create mode 100644 src/openzeppelin/token/erc20/presets/cairo_project.toml create mode 100644 src/openzeppelin/token/erc20/presets/erc20_burnable.cairo create mode 100644 src/openzeppelin/token/erc20/presets/erc20_mintable.cairo create mode 100644 src/openzeppelin/token/erc20/presets/lib.cairo diff --git a/src/openzeppelin/token/erc20/presets.cairo b/src/openzeppelin/token/erc20/presets.cairo new file mode 100644 index 000000000..0ca3c862c --- /dev/null +++ b/src/openzeppelin/token/erc20/presets.cairo @@ -0,0 +1,3 @@ +mod erc20; +mod erc20_mintable; +mod erc20_burnable; diff --git a/src/openzeppelin/token/erc20/presets/ERC20.cairo b/src/openzeppelin/token/erc20/presets/ERC20.cairo index aec8de917..d2273854e 100644 --- a/src/openzeppelin/token/erc20/presets/ERC20.cairo +++ b/src/openzeppelin/token/erc20/presets/ERC20.cairo @@ -1,100 +1,71 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc20/presets/ERC20.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.uint256 import Uint256 - -from openzeppelin.token.erc20.library import ERC20 - -@constructor -func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - name: felt, symbol: felt, decimals: felt, initial_supply: Uint256, recipient: felt -) { - ERC20.initializer(name, symbol, decimals); - ERC20._mint(recipient, initial_supply); - return (); -} - -// -// Getters -// - -@view -func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) { - return ERC20.name(); -} - -@view -func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) { - return ERC20.symbol(); -} - -@view -func totalSupply{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - totalSupply: Uint256 -) { - let (totalSupply: Uint256) = ERC20.total_supply(); - return (totalSupply=totalSupply); -} - -@view -func decimals{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - decimals: felt -) { - return ERC20.decimals(); -} - -@view -func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(account: felt) -> ( - balance: Uint256 -) { - return ERC20.balance_of(account); -} - -@view -func allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - owner: felt, spender: felt -) -> (remaining: Uint256) { - return ERC20.allowance(owner, spender); -} - -// -// Externals -// - -@external -func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - recipient: felt, amount: Uint256 -) -> (success: felt) { - return ERC20.transfer(recipient, amount); -} - -@external -func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - sender: felt, recipient: felt, amount: Uint256 -) -> (success: felt) { - return ERC20.transfer_from(sender, recipient, amount); -} - -@external -func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - spender: felt, amount: Uint256 -) -> (success: felt) { - return ERC20.approve(spender, amount); -} - -@external -func increaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - spender: felt, added_value: Uint256 -) -> (success: felt) { - return ERC20.increase_allowance(spender, added_value); -} - -@external -func decreaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - spender: felt, subtracted_value: Uint256 -) -> (success: felt) { - return ERC20.decrease_allowance(spender, subtracted_value); +#[contract] +mod ERC20 { + use erc20::ERC20Library; + + // TMP starknet testing isn't fully functional. + // Use to ensure paths are correctly set. + #[external] + fn mock_constructor(name: felt, symbol: felt) { + ERC20Library::mock_initializer(name, symbol); + } + + #[constructor] + fn constructor(name: felt, symbol: felt, initial_supply: u256, recipient: ContractAddress) { + ERC20Library::initializer(name, symbol, initial_supply, recipient); + } + + #[view] + fn name() -> felt { + ERC20Library::name() + } + + #[view] + fn symbol() -> felt { + ERC20Library::symbol() + } + + #[view] + fn decimals() -> u8 { + ERC20Library::decimals() + } + + #[view] + fn totalSupply() -> u256 { + ERC20Library::total_supply() + } + + #[view] + fn balanceOf(account: ContractAddress) -> u256 { + ERC20Library::balance_of(account) + } + + #[view] + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { + ERC20Library::allowance(owner, spender) + } + + #[external] + fn transfer(recipient: ContractAddress, amount: u256) -> bool { + ERC20Library::transfer(recipient, amount) + } + + #[external] + fn transferFrom(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { + ERC20Library::transfer_from(sender, recipient, amount) + } + + #[external] + fn approve(spender: ContractAddress, amount: u256) -> bool { + ERC20Library::approve(spender, amount) + } + + #[external] + fn increaseAllowance(spender: ContractAddress, added_value: u256) -> bool { + ERC20Library::increase_allowance(spender, added_value) + } + + #[external] + fn decreaseAllowance(spender: ContractAddress, subtracted_value: u256) -> bool { + ERC20Library::decrease_allowance(spender, subtracted_value) + } } diff --git a/src/openzeppelin/token/erc20/presets/ERC20Burnable.cairo b/src/openzeppelin/token/erc20/presets/ERC20Burnable.cairo deleted file mode 100644 index 137fde7ce..000000000 --- a/src/openzeppelin/token/erc20/presets/ERC20Burnable.cairo +++ /dev/null @@ -1,118 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Cairo Contracts v0.6.1 (token/erc20/presets/ERC20Burnable.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.uint256 import Uint256 -from starkware.starknet.common.syscalls import get_caller_address - -from openzeppelin.token.erc20.library import ERC20 - -@constructor -func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - name: felt, symbol: felt, decimals: felt, initial_supply: Uint256, recipient: felt -) { - ERC20.initializer(name, symbol, decimals); - ERC20._mint(recipient, initial_supply); - return (); -} - -// -// Getters -// - -@view -func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) { - return ERC20.name(); -} - -@view -func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) { - return ERC20.symbol(); -} - -@view -func totalSupply{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - totalSupply: Uint256 -) { - let (totalSupply: Uint256) = ERC20.total_supply(); - return (totalSupply=totalSupply); -} - -@view -func decimals{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - decimals: felt -) { - return ERC20.decimals(); -} - -@view -func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(account: felt) -> ( - balance: Uint256 -) { - return ERC20.balance_of(account); -} - -@view -func allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - owner: felt, spender: felt -) -> (remaining: Uint256) { - return ERC20.allowance(owner, spender); -} - -// -// External -// - -@external -func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - recipient: felt, amount: Uint256 -) -> (success: felt) { - return ERC20.transfer(recipient, amount); -} - -@external -func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - sender: felt, recipient: felt, amount: Uint256 -) -> (success: felt) { - return ERC20.transfer_from(sender, recipient, amount); -} - -@external -func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - spender: felt, amount: Uint256 -) -> (success: felt) { - return ERC20.approve(spender, amount); -} - -@external -func increaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - spender: felt, added_value: Uint256 -) -> (success: felt) { - return ERC20.increase_allowance(spender, added_value); -} - -@external -func decreaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - spender: felt, subtracted_value: Uint256 -) -> (success: felt) { - return ERC20.decrease_allowance(spender, subtracted_value); -} - -@external -func burn{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(amount: Uint256) { - let (caller) = get_caller_address(); - ERC20._burn(caller, amount); - return (); -} - -@external -func burnFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - account: felt, amount: Uint256 -) { - let (caller) = get_caller_address(); - ERC20._spend_allowance(account, caller, amount); - ERC20._burn(account, amount); - return (); -} diff --git a/src/openzeppelin/token/erc20/presets/ERC20Mintable.cairo b/src/openzeppelin/token/erc20/presets/ERC20Mintable.cairo deleted file mode 100644 index f9e43843f..000000000 --- a/src/openzeppelin/token/erc20/presets/ERC20Mintable.cairo +++ /dev/null @@ -1,130 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc20/presets/ERC20Mintable.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.uint256 import Uint256 - -from openzeppelin.access.ownable.library import Ownable -from openzeppelin.token.erc20.library import ERC20 - -@constructor -func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - name: felt, symbol: felt, decimals: felt, initial_supply: Uint256, recipient: felt, owner: felt -) { - ERC20.initializer(name, symbol, decimals); - ERC20._mint(recipient, initial_supply); - Ownable.initializer(owner); - return (); -} - -// -// Getters -// - -@view -func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) { - return ERC20.name(); -} - -@view -func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) { - return ERC20.symbol(); -} - -@view -func totalSupply{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - totalSupply: Uint256 -) { - let (totalSupply: Uint256) = ERC20.total_supply(); - return (totalSupply=totalSupply); -} - -@view -func decimals{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - decimals: felt -) { - return ERC20.decimals(); -} - -@view -func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(account: felt) -> ( - balance: Uint256 -) { - return ERC20.balance_of(account); -} - -@view -func allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - owner: felt, spender: felt -) -> (remaining: Uint256) { - return ERC20.allowance(owner, spender); -} - -@view -func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) { - return Ownable.owner(); -} - -// -// Externals -// - -@external -func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - recipient: felt, amount: Uint256 -) -> (success: felt) { - return ERC20.transfer(recipient, amount); -} - -@external -func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - sender: felt, recipient: felt, amount: Uint256 -) -> (success: felt) { - return ERC20.transfer_from(sender, recipient, amount); -} - -@external -func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - spender: felt, amount: Uint256 -) -> (success: felt) { - return ERC20.approve(spender, amount); -} - -@external -func increaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - spender: felt, added_value: Uint256 -) -> (success: felt) { - return ERC20.increase_allowance(spender, added_value); -} - -@external -func decreaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - spender: felt, subtracted_value: Uint256 -) -> (success: felt) { - return ERC20.decrease_allowance(spender, subtracted_value); -} - -@external -func mint{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - to: felt, amount: Uint256 -) { - Ownable.assert_only_owner(); - ERC20._mint(to, amount); - return (); -} - -@external -func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - newOwner: felt -) { - Ownable.transfer_ownership(newOwner); - return (); -} - -@external -func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - Ownable.renounce_ownership(); - return (); -} diff --git a/src/openzeppelin/token/erc20/presets/ERC20Pausable.cairo b/src/openzeppelin/token/erc20/presets/ERC20Pausable.cairo deleted file mode 100644 index 7519902fe..000000000 --- a/src/openzeppelin/token/erc20/presets/ERC20Pausable.cairo +++ /dev/null @@ -1,146 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc20/presets/ERC20Pausable.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.uint256 import Uint256 - -from openzeppelin.access.ownable.library import Ownable -from openzeppelin.security.pausable.library import Pausable -from openzeppelin.token.erc20.library import ERC20 - -@constructor -func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - name: felt, symbol: felt, decimals: felt, initial_supply: Uint256, recipient: felt, owner: felt -) { - ERC20.initializer(name, symbol, decimals); - ERC20._mint(recipient, initial_supply); - Ownable.initializer(owner); - return (); -} - -// -// Getters -// - -@view -func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) { - return ERC20.name(); -} - -@view -func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) { - return ERC20.symbol(); -} - -@view -func totalSupply{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - totalSupply: Uint256 -) { - let (totalSupply: Uint256) = ERC20.total_supply(); - return (totalSupply=totalSupply); -} - -@view -func decimals{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - decimals: felt -) { - return ERC20.decimals(); -} - -@view -func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(account: felt) -> ( - balance: Uint256 -) { - return ERC20.balance_of(account); -} - -@view -func allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - owner: felt, spender: felt -) -> (remaining: Uint256) { - return ERC20.allowance(owner, spender); -} - -@view -func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) { - return Ownable.owner(); -} - -@view -func paused{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (paused: felt) { - return Pausable.is_paused(); -} - -// -// Externals -// - -@external -func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - recipient: felt, amount: Uint256 -) -> (success: felt) { - Pausable.assert_not_paused(); - return ERC20.transfer(recipient, amount); -} - -@external -func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - sender: felt, recipient: felt, amount: Uint256 -) -> (success: felt) { - Pausable.assert_not_paused(); - return ERC20.transfer_from(sender, recipient, amount); -} - -@external -func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - spender: felt, amount: Uint256 -) -> (success: felt) { - Pausable.assert_not_paused(); - return ERC20.approve(spender, amount); -} - -@external -func increaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - spender: felt, added_value: Uint256 -) -> (success: felt) { - Pausable.assert_not_paused(); - return ERC20.increase_allowance(spender, added_value); -} - -@external -func decreaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - spender: felt, subtracted_value: Uint256 -) -> (success: felt) { - Pausable.assert_not_paused(); - return ERC20.decrease_allowance(spender, subtracted_value); -} - -@external -func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - newOwner: felt -) { - Ownable.transfer_ownership(newOwner); - return (); -} - -@external -func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - Ownable.renounce_ownership(); - return (); -} - -@external -func pause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - Ownable.assert_only_owner(); - Pausable._pause(); - return (); -} - -@external -func unpause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - Ownable.assert_only_owner(); - Pausable._unpause(); - return (); -} diff --git a/src/openzeppelin/token/erc20/presets/ERC20Upgradeable.cairo b/src/openzeppelin/token/erc20/presets/ERC20Upgradeable.cairo deleted file mode 100644 index 3244ba0a5..000000000 --- a/src/openzeppelin/token/erc20/presets/ERC20Upgradeable.cairo +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc20/presets/ERC20Upgradeable.cairo) - -%lang starknet -%builtins pedersen range_check - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.uint256 import Uint256 - -from openzeppelin.token.erc20.library import ERC20 -from openzeppelin.upgrades.library import Proxy - -// -// Initializer -// - -@external -func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - name: felt, - symbol: felt, - decimals: felt, - initial_supply: Uint256, - recipient: felt, - proxy_admin: felt, -) { - ERC20.initializer(name, symbol, decimals); - ERC20._mint(recipient, initial_supply); - Proxy.initializer(proxy_admin); - return (); -} - -@external -func upgrade{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - new_implementation: felt -) { - Proxy.assert_only_admin(); - Proxy._set_implementation_hash(new_implementation); - return (); -} - -// -// Getters -// - -@view -func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) { - return ERC20.name(); -} - -@view -func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) { - return ERC20.symbol(); -} - -@view -func totalSupply{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - totalSupply: Uint256 -) { - let (totalSupply: Uint256) = ERC20.total_supply(); - return (totalSupply=totalSupply); -} - -@view -func decimals{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - decimals: felt -) { - return ERC20.decimals(); -} - -@view -func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(account: felt) -> ( - balance: Uint256 -) { - return ERC20.balance_of(account); -} - -@view -func allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - owner: felt, spender: felt -) -> (remaining: Uint256) { - return ERC20.allowance(owner, spender); -} - -// -// Externals -// - -@external -func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - recipient: felt, amount: Uint256 -) -> (success: felt) { - return ERC20.transfer(recipient, amount); -} - -@external -func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - sender: felt, recipient: felt, amount: Uint256 -) -> (success: felt) { - return ERC20.transfer_from(sender, recipient, amount); -} - -@external -func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - spender: felt, amount: Uint256 -) -> (success: felt) { - return ERC20.approve(spender, amount); -} - -@external -func increaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - spender: felt, added_value: Uint256 -) -> (success: felt) { - return ERC20.increase_allowance(spender, added_value); -} - -@external -func decreaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - spender: felt, subtracted_value: Uint256 -) -> (success: felt) { - return ERC20.decrease_allowance(spender, subtracted_value); -} diff --git a/src/openzeppelin/token/erc20/presets/cairo_project.toml b/src/openzeppelin/token/erc20/presets/cairo_project.toml new file mode 100644 index 000000000..2f96b70fe --- /dev/null +++ b/src/openzeppelin/token/erc20/presets/cairo_project.toml @@ -0,0 +1,2 @@ +[crate_roots] +presets = "." diff --git a/src/openzeppelin/token/erc20/presets/erc20_burnable.cairo b/src/openzeppelin/token/erc20/presets/erc20_burnable.cairo new file mode 100644 index 000000000..cd73016a3 --- /dev/null +++ b/src/openzeppelin/token/erc20/presets/erc20_burnable.cairo @@ -0,0 +1,85 @@ +#[contract] +mod ERC20Burnable { + use erc20::ERC20Library; + use starknet::get_caller_address; + + // TMP starknet testing isn't fully functional. + // Use to ensure paths are correctly set. + #[external] + fn mock_constructor(name: felt, symbol: felt) { + ERC20Library::mock_initializer(name, symbol); + } + + #[constructor] + fn constructor(name: felt, symbol: felt, initial_supply: u256, recipient: ContractAddress) { + ERC20Library::initializer(name, symbol, initial_supply, recipient); + } + + #[view] + fn name() -> felt { + ERC20Library::name() + } + + #[view] + fn symbol() -> felt { + ERC20Library::symbol() + } + + #[view] + fn decimals() -> u8 { + ERC20Library::decimals() + } + + #[view] + fn totalSupply() -> u256 { + ERC20Library::total_supply() + } + + #[view] + fn balanceOf(account: ContractAddress) -> u256 { + ERC20Library::balance_of(account) + } + + #[view] + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { + ERC20Library::allowance(owner, spender) + } + + #[external] + fn transfer(recipient: ContractAddress, amount: u256) -> bool { + ERC20Library::transfer(recipient, amount) + } + + #[external] + fn transferFrom(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { + ERC20Library::transfer_from(sender, recipient, amount) + } + + #[external] + fn approve(spender: ContractAddress, amount: u256) -> bool { + ERC20Library::approve(spender, amount) + } + + #[external] + fn increaseAllowance(spender: ContractAddress, added_value: u256) -> bool { + ERC20Library::increase_allowance(spender, added_value) + } + + #[external] + fn decreaseAllowance(spender: ContractAddress, subtracted_value: u256) -> bool { + ERC20Library::decrease_allowance(spender, subtracted_value) + } + + #[external] + fn burn(amount: u256) { + let caller = get_caller_address(); + ERC20Library::_burn(caller, amount); + } + + #[external] + fn burnFrom(account: ContractAddress, amount: u256) { + let caller = get_caller_address(); + ERC20Library::_spend_allowance(account, caller, amount); + ERC20Library::_burn(account, amount); + } +} diff --git a/src/openzeppelin/token/erc20/presets/erc20_mintable.cairo b/src/openzeppelin/token/erc20/presets/erc20_mintable.cairo new file mode 100644 index 000000000..0e176db91 --- /dev/null +++ b/src/openzeppelin/token/erc20/presets/erc20_mintable.cairo @@ -0,0 +1,76 @@ +#[contract] +mod ERC20Mintable { + use erc20::ERC20Library; + + // TMP starknet testing isn't fully functional. + // Use to ensure paths are correctly set. + #[external] + fn mock_constructor(name: felt, symbol: felt) { + ERC20Library::mock_initializer(name, symbol); + } + + #[constructor] + fn constructor(name: felt, symbol: felt, initial_supply: u256, recipient: ContractAddress) { + ERC20Library::initializer(name, symbol, initial_supply, recipient); + } + + #[view] + fn name() -> felt { + ERC20Library::name() + } + + #[view] + fn symbol() -> felt { + ERC20Library::symbol() + } + + #[view] + fn decimals() -> u8 { + ERC20Library::decimals() + } + + #[view] + fn totalSupply() -> u256 { + ERC20Library::total_supply() + } + + #[view] + fn balanceOf(account: ContractAddress) -> u256 { + ERC20Library::balance_of(account) + } + + #[view] + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { + ERC20Library::allowance(owner, spender) + } + + #[external] + fn transfer(recipient: ContractAddress, amount: u256) -> bool { + ERC20Library::transfer(recipient, amount) + } + + #[external] + fn transferFrom(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { + ERC20Library::transfer_from(sender, recipient, amount) + } + + #[external] + fn approve(spender: ContractAddress, amount: u256) -> bool { + ERC20Library::approve(spender, amount) + } + + #[external] + fn increaseAllowance(spender: ContractAddress, added_value: u256) -> bool { + ERC20Library::increase_allowance(spender, added_value) + } + + #[external] + fn decreaseAllowance(spender: ContractAddress, subtracted_value: u256) -> bool { + ERC20Library::decrease_allowance(spender, subtracted_value) + } + + #[external] + fn mint(recipient: ContractAddress, amount: u256) { + ERC20Library::_mint(recipient, amount); + } +} diff --git a/src/openzeppelin/token/erc20/presets/lib.cairo b/src/openzeppelin/token/erc20/presets/lib.cairo new file mode 100644 index 000000000..31d88e007 --- /dev/null +++ b/src/openzeppelin/token/erc20/presets/lib.cairo @@ -0,0 +1,8 @@ +mod erc20; +use erc20::ERC20; + +mod erc20_mintable; +use erc20_mintable::ERC20Mintable; + +mod erc20_burnable; +use erc20_burnable::ERC20Burnable; From 2fa00ddd16d3bcbe4413e32f7338fffd33c5ee10 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 18 Feb 2023 10:02:31 -0500 Subject: [PATCH 008/246] add base lib --- src/openzeppelin/token/erc20/erc20.cairo | 128 +++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/openzeppelin/token/erc20/erc20.cairo diff --git a/src/openzeppelin/token/erc20/erc20.cairo b/src/openzeppelin/token/erc20/erc20.cairo new file mode 100644 index 000000000..e642211a5 --- /dev/null +++ b/src/openzeppelin/token/erc20/erc20.cairo @@ -0,0 +1,128 @@ +#[contract] +mod ERC20Library { + use starknet::get_caller_address; + use starknet::contract_address_const; + use starknet::ContractAddressZeroable; + use zeroable::Zeroable; + + struct Storage { + _name: felt, + _symbol: felt, + _total_supply: u256, + _balances: LegacyMap::, + _allowances: LegacyMap::<(ContractAddress, ContractAddress), u256>, + } + + #[event] + fn Transfer(from: ContractAddress, to: ContractAddress, value: u256) {} + + #[event] + fn Approval(owner: ContractAddress, spender: ContractAddress, value: u256) {} + + // TMP starknet testing isn't fully functional. + // Use to ensure paths are correctly set. + fn mock_initializer(name_: felt, symbol_: felt) { + _name::write(name_); + _symbol::write(symbol_); + } + + fn initializer(name_: felt, symbol_: felt, initial_supply: u256, recipient: ContractAddress) { + _name::write(name_); + _symbol::write(symbol_); + _mint(recipient, initial_supply); + } + + fn name() -> felt { + _name::read() + } + + fn symbol() -> felt { + _symbol::read() + } + + fn decimals() -> u8 { + 18_u8 + } + + fn total_supply() -> u256 { + _total_supply::read() + } + + fn balance_of(account: ContractAddress) -> u256 { + _balances::read(account) + } + + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { + _allowances::read((owner, spender)) + } + + fn transfer(recipient: ContractAddress, amount: u256) -> bool { + let sender = get_caller_address(); + _transfer(sender, recipient, amount); + true + } + + fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { + let caller = get_caller_address(); + _spend_allowance(sender, caller, amount); + _transfer(sender, recipient, amount); + true + } + + fn approve(spender: ContractAddress, amount: u256) -> bool { + let caller = get_caller_address(); + _approve(caller, spender, amount); + true + } + + fn increase_allowance(spender: ContractAddress, added_value: u256) -> bool { + let caller = get_caller_address(); + _approve(caller, spender, _allowances::read((caller, spender)) + added_value); + true + } + + fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { + let caller = get_caller_address(); + _approve(caller, spender, _allowances::read((caller, spender)) - subtracted_value); + true + } + + fn _mint(recipient: ContractAddress, amount: u256) { + assert(!recipient.is_zero(), 'ERC20: mint to 0'); + _total_supply::write(_total_supply::read() + amount); + _balances::write(recipient, _balances::read(recipient) + amount); + Transfer(contract_address_const::<0>(), recipient, amount); + } + + fn _burn(account: ContractAddress, amount: u256) { + assert(!account.is_zero(), 'ERC20: burn from 0'); + _total_supply::write(_total_supply::read() - amount); + _balances::write(account, _balances::read(account) - amount); + Transfer(account, contract_address_const::<0>(), amount); + } + + fn _approve(owner: ContractAddress, spender: ContractAddress, amount: u256) { + assert(!owner.is_zero(), 'ERC20: approve from 0'); + assert(!spender.is_zero(), 'ERC20: approve to 0'); + _allowances::write((owner, spender), amount); + Approval(owner, spender, amount); + } + + fn _transfer(sender: ContractAddress, recipient: ContractAddress, amount: u256) { + assert(!sender.is_zero(), 'ERC20: transfer from 0'); + assert(!recipient.is_zero(), 'ERC20: transfer to 0'); + _balances::write(sender, _balances::read(sender) - amount); + _balances::write(recipient, _balances::read(recipient) + amount); + Transfer(sender, recipient, amount); + } + + fn _spend_allowance(owner: ContractAddress, spender: ContractAddress, amount: u256) { + let current_allowance = _allowances::read((owner, spender)); + let ONES_MASK = 0xffffffffffffffffffffffffffffffff_u128; + let is_unlimited_allowance = + current_allowance.low == ONES_MASK & current_allowance.high == ONES_MASK; + if !is_unlimited_allowance { + _approve(owner, spender, current_allowance - amount); + } + } +} From f0036e7da1a03f0a3a80811447e72cb5b84ffcda Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 18 Feb 2023 10:03:01 -0500 Subject: [PATCH 009/246] add tests --- src/openzeppelin/token/erc20/tests.cairo | 4 + .../token/erc20/tests/test_erc20.cairo | 18 ++ .../erc20/tests/test_erc20_burnable.cairo | 18 ++ .../erc20/tests/test_erc20_library.cairo | 189 ++++++++++++++++++ .../erc20/tests/test_erc20_mintable.cairo | 18 ++ 5 files changed, 247 insertions(+) create mode 100644 src/openzeppelin/token/erc20/tests.cairo create mode 100644 src/openzeppelin/token/erc20/tests/test_erc20.cairo create mode 100644 src/openzeppelin/token/erc20/tests/test_erc20_burnable.cairo create mode 100644 src/openzeppelin/token/erc20/tests/test_erc20_library.cairo create mode 100644 src/openzeppelin/token/erc20/tests/test_erc20_mintable.cairo diff --git a/src/openzeppelin/token/erc20/tests.cairo b/src/openzeppelin/token/erc20/tests.cairo new file mode 100644 index 000000000..13e522153 --- /dev/null +++ b/src/openzeppelin/token/erc20/tests.cairo @@ -0,0 +1,4 @@ +mod test_erc20_library; +mod test_erc20; +mod test_erc20_mintable; +mod test_erc20_burnable; diff --git a/src/openzeppelin/token/erc20/tests/test_erc20.cairo b/src/openzeppelin/token/erc20/tests/test_erc20.cairo new file mode 100644 index 000000000..330f08c83 --- /dev/null +++ b/src/openzeppelin/token/erc20/tests/test_erc20.cairo @@ -0,0 +1,18 @@ +use presets::ERC20; +use starknet::contract_address_const; +use integer::u256_from_felt; + +const NAME: felt = 111; +const SYMBOL: felt = 222; + +#[test] +#[available_gas(2000000)] +fn initialize() { + let decimals: u8 = 18_u8; + + ERC20::mock_constructor(NAME, SYMBOL); + + assert(ERC20::name() == NAME, 'Name should be NAME'); + assert(ERC20::symbol() == SYMBOL, 'Symbol should be SYMBOL'); + assert(ERC20::decimals() == decimals, 'Decimals should be 18'); +} diff --git a/src/openzeppelin/token/erc20/tests/test_erc20_burnable.cairo b/src/openzeppelin/token/erc20/tests/test_erc20_burnable.cairo new file mode 100644 index 000000000..0b64408ad --- /dev/null +++ b/src/openzeppelin/token/erc20/tests/test_erc20_burnable.cairo @@ -0,0 +1,18 @@ +use presets::ERC20Burnable; +use starknet::contract_address_const; +use integer::u256_from_felt; + +const NAME: felt = 111; +const SYMBOL: felt = 222; + +#[test] +#[available_gas(2000000)] +fn initialize() { + let decimals: u8 = 18_u8; + + ERC20Burnable::mock_constructor(NAME, SYMBOL); + + assert(ERC20Burnable::name() == NAME, 'Name should be NAME'); + assert(ERC20Burnable::symbol() == SYMBOL, 'Symbol should be SYMBOL'); + assert(ERC20Burnable::decimals() == decimals, 'Decimals should be 18'); +} diff --git a/src/openzeppelin/token/erc20/tests/test_erc20_library.cairo b/src/openzeppelin/token/erc20/tests/test_erc20_library.cairo new file mode 100644 index 000000000..d0f67ecc8 --- /dev/null +++ b/src/openzeppelin/token/erc20/tests/test_erc20_library.cairo @@ -0,0 +1,189 @@ +/// Tests only the internal functions of the erc20 library because +/// `get_caller_address` is not yet functional with accounts. +/// +/// Events are not yet recognized in starknet tests. +/// Some of these tests will panic with 'Unknown selector for system call!' +/// Comment out events in erc20/erc20.cairo to omit panicking. + +use erc20::ERC20Library; +use starknet::contract_address_const; +use integer::u256_from_felt; + +const NAME: felt = 111; +const SYMBOL: felt = 222; + +fn setup() -> (ContractAddress, u256) { + let account: ContractAddress = contract_address_const::<1>(); + let initial_supply: u256 = u256_from_felt(2000); + + ERC20Library::mock_initializer(NAME, SYMBOL); + ERC20Library::_total_supply::write(initial_supply); + ERC20Library::_balances::write(account, initial_supply); + (account, initial_supply) +} + +#[test] +#[available_gas(2000000)] +fn initialize() { + let decimals: u8 = 18_u8; + + ERC20Library::mock_initializer(NAME, SYMBOL); + + assert(ERC20Library::name() == NAME, 'Name should be NAME'); + assert(ERC20Library::symbol() == SYMBOL, 'Symbol should be SYMBOL'); + assert(ERC20Library::decimals() == decimals, 'Decimals should be 18'); +} + +#[test] +#[available_gas(2000000)] +fn test__approve() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + + ERC20Library::_approve(owner, spender, amount); + assert(ERC20Library::allowance(owner, spender) == amount, 'Spender not approved correctly'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test__approve_from_zero() { + let owner: ContractAddress = contract_address_const::<0>(); + let spender: ContractAddress = contract_address_const::<1>(); + let amount: u256 = u256_from_felt(100); + ERC20Library::_approve(owner, spender, amount); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test__approve_to_zero() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt(100); + ERC20Library::_approve(owner, spender, amount); +} + +#[test] +#[available_gas(2000000)] +fn test__transfer() { + let (account, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + ERC20Library::_transfer(account, recipient, amount); + + assert(ERC20Library::balance_of(recipient) == amount, 'Balance should eq amount'); + assert(ERC20Library::balance_of(account) == supply - amount, 'Should eq supply - amount'); + assert(ERC20Library::total_supply() == supply, 'Total supply should not change'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test__transfer_not_enough_balance() { + let (account, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<2>(); + let amount: u256 = supply + u256_from_felt(1); + ERC20Library::_transfer(account, recipient, amount); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test__transfer_from_zero() { + let owner: ContractAddress = contract_address_const::<0>(); + let spender: ContractAddress = contract_address_const::<1>(); + let amount: u256 = u256_from_felt(100); + ERC20Library::_transfer(owner, spender, amount); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test__transfer_to_zero() { + let (account, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt(100); + ERC20Library::_transfer(account, spender, amount); +} + +#[test] +#[available_gas(2000000)] +fn test__spend_allowance_not_unlimited() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + + ERC20Library::_approve(owner, spender, supply); + ERC20Library::_spend_allowance(owner, spender, amount); + assert(ERC20Library::allowance(owner, spender) == supply - amount, 'Should eq supply - amount'); +} + +#[test] +#[available_gas(2000000)] +fn test__spend_allowance_unlimited() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<2>(); + + let max_u256: u256 = u256_from_felt(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) * u256_from_felt(256) + u256_from_felt(255); + let max_minus_one: u256 = max_u256 - u256_from_felt(1); + + ERC20Library::_approve(owner, spender, max_u256); + ERC20Library::_spend_allowance(owner, spender, max_minus_one); + + assert(ERC20Library::allowance(owner, spender) == max_u256, 'Allowance should not change'); +} + +#[test] +#[available_gas(2000000)] +fn test__mint() { + let minter: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + + ERC20Library::_mint(minter, amount); + + assert(ERC20Library::total_supply() == amount, 'Should eq total supply'); + // assert(ERC20Library::balance_of(minter) == amount, 'Should eq amount'); + // Causes 'Error: Failed setting up runner' +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test__mint_to_zero() { + let minter: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt(100); + + ERC20Library::_mint(minter, amount); +} + +#[test] +#[available_gas(2000000)] +fn test__burn() { + let (owner, supply) = setup(); + + let amount: u256 = u256_from_felt(100); + ERC20Library::_burn(owner, amount); + + assert(ERC20Library::total_supply() == supply - amount, 'Should eq supply - amount'); + assert(ERC20Library::balance_of(owner) == supply - amount, 'Should eq supply - amount'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test__burn_from_zero() { + setup(); + let zero_address: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt(100); + + ERC20Library::_burn(zero_address, amount); +} diff --git a/src/openzeppelin/token/erc20/tests/test_erc20_mintable.cairo b/src/openzeppelin/token/erc20/tests/test_erc20_mintable.cairo new file mode 100644 index 000000000..2c2a07e2e --- /dev/null +++ b/src/openzeppelin/token/erc20/tests/test_erc20_mintable.cairo @@ -0,0 +1,18 @@ +use presets::ERC20Mintable; +use starknet::contract_address_const; +use integer::u256_from_felt; + +const NAME: felt = 111; +const SYMBOL: felt = 222; + +#[test] +#[available_gas(2000000)] +fn initialize() { + let decimals: u8 = 18_u8; + + ERC20Mintable::mock_constructor(NAME, SYMBOL); + + assert(ERC20Mintable::name() == NAME, 'Name should be NAME'); + assert(ERC20Mintable::symbol() == SYMBOL, 'Symbol should be SYMBOL'); + assert(ERC20Mintable::decimals() == decimals, 'Decimals should be 18'); +} From 8d781a859aaa9d7fcd3aa4d20ca1116c753991ff Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 18 Feb 2023 10:03:32 -0500 Subject: [PATCH 010/246] remove old cairo lib and interface --- src/openzeppelin/token/erc20/IERC20.cairo | 36 --- src/openzeppelin/token/erc20/library.cairo | 306 --------------------- 2 files changed, 342 deletions(-) delete mode 100644 src/openzeppelin/token/erc20/IERC20.cairo delete mode 100644 src/openzeppelin/token/erc20/library.cairo diff --git a/src/openzeppelin/token/erc20/IERC20.cairo b/src/openzeppelin/token/erc20/IERC20.cairo deleted file mode 100644 index 3c8cf7018..000000000 --- a/src/openzeppelin/token/erc20/IERC20.cairo +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc20/IERC20.cairo) - -%lang starknet - -from starkware.cairo.common.uint256 import Uint256 - -@contract_interface -namespace IERC20 { - func name() -> (name: felt) { - } - - func symbol() -> (symbol: felt) { - } - - func decimals() -> (decimals: felt) { - } - - func totalSupply() -> (totalSupply: Uint256) { - } - - func balanceOf(account: felt) -> (balance: Uint256) { - } - - func allowance(owner: felt, spender: felt) -> (remaining: Uint256) { - } - - func transfer(recipient: felt, amount: Uint256) -> (success: felt) { - } - - func transferFrom(sender: felt, recipient: felt, amount: Uint256) -> (success: felt) { - } - - func approve(spender: felt, amount: Uint256) -> (success: felt) { - } -} diff --git a/src/openzeppelin/token/erc20/library.cairo b/src/openzeppelin/token/erc20/library.cairo deleted file mode 100644 index 3b89cd7fd..000000000 --- a/src/openzeppelin/token/erc20/library.cairo +++ /dev/null @@ -1,306 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc20/library.cairo) - -%lang starknet - -from starkware.starknet.common.syscalls import get_caller_address -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.math import assert_not_zero, assert_le -from starkware.cairo.common.bool import TRUE, FALSE -from starkware.cairo.common.uint256 import Uint256, uint256_check, uint256_eq, uint256_not - -from openzeppelin.security.safemath.library import SafeUint256 -from openzeppelin.utils.constants.library import UINT8_MAX - -// -// Events -// - -@event -func Transfer(from_: felt, to: felt, value: Uint256) { -} - -@event -func Approval(owner: felt, spender: felt, value: Uint256) { -} - -// -// Storage -// - -@storage_var -func ERC20_name() -> (name: felt) { -} - -@storage_var -func ERC20_symbol() -> (symbol: felt) { -} - -@storage_var -func ERC20_decimals() -> (decimals: felt) { -} - -@storage_var -func ERC20_total_supply() -> (total_supply: Uint256) { -} - -@storage_var -func ERC20_balances(account: felt) -> (balance: Uint256) { -} - -@storage_var -func ERC20_allowances(owner: felt, spender: felt) -> (remaining: Uint256) { -} - -namespace ERC20 { - // - // Initializer - // - - func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - name: felt, symbol: felt, decimals: felt - ) { - ERC20_name.write(name); - ERC20_symbol.write(symbol); - with_attr error_message("ERC20: decimals exceed 2^8") { - assert_le(decimals, UINT8_MAX); - } - ERC20_decimals.write(decimals); - return (); - } - - // - // Public functions - // - - func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) { - return ERC20_name.read(); - } - - func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - symbol: felt - ) { - return ERC20_symbol.read(); - } - - func total_supply{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - total_supply: Uint256 - ) { - return ERC20_total_supply.read(); - } - - func decimals{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - decimals: felt - ) { - return ERC20_decimals.read(); - } - - func balance_of{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - account: felt - ) -> (balance: Uint256) { - return ERC20_balances.read(account); - } - - func allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - owner: felt, spender: felt - ) -> (remaining: Uint256) { - return ERC20_allowances.read(owner, spender); - } - - func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - recipient: felt, amount: Uint256 - ) -> (success: felt) { - let (sender) = get_caller_address(); - _transfer(sender, recipient, amount); - return (success=TRUE); - } - - func transfer_from{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - sender: felt, recipient: felt, amount: Uint256 - ) -> (success: felt) { - let (caller) = get_caller_address(); - _spend_allowance(sender, caller, amount); - _transfer(sender, recipient, amount); - return (success=TRUE); - } - - func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - spender: felt, amount: Uint256 - ) -> (success: felt) { - with_attr error_message("ERC20: amount is not a valid Uint256") { - uint256_check(amount); - } - - let (caller) = get_caller_address(); - _approve(caller, spender, amount); - return (success=TRUE); - } - - func increase_allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - spender: felt, added_value: Uint256 - ) -> (success: felt) { - with_attr error("ERC20: added_value is not a valid Uint256") { - uint256_check(added_value); - } - - let (caller) = get_caller_address(); - let (current_allowance: Uint256) = ERC20_allowances.read(caller, spender); - - // add allowance - with_attr error_message("ERC20: allowance overflow") { - let (new_allowance: Uint256) = SafeUint256.add(current_allowance, added_value); - } - - _approve(caller, spender, new_allowance); - return (success=TRUE); - } - - func decrease_allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - spender: felt, subtracted_value: Uint256 - ) -> (success: felt) { - alloc_locals; - with_attr error_message("ERC20: subtracted_value is not a valid Uint256") { - uint256_check(subtracted_value); - } - - let (caller) = get_caller_address(); - let (current_allowance: Uint256) = ERC20_allowances.read(owner=caller, spender=spender); - - with_attr error_message("ERC20: allowance below zero") { - let (new_allowance: Uint256) = SafeUint256.sub_le(current_allowance, subtracted_value); - } - - _approve(caller, spender, new_allowance); - return (success=TRUE); - } - - // - // Internal - // - - func _mint{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - recipient: felt, amount: Uint256 - ) { - with_attr error_message("ERC20: amount is not a valid Uint256") { - uint256_check(amount); - } - - with_attr error_message("ERC20: cannot mint to the zero address") { - assert_not_zero(recipient); - } - - let (supply: Uint256) = ERC20_total_supply.read(); - with_attr error_message("ERC20: mint overflow") { - let (new_supply: Uint256) = SafeUint256.add(supply, amount); - } - ERC20_total_supply.write(new_supply); - - let (balance: Uint256) = ERC20_balances.read(account=recipient); - // overflow is not possible because sum is guaranteed to be less than total supply - // which we check for overflow below - let (new_balance: Uint256) = SafeUint256.add(balance, amount); - ERC20_balances.write(recipient, new_balance); - - Transfer.emit(0, recipient, amount); - return (); - } - - func _burn{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - account: felt, amount: Uint256 - ) { - with_attr error_message("ERC20: amount is not a valid Uint256") { - uint256_check(amount); - } - - with_attr error_message("ERC20: cannot burn from the zero address") { - assert_not_zero(account); - } - - let (balance: Uint256) = ERC20_balances.read(account); - with_attr error_message("ERC20: burn amount exceeds balance") { - let (new_balance: Uint256) = SafeUint256.sub_le(balance, amount); - } - - ERC20_balances.write(account, new_balance); - - let (supply: Uint256) = ERC20_total_supply.read(); - let (new_supply: Uint256) = SafeUint256.sub_le(supply, amount); - ERC20_total_supply.write(new_supply); - Transfer.emit(account, 0, amount); - return (); - } - - func _transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - sender: felt, recipient: felt, amount: Uint256 - ) { - with_attr error_message("ERC20: amount is not a valid Uint256") { - uint256_check(amount); // almost surely not needed, might remove after confirmation - } - - with_attr error_message("ERC20: cannot transfer from the zero address") { - assert_not_zero(sender); - } - - with_attr error_message("ERC20: cannot transfer to the zero address") { - assert_not_zero(recipient); - } - - let (sender_balance: Uint256) = ERC20_balances.read(account=sender); - with_attr error_message("ERC20: transfer amount exceeds balance") { - let (new_sender_balance: Uint256) = SafeUint256.sub_le(sender_balance, amount); - } - - ERC20_balances.write(sender, new_sender_balance); - - // add to recipient - let (recipient_balance: Uint256) = ERC20_balances.read(account=recipient); - // overflow is not possible because sum is guaranteed by mint to be less than total supply - let (new_recipient_balance: Uint256) = SafeUint256.add(recipient_balance, amount); - ERC20_balances.write(recipient, new_recipient_balance); - Transfer.emit(sender, recipient, amount); - return (); - } - - func _approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - owner: felt, spender: felt, amount: Uint256 - ) { - with_attr error_message("ERC20: amount is not a valid Uint256") { - uint256_check(amount); - } - - with_attr error_message("ERC20: cannot approve from the zero address") { - assert_not_zero(owner); - } - - with_attr error_message("ERC20: cannot approve to the zero address") { - assert_not_zero(spender); - } - - ERC20_allowances.write(owner, spender, amount); - Approval.emit(owner, spender, amount); - return (); - } - - func _spend_allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - owner: felt, spender: felt, amount: Uint256 - ) { - alloc_locals; - with_attr error_message("ERC20: amount is not a valid Uint256") { - uint256_check(amount); // almost surely not needed, might remove after confirmation - } - - let (current_allowance: Uint256) = ERC20_allowances.read(owner, spender); - let (infinite: Uint256) = uint256_not(Uint256(0, 0)); - let (is_infinite: felt) = uint256_eq(current_allowance, infinite); - - if (is_infinite == FALSE) { - with_attr error_message("ERC20: insufficient allowance") { - let (new_allowance: Uint256) = SafeUint256.sub_le(current_allowance, amount); - } - - _approve(owner, spender, new_allowance); - return (); - } - return (); - } -} From ae37b1cece52edeb96798da3006eacb17df8bd92 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 18 Feb 2023 11:34:07 -0500 Subject: [PATCH 011/246] remove unused import --- src/openzeppelin/token/erc20/tests/test_erc20.cairo | 1 - src/openzeppelin/token/erc20/tests/test_erc20_burnable.cairo | 1 - src/openzeppelin/token/erc20/tests/test_erc20_mintable.cairo | 1 - 3 files changed, 3 deletions(-) diff --git a/src/openzeppelin/token/erc20/tests/test_erc20.cairo b/src/openzeppelin/token/erc20/tests/test_erc20.cairo index 330f08c83..b1458e457 100644 --- a/src/openzeppelin/token/erc20/tests/test_erc20.cairo +++ b/src/openzeppelin/token/erc20/tests/test_erc20.cairo @@ -1,6 +1,5 @@ use presets::ERC20; use starknet::contract_address_const; -use integer::u256_from_felt; const NAME: felt = 111; const SYMBOL: felt = 222; diff --git a/src/openzeppelin/token/erc20/tests/test_erc20_burnable.cairo b/src/openzeppelin/token/erc20/tests/test_erc20_burnable.cairo index 0b64408ad..88dcbc24f 100644 --- a/src/openzeppelin/token/erc20/tests/test_erc20_burnable.cairo +++ b/src/openzeppelin/token/erc20/tests/test_erc20_burnable.cairo @@ -1,6 +1,5 @@ use presets::ERC20Burnable; use starknet::contract_address_const; -use integer::u256_from_felt; const NAME: felt = 111; const SYMBOL: felt = 222; diff --git a/src/openzeppelin/token/erc20/tests/test_erc20_mintable.cairo b/src/openzeppelin/token/erc20/tests/test_erc20_mintable.cairo index 2c2a07e2e..82db86044 100644 --- a/src/openzeppelin/token/erc20/tests/test_erc20_mintable.cairo +++ b/src/openzeppelin/token/erc20/tests/test_erc20_mintable.cairo @@ -1,6 +1,5 @@ use presets::ERC20Mintable; use starknet::contract_address_const; -use integer::u256_from_felt; const NAME: felt = 111; const SYMBOL: felt = 222; From da57a91561fd6b728a2fd45a4b7e6638f44e4217 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 18 Feb 2023 11:34:34 -0500 Subject: [PATCH 012/246] fix vars --- .../erc20/tests/test_erc20_library.cairo | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/openzeppelin/token/erc20/tests/test_erc20_library.cairo b/src/openzeppelin/token/erc20/tests/test_erc20_library.cairo index d0f67ecc8..857f5d7d0 100644 --- a/src/openzeppelin/token/erc20/tests/test_erc20_library.cairo +++ b/src/openzeppelin/token/erc20/tests/test_erc20_library.cairo @@ -70,14 +70,14 @@ fn test__approve_to_zero() { #[test] #[available_gas(2000000)] fn test__transfer() { - let (account, supply) = setup(); + let (sender, supply) = setup(); let recipient: ContractAddress = contract_address_const::<2>(); let amount: u256 = u256_from_felt(100); - ERC20Library::_transfer(account, recipient, amount); + ERC20Library::_transfer(sender, recipient, amount); assert(ERC20Library::balance_of(recipient) == amount, 'Balance should eq amount'); - assert(ERC20Library::balance_of(account) == supply - amount, 'Should eq supply - amount'); + assert(ERC20Library::balance_of(sender) == supply - amount, 'Should eq supply - amount'); assert(ERC20Library::total_supply() == supply, 'Total supply should not change'); } @@ -85,32 +85,32 @@ fn test__transfer() { #[available_gas(2000000)] #[should_panic] fn test__transfer_not_enough_balance() { - let (account, supply) = setup(); + let (sender, supply) = setup(); let recipient: ContractAddress = contract_address_const::<2>(); let amount: u256 = supply + u256_from_felt(1); - ERC20Library::_transfer(account, recipient, amount); + ERC20Library::_transfer(sender, recipient, amount); } #[test] #[available_gas(2000000)] #[should_panic] fn test__transfer_from_zero() { - let owner: ContractAddress = contract_address_const::<0>(); - let spender: ContractAddress = contract_address_const::<1>(); + let sender: ContractAddress = contract_address_const::<0>(); + let recipient: ContractAddress = contract_address_const::<1>(); let amount: u256 = u256_from_felt(100); - ERC20Library::_transfer(owner, spender, amount); + ERC20Library::_transfer(sender, recipient, amount); } #[test] #[available_gas(2000000)] #[should_panic] fn test__transfer_to_zero() { - let (account, supply) = setup(); + let (sender, supply) = setup(); - let spender: ContractAddress = contract_address_const::<0>(); + let recipient: ContractAddress = contract_address_const::<0>(); let amount: u256 = u256_from_felt(100); - ERC20Library::_transfer(account, spender, amount); + ERC20Library::_transfer(sender, recipient, amount); } #[test] From fd42fddf984a0bf22667c6dc94fc2c80f21f8dc1 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 18 Feb 2023 11:45:47 -0500 Subject: [PATCH 013/246] change external funcs to snake case --- src/openzeppelin/token/erc20/presets/ERC20.cairo | 10 +++++----- .../token/erc20/presets/erc20_burnable.cairo | 12 ++++++------ .../token/erc20/presets/erc20_mintable.cairo | 10 +++++----- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/openzeppelin/token/erc20/presets/ERC20.cairo b/src/openzeppelin/token/erc20/presets/ERC20.cairo index d2273854e..6d6ee2eac 100644 --- a/src/openzeppelin/token/erc20/presets/ERC20.cairo +++ b/src/openzeppelin/token/erc20/presets/ERC20.cairo @@ -30,12 +30,12 @@ mod ERC20 { } #[view] - fn totalSupply() -> u256 { + fn total_supply() -> u256 { ERC20Library::total_supply() } #[view] - fn balanceOf(account: ContractAddress) -> u256 { + fn balance_of(account: ContractAddress) -> u256 { ERC20Library::balance_of(account) } @@ -50,7 +50,7 @@ mod ERC20 { } #[external] - fn transferFrom(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { + fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { ERC20Library::transfer_from(sender, recipient, amount) } @@ -60,12 +60,12 @@ mod ERC20 { } #[external] - fn increaseAllowance(spender: ContractAddress, added_value: u256) -> bool { + fn increase_allowance(spender: ContractAddress, added_value: u256) -> bool { ERC20Library::increase_allowance(spender, added_value) } #[external] - fn decreaseAllowance(spender: ContractAddress, subtracted_value: u256) -> bool { + fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { ERC20Library::decrease_allowance(spender, subtracted_value) } } diff --git a/src/openzeppelin/token/erc20/presets/erc20_burnable.cairo b/src/openzeppelin/token/erc20/presets/erc20_burnable.cairo index cd73016a3..644dcdd6f 100644 --- a/src/openzeppelin/token/erc20/presets/erc20_burnable.cairo +++ b/src/openzeppelin/token/erc20/presets/erc20_burnable.cairo @@ -31,12 +31,12 @@ mod ERC20Burnable { } #[view] - fn totalSupply() -> u256 { + fn total_supply() -> u256 { ERC20Library::total_supply() } #[view] - fn balanceOf(account: ContractAddress) -> u256 { + fn balance_of(account: ContractAddress) -> u256 { ERC20Library::balance_of(account) } @@ -51,7 +51,7 @@ mod ERC20Burnable { } #[external] - fn transferFrom(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { + fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { ERC20Library::transfer_from(sender, recipient, amount) } @@ -61,12 +61,12 @@ mod ERC20Burnable { } #[external] - fn increaseAllowance(spender: ContractAddress, added_value: u256) -> bool { + fn increase_allowance(spender: ContractAddress, added_value: u256) -> bool { ERC20Library::increase_allowance(spender, added_value) } #[external] - fn decreaseAllowance(spender: ContractAddress, subtracted_value: u256) -> bool { + fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { ERC20Library::decrease_allowance(spender, subtracted_value) } @@ -77,7 +77,7 @@ mod ERC20Burnable { } #[external] - fn burnFrom(account: ContractAddress, amount: u256) { + fn burn_from(account: ContractAddress, amount: u256) { let caller = get_caller_address(); ERC20Library::_spend_allowance(account, caller, amount); ERC20Library::_burn(account, amount); diff --git a/src/openzeppelin/token/erc20/presets/erc20_mintable.cairo b/src/openzeppelin/token/erc20/presets/erc20_mintable.cairo index 0e176db91..d25661a88 100644 --- a/src/openzeppelin/token/erc20/presets/erc20_mintable.cairo +++ b/src/openzeppelin/token/erc20/presets/erc20_mintable.cairo @@ -30,12 +30,12 @@ mod ERC20Mintable { } #[view] - fn totalSupply() -> u256 { + fn total_supply() -> u256 { ERC20Library::total_supply() } #[view] - fn balanceOf(account: ContractAddress) -> u256 { + fn balance_of(account: ContractAddress) -> u256 { ERC20Library::balance_of(account) } @@ -50,7 +50,7 @@ mod ERC20Mintable { } #[external] - fn transferFrom(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { + fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { ERC20Library::transfer_from(sender, recipient, amount) } @@ -60,12 +60,12 @@ mod ERC20Mintable { } #[external] - fn increaseAllowance(spender: ContractAddress, added_value: u256) -> bool { + fn increase_allowance(spender: ContractAddress, added_value: u256) -> bool { ERC20Library::increase_allowance(spender, added_value) } #[external] - fn decreaseAllowance(spender: ContractAddress, subtracted_value: u256) -> bool { + fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { ERC20Library::decrease_allowance(spender, subtracted_value) } From 1cb12529f284c8d27ec92a3294419a2d79b83e65 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 18 Feb 2023 11:48:30 -0500 Subject: [PATCH 014/246] remove unused import --- src/openzeppelin/token/erc20/tests/test_erc20.cairo | 1 - src/openzeppelin/token/erc20/tests/test_erc20_burnable.cairo | 1 - src/openzeppelin/token/erc20/tests/test_erc20_mintable.cairo | 1 - 3 files changed, 3 deletions(-) diff --git a/src/openzeppelin/token/erc20/tests/test_erc20.cairo b/src/openzeppelin/token/erc20/tests/test_erc20.cairo index b1458e457..7d5b903d5 100644 --- a/src/openzeppelin/token/erc20/tests/test_erc20.cairo +++ b/src/openzeppelin/token/erc20/tests/test_erc20.cairo @@ -1,5 +1,4 @@ use presets::ERC20; -use starknet::contract_address_const; const NAME: felt = 111; const SYMBOL: felt = 222; diff --git a/src/openzeppelin/token/erc20/tests/test_erc20_burnable.cairo b/src/openzeppelin/token/erc20/tests/test_erc20_burnable.cairo index 88dcbc24f..53e2a53ee 100644 --- a/src/openzeppelin/token/erc20/tests/test_erc20_burnable.cairo +++ b/src/openzeppelin/token/erc20/tests/test_erc20_burnable.cairo @@ -1,5 +1,4 @@ use presets::ERC20Burnable; -use starknet::contract_address_const; const NAME: felt = 111; const SYMBOL: felt = 222; diff --git a/src/openzeppelin/token/erc20/tests/test_erc20_mintable.cairo b/src/openzeppelin/token/erc20/tests/test_erc20_mintable.cairo index 82db86044..448bd82dd 100644 --- a/src/openzeppelin/token/erc20/tests/test_erc20_mintable.cairo +++ b/src/openzeppelin/token/erc20/tests/test_erc20_mintable.cairo @@ -1,5 +1,4 @@ use presets::ERC20Mintable; -use starknet::contract_address_const; const NAME: felt = 111; const SYMBOL: felt = 222; From f0330cdad40bfc4d304e30ffe18651f89eb936a2 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 21 Feb 2023 01:02:52 -0500 Subject: [PATCH 015/246] update cairo --- Cargo.lock | 56 +++++++++++++++++++++++++++--------------------------- Cargo.toml | 2 +- cairo | 2 +- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 696ce9451..3dbb84efb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -307,7 +307,7 @@ dependencies = [ [[package]] name = "cairo-lang-casm" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "cairo-lang-utils", "env_logger", @@ -323,7 +323,7 @@ dependencies = [ [[package]] name = "cairo-lang-compiler" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "anyhow", "cairo-lang-defs", @@ -347,7 +347,7 @@ dependencies = [ [[package]] name = "cairo-lang-debug" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "cairo-lang-proc-macros", "cairo-lang-utils", @@ -358,7 +358,7 @@ dependencies = [ [[package]] name = "cairo-lang-defs" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "cairo-lang-debug", "cairo-lang-diagnostics", @@ -379,7 +379,7 @@ dependencies = [ [[package]] name = "cairo-lang-diagnostics" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "cairo-lang-filesystem", "cairo-lang-proc-macros", @@ -394,7 +394,7 @@ dependencies = [ [[package]] name = "cairo-lang-eq-solver" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "cairo-lang-utils", "env_logger", @@ -408,7 +408,7 @@ dependencies = [ [[package]] name = "cairo-lang-filesystem" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "cairo-lang-debug", "cairo-lang-utils", @@ -421,7 +421,7 @@ dependencies = [ [[package]] name = "cairo-lang-formatter" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "anyhow", "cairo-lang-diagnostics", @@ -444,7 +444,7 @@ dependencies = [ [[package]] name = "cairo-lang-language-server" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "cairo-lang-compiler", "cairo-lang-debug", @@ -471,7 +471,7 @@ dependencies = [ [[package]] name = "cairo-lang-lowering" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -499,7 +499,7 @@ dependencies = [ [[package]] name = "cairo-lang-parser" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "cairo-lang-diagnostics", "cairo-lang-filesystem", @@ -520,7 +520,7 @@ dependencies = [ [[package]] name = "cairo-lang-plugins" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -543,7 +543,7 @@ dependencies = [ [[package]] name = "cairo-lang-proc-macros" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "cairo-lang-debug", "quote", @@ -552,7 +552,7 @@ dependencies = [ [[package]] name = "cairo-lang-project" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "cairo-lang-filesystem", "indoc", @@ -565,7 +565,7 @@ dependencies = [ [[package]] name = "cairo-lang-runner" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "anyhow", "ark-ff 0.4.0-alpha.7", @@ -593,7 +593,7 @@ dependencies = [ [[package]] name = "cairo-lang-semantic" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "assert_matches", "cairo-lang-debug", @@ -623,7 +623,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "assert_matches", "bimap", @@ -651,7 +651,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-ap-change" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", @@ -666,7 +666,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-gas" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", @@ -683,7 +683,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-generator" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -714,7 +714,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-to-casm" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "assert_matches", "cairo-felt", @@ -738,7 +738,7 @@ dependencies = [ [[package]] name = "cairo-lang-starknet" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "anyhow", "cairo-lang-compiler", @@ -780,7 +780,7 @@ dependencies = [ [[package]] name = "cairo-lang-syntax" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "cairo-lang-debug", "cairo-lang-filesystem", @@ -794,7 +794,7 @@ dependencies = [ [[package]] name = "cairo-lang-syntax-codegen" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "cairo-lang-utils", "env_logger", @@ -806,7 +806,7 @@ dependencies = [ [[package]] name = "cairo-lang-test-runner" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "anyhow", "cairo-felt", @@ -838,7 +838,7 @@ dependencies = [ [[package]] name = "cairo-lang-test-utils" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "cairo-lang-utils", "env_logger", @@ -849,7 +849,7 @@ dependencies = [ [[package]] name = "cairo-lang-utils" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "chrono", "env_logger", @@ -2664,7 +2664,7 @@ dependencies = [ [[package]] name = "tests" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" dependencies = [ "assert_matches", "cairo-felt", diff --git a/Cargo.toml b/Cargo.toml index 1179081f2..4294fd6aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ members = [ ] [workspace.package] -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" edition = "2021" repository = "https://github.com/starkware-libs/cairo/" license = "Apache-2.0" diff --git a/cairo b/cairo index 7d27aee2c..d297e4721 160000 --- a/cairo +++ b/cairo @@ -1 +1 @@ -Subproject commit 7d27aee2c7832592f3d091d01c3ceb39e45484e1 +Subproject commit d297e472133680481068cba94d8758aa12117164 From abc737a09dcd103cd9db0aff1ff1b6a6133ce3cf Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 21 Feb 2023 01:04:14 -0500 Subject: [PATCH 016/246] add tests for externals --- .../erc20/tests/test_erc20_library.cairo | 265 +++++++++++++++++- 1 file changed, 251 insertions(+), 14 deletions(-) diff --git a/src/openzeppelin/token/erc20/tests/test_erc20_library.cairo b/src/openzeppelin/token/erc20/tests/test_erc20_library.cairo index 857f5d7d0..689e7d141 100644 --- a/src/openzeppelin/token/erc20/tests/test_erc20_library.cairo +++ b/src/openzeppelin/token/erc20/tests/test_erc20_library.cairo @@ -1,39 +1,80 @@ -/// Tests only the internal functions of the erc20 library because -/// `get_caller_address` is not yet functional with accounts. -/// -/// Events are not yet recognized in starknet tests. -/// Some of these tests will panic with 'Unknown selector for system call!' -/// Comment out events in erc20/erc20.cairo to omit panicking. - use erc20::ERC20Library; use starknet::contract_address_const; +use starknet_testing::set_caller_address; use integer::u256_from_felt; const NAME: felt = 111; const SYMBOL: felt = 222; fn setup() -> (ContractAddress, u256) { - let account: ContractAddress = contract_address_const::<1>(); let initial_supply: u256 = u256_from_felt(2000); + let account: ContractAddress = contract_address_const::<1>(); + // Set caller + starknet_testing::set_caller_address(account); - ERC20Library::mock_initializer(NAME, SYMBOL); - ERC20Library::_total_supply::write(initial_supply); - ERC20Library::_balances::write(account, initial_supply); + ERC20Library::initializer(NAME, SYMBOL, initial_supply, account); (account, initial_supply) } +fn set_caller_as_zero() { + starknet_testing::set_caller_address(contract_address_const::<0>()); +} + #[test] #[available_gas(2000000)] fn initialize() { + let initial_supply: u256 = u256_from_felt(2000); + let account: ContractAddress = contract_address_const::<1>(); let decimals: u8 = 18_u8; - ERC20Library::mock_initializer(NAME, SYMBOL); + ERC20Library::initializer(NAME, SYMBOL, initial_supply, account); + + let owner_balance: u256 = ERC20Library::balance_of(account); + assert(owner_balance == initial_supply, 'Should eq inital_supply'); + assert(ERC20Library::total_supply() == initial_supply, 'Should eq inital_supply'); assert(ERC20Library::name() == NAME, 'Name should be NAME'); assert(ERC20Library::symbol() == SYMBOL, 'Symbol should be SYMBOL'); assert(ERC20Library::decimals() == decimals, 'Decimals should be 18'); } +#[test] +#[available_gas(2000000)] +fn test_approve() { + let (owner, supply) = setup(); + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + + let result: bool = ERC20Library::approve(spender, amount); + assert(result, ''); + assert(ERC20Library::allowance(owner, spender) == amount, 'Spender not approved correctly'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_approve_from_zero() { + let (owner, supply) = setup(); + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + + set_caller_as_zero(); + + ERC20Library::approve(spender, amount); + assert(ERC20Library::allowance(owner, spender) == amount, 'Spender not approved correctly'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_approve_to_zero() { + let (owner, supply) = setup(); + let spender: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt(100); + + ERC20Library::approve(spender, amount); +} + #[test] #[available_gas(2000000)] fn test__approve() { @@ -67,6 +108,21 @@ fn test__approve_to_zero() { ERC20Library::_approve(owner, spender, amount); } +#[test] +#[available_gas(2000000)] +fn test_transfer() { + let (sender, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + let success: bool = ERC20Library::transfer(recipient, amount); + + assert(success, ''); + assert(ERC20Library::balance_of(recipient) == amount, 'Balance should eq amount'); + assert(ERC20Library::balance_of(sender) == supply - amount, 'Should eq supply - amount'); + assert(ERC20Library::total_supply() == supply, 'Total supply should not change'); +} + #[test] #[available_gas(2000000)] fn test__transfer() { @@ -113,6 +169,186 @@ fn test__transfer_to_zero() { ERC20Library::_transfer(sender, recipient, amount); } +#[test] +#[available_gas(2000000)] +fn test_transfer_from() { + let (owner, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<2>(); + let spender: ContractAddress = contract_address_const::<3>(); + let amount: u256 = u256_from_felt(100); + + ERC20Library::approve(spender, amount); + + starknet_testing::set_caller_address(spender); + + let success: bool = ERC20Library::transfer_from(owner, recipient, amount); + assert(success, 'Should return success'); + + // Will dangle without setting as a var + let spender_allowance: u256 = ERC20Library::allowance(owner, spender); + + assert(ERC20Library::balance_of(recipient) == amount, 'Should eq amount'); + assert(ERC20Library::balance_of(owner) == supply - amount, 'Should eq suppy - amount'); + assert(spender_allowance == u256_from_felt(0), 'Should eq 0'); + assert(ERC20Library::total_supply() == supply, 'Total supply should not change'); +} + +#[test] +#[available_gas(2000000)] +fn test_transfer_from_doesnt_consume_infinite_allowance() { + let (owner, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<2>(); + let spender: ContractAddress = contract_address_const::<3>(); + let amount: u256 = u256_from_felt(100); + let max_u256: u256 = u256_from_felt(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) * u256_from_felt(256) + u256_from_felt(255); + + ERC20Library::approve(spender, max_u256); + + starknet_testing::set_caller_address(spender); + + ERC20Library::transfer_from(owner, recipient, amount); + + let spender_allowance: u256 = ERC20Library::allowance(owner, spender); + assert(spender_allowance == max_u256, 'Should remain max_uint256'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_transfer_from_greater_than_allowance() { + let (owner, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<2>(); + let spender: ContractAddress = contract_address_const::<3>(); + let amount: u256 = u256_from_felt(100); + let amount_plus_one: u256 = amount + u256_from_felt(1); + + ERC20Library::approve(spender, amount); + + starknet_testing::set_caller_address(spender); + + ERC20Library::transfer_from(owner, recipient, amount_plus_one); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_transfer_from_to_zero_address() { + let (owner, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<0>(); + let spender: ContractAddress = contract_address_const::<3>(); + let amount: u256 = u256_from_felt(100); + + ERC20Library::approve(spender, amount); + + starknet_testing::set_caller_address(spender); + + ERC20Library::transfer_from(owner, recipient, amount); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_transfer_from_from_zero_address() { + let (owner, supply) = setup(); + + let zero_address: ContractAddress = contract_address_const::<0>(); + let recipient: ContractAddress = contract_address_const::<2>(); + let spender: ContractAddress = contract_address_const::<3>(); + let amount: u256 = u256_from_felt(100); + + starknet_testing::set_caller_address(zero_address); + + ERC20Library::transfer_from(owner, recipient, amount); +} + +#[test] +#[available_gas(2000000)] +fn test_increase_allowance() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + + ERC20Library::approve(spender, amount); + ERC20Library::increase_allowance(spender, amount); + + let spender_allowance: u256 = ERC20Library::allowance(owner, spender); + assert(spender_allowance == amount + amount, ''); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_increase_allowance_to_zero_address() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt(100); + + ERC20Library::increase_allowance(spender, amount); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_increase_allowance_from_zero_address() { + let (owner, supply) = setup(); + + let zero_address: ContractAddress = contract_address_const::<0>(); + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + + starknet_testing::set_caller_address(zero_address); + + ERC20Library::increase_allowance(spender, amount); +} + +#[test] +#[available_gas(2000000)] +fn test_decrease_allowance() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + + ERC20Library::approve(spender, amount); + ERC20Library::decrease_allowance(spender, amount); + + let spender_allowance: u256 = ERC20Library::allowance(owner, spender); + assert(spender_allowance == amount - amount, ''); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_decrease_allowance_to_zero_address() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt(100); + + ERC20Library::decrease_allowance(spender, amount); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_decrease_allowance_from_zero_address() { + let (owner, supply) = setup(); + + let zero_address: ContractAddress = contract_address_const::<0>(); + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + + starknet_testing::set_caller_address(zero_address); + + ERC20Library::decrease_allowance(spender, amount); +} + #[test] #[available_gas(2000000)] fn test__spend_allowance_not_unlimited() { @@ -150,9 +386,10 @@ fn test__mint() { ERC20Library::_mint(minter, amount); + let minter_balance: u256 = ERC20Library::balance_of(minter); + assert(minter_balance == amount, 'Should eq amount'); + assert(ERC20Library::total_supply() == amount, 'Should eq total supply'); - // assert(ERC20Library::balance_of(minter) == amount, 'Should eq amount'); - // Causes 'Error: Failed setting up runner' } #[test] From 4ca2620dc023d83ec3f7fbd5612f92af6accb44e Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 21 Feb 2023 01:10:29 -0500 Subject: [PATCH 017/246] add bool assertions --- .../erc20/tests/test_erc20_library.cairo | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/openzeppelin/token/erc20/tests/test_erc20_library.cairo b/src/openzeppelin/token/erc20/tests/test_erc20_library.cairo index 689e7d141..519f73e17 100644 --- a/src/openzeppelin/token/erc20/tests/test_erc20_library.cairo +++ b/src/openzeppelin/token/erc20/tests/test_erc20_library.cairo @@ -45,8 +45,8 @@ fn test_approve() { let spender: ContractAddress = contract_address_const::<2>(); let amount: u256 = u256_from_felt(100); - let result: bool = ERC20Library::approve(spender, amount); - assert(result, ''); + let success: bool = ERC20Library::approve(spender, amount); + assert(success, 'Should return true'); assert(ERC20Library::allowance(owner, spender) == amount, 'Spender not approved correctly'); } @@ -117,7 +117,7 @@ fn test_transfer() { let amount: u256 = u256_from_felt(100); let success: bool = ERC20Library::transfer(recipient, amount); - assert(success, ''); + assert(success, 'Should return true'); assert(ERC20Library::balance_of(recipient) == amount, 'Balance should eq amount'); assert(ERC20Library::balance_of(sender) == supply - amount, 'Should eq supply - amount'); assert(ERC20Library::total_supply() == supply, 'Total supply should not change'); @@ -183,7 +183,7 @@ fn test_transfer_from() { starknet_testing::set_caller_address(spender); let success: bool = ERC20Library::transfer_from(owner, recipient, amount); - assert(success, 'Should return success'); + assert(success, 'Should return true'); // Will dangle without setting as a var let spender_allowance: u256 = ERC20Library::allowance(owner, spender); @@ -274,10 +274,12 @@ fn test_increase_allowance() { let amount: u256 = u256_from_felt(100); ERC20Library::approve(spender, amount); - ERC20Library::increase_allowance(spender, amount); + let success: bool = ERC20Library::increase_allowance(spender, amount); + assert(success, 'Should return true'); + let spender_allowance: u256 = ERC20Library::allowance(owner, spender); - assert(spender_allowance == amount + amount, ''); + assert(spender_allowance == amount + amount, 'Should be amount * 2'); } #[test] @@ -316,10 +318,11 @@ fn test_decrease_allowance() { let amount: u256 = u256_from_felt(100); ERC20Library::approve(spender, amount); - ERC20Library::decrease_allowance(spender, amount); + let success: bool = ERC20Library::decrease_allowance(spender, amount); + assert(success, 'Should return true'); let spender_allowance: u256 = ERC20Library::allowance(owner, spender); - assert(spender_allowance == amount - amount, ''); + assert(spender_allowance == amount - amount, 'Should be 0'); } #[test] From 8dd8ed34ccf7fb251018d9bb1908005356598d91 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 22 Feb 2023 00:19:21 -0500 Subject: [PATCH 018/246] update cairo --- cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cairo b/cairo index d297e4721..30b4acdd4 160000 --- a/cairo +++ b/cairo @@ -1 +1 @@ -Subproject commit d297e472133680481068cba94d8758aa12117164 +Subproject commit 30b4acdd4df7494b7216206c2a60bfc0876361b7 From 091a29c7cf9aa1ab4d05adab8d53970d2d9cced2 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 22 Feb 2023 00:21:32 -0500 Subject: [PATCH 019/246] remove preset mods --- src/openzeppelin/token/erc20/cairo_project.toml | 1 - src/openzeppelin/token/erc20/lib.cairo | 14 ++++++++++++-- src/openzeppelin/token/erc20/tests.cairo | 3 --- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/openzeppelin/token/erc20/cairo_project.toml b/src/openzeppelin/token/erc20/cairo_project.toml index ffd670876..e3dce5a73 100644 --- a/src/openzeppelin/token/erc20/cairo_project.toml +++ b/src/openzeppelin/token/erc20/cairo_project.toml @@ -1,3 +1,2 @@ [crate_roots] erc20 = "." -presets = "presets" diff --git a/src/openzeppelin/token/erc20/lib.cairo b/src/openzeppelin/token/erc20/lib.cairo index e87b836c1..29d85d5fe 100644 --- a/src/openzeppelin/token/erc20/lib.cairo +++ b/src/openzeppelin/token/erc20/lib.cairo @@ -1,6 +1,16 @@ mod erc20; -use erc20::ERC20Library; +use erc20::ERC20; mod tests; -mod presets; +trait IERC20 { + fn name() -> felt; + fn symbol() -> felt; + fn decimals() -> u8; + fn total_supply() -> u256; + fn balance_of(account: ContractAddress) -> u256; + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256; + fn transfer(recipient: ContractAddress, amount: u256) -> bool; + fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool; + fn approve(spender: ContractAddress, amount: u256) -> bool; +} diff --git a/src/openzeppelin/token/erc20/tests.cairo b/src/openzeppelin/token/erc20/tests.cairo index 13e522153..0117a1281 100644 --- a/src/openzeppelin/token/erc20/tests.cairo +++ b/src/openzeppelin/token/erc20/tests.cairo @@ -1,4 +1 @@ -mod test_erc20_library; mod test_erc20; -mod test_erc20_mintable; -mod test_erc20_burnable; From 1c866f25df3437d6b0e89286568a727529012989 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 22 Feb 2023 00:22:29 -0500 Subject: [PATCH 020/246] add IERC20 trait --- src/openzeppelin/token/erc20/erc20.cairo | 114 +++-- src/openzeppelin/token/erc20/presets.cairo | 3 - .../token/erc20/presets/ERC20.cairo | 71 --- .../token/erc20/presets/cairo_project.toml | 2 - .../token/erc20/presets/erc20_burnable.cairo | 85 ---- .../token/erc20/presets/erc20_mintable.cairo | 76 ---- .../token/erc20/presets/lib.cairo | 8 - .../token/erc20/tests/test_erc20.cairo | 417 ++++++++++++++++- .../erc20/tests/test_erc20_burnable.cairo | 16 - .../erc20/tests/test_erc20_library.cairo | 429 ------------------ .../erc20/tests/test_erc20_mintable.cairo | 16 - 11 files changed, 502 insertions(+), 735 deletions(-) delete mode 100644 src/openzeppelin/token/erc20/presets.cairo delete mode 100644 src/openzeppelin/token/erc20/presets/ERC20.cairo delete mode 100644 src/openzeppelin/token/erc20/presets/cairo_project.toml delete mode 100644 src/openzeppelin/token/erc20/presets/erc20_burnable.cairo delete mode 100644 src/openzeppelin/token/erc20/presets/erc20_mintable.cairo delete mode 100644 src/openzeppelin/token/erc20/presets/lib.cairo delete mode 100644 src/openzeppelin/token/erc20/tests/test_erc20_burnable.cairo delete mode 100644 src/openzeppelin/token/erc20/tests/test_erc20_library.cairo delete mode 100644 src/openzeppelin/token/erc20/tests/test_erc20_mintable.cairo diff --git a/src/openzeppelin/token/erc20/erc20.cairo b/src/openzeppelin/token/erc20/erc20.cairo index e642211a5..82aed427f 100644 --- a/src/openzeppelin/token/erc20/erc20.cairo +++ b/src/openzeppelin/token/erc20/erc20.cairo @@ -1,5 +1,6 @@ #[contract] -mod ERC20Library { +mod ERC20 { + use erc20::IERC20; use starknet::get_caller_address; use starknet::contract_address_const; use starknet::ContractAddressZeroable; @@ -19,69 +20,128 @@ mod ERC20Library { #[event] fn Approval(owner: ContractAddress, spender: ContractAddress, value: u256) {} - // TMP starknet testing isn't fully functional. - // Use to ensure paths are correctly set. - fn mock_initializer(name_: felt, symbol_: felt) { - _name::write(name_); - _symbol::write(symbol_); + impl ERC20 of IERC20 { + fn name() -> felt { + _name::read() + } + + fn symbol() -> felt { + _symbol::read() + } + + fn decimals() -> u8 { + 18_u8 + } + + fn total_supply() -> u256 { + _total_supply::read() + } + + fn balance_of(account: ContractAddress) -> u256 { + _balances::read(account) + } + + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { + _allowances::read((owner, spender)) + } + + fn transfer(recipient: ContractAddress, amount: u256) -> bool { + let sender = get_caller_address(); + _transfer(sender, recipient, amount); + true + } + + fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { + let caller = get_caller_address(); + _spend_allowance(sender, caller, amount); + _transfer(sender, recipient, amount); + true + } + + fn approve(spender: ContractAddress, amount: u256) -> bool { + let caller = get_caller_address(); + _approve(caller, spender, amount); + true + } } - fn initializer(name_: felt, symbol_: felt, initial_supply: u256, recipient: ContractAddress) { - _name::write(name_); - _symbol::write(symbol_); - _mint(recipient, initial_supply); + #[constructor] + fn constructor(name: felt, symbol: felt, initial_supply: u256, recipient: ContractAddress) { + initializer(name, symbol, initial_supply, recipient); } + #[view] fn name() -> felt { - _name::read() + ERC20::name() } + #[view] fn symbol() -> felt { - _symbol::read() + ERC20::symbol() } + #[view] fn decimals() -> u8 { - 18_u8 + ERC20::decimals() } + #[view] fn total_supply() -> u256 { - _total_supply::read() + ERC20::total_supply() } + #[view] fn balance_of(account: ContractAddress) -> u256 { - _balances::read(account) + ERC20::balance_of(account) } + #[view] fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { - _allowances::read((owner, spender)) + ERC20::allowance(owner, spender) } + #[external] fn transfer(recipient: ContractAddress, amount: u256) -> bool { - let sender = get_caller_address(); - _transfer(sender, recipient, amount); - true + ERC20::transfer(recipient, amount) } + #[external] fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { - let caller = get_caller_address(); - _spend_allowance(sender, caller, amount); - _transfer(sender, recipient, amount); - true + ERC20::transfer_from(sender, recipient, amount) } + #[external] fn approve(spender: ContractAddress, amount: u256) -> bool { - let caller = get_caller_address(); - _approve(caller, spender, amount); - true + ERC20::approve(spender, amount) } + #[external] fn increase_allowance(spender: ContractAddress, added_value: u256) -> bool { + _increase_allowance(spender, added_value) + } + + #[external] + fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { + _decrease_allowance(spender, subtracted_value) + } + + /// + /// Internals + /// + + fn initializer(name_: felt, symbol_: felt, initial_supply: u256, recipient: ContractAddress) { + _name::write(name_); + _symbol::write(symbol_); + _mint(recipient, initial_supply); + } + + fn _increase_allowance(spender: ContractAddress, added_value: u256) -> bool { let caller = get_caller_address(); _approve(caller, spender, _allowances::read((caller, spender)) + added_value); true } - fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { + fn _decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { let caller = get_caller_address(); _approve(caller, spender, _allowances::read((caller, spender)) - subtracted_value); true diff --git a/src/openzeppelin/token/erc20/presets.cairo b/src/openzeppelin/token/erc20/presets.cairo deleted file mode 100644 index 0ca3c862c..000000000 --- a/src/openzeppelin/token/erc20/presets.cairo +++ /dev/null @@ -1,3 +0,0 @@ -mod erc20; -mod erc20_mintable; -mod erc20_burnable; diff --git a/src/openzeppelin/token/erc20/presets/ERC20.cairo b/src/openzeppelin/token/erc20/presets/ERC20.cairo deleted file mode 100644 index 6d6ee2eac..000000000 --- a/src/openzeppelin/token/erc20/presets/ERC20.cairo +++ /dev/null @@ -1,71 +0,0 @@ -#[contract] -mod ERC20 { - use erc20::ERC20Library; - - // TMP starknet testing isn't fully functional. - // Use to ensure paths are correctly set. - #[external] - fn mock_constructor(name: felt, symbol: felt) { - ERC20Library::mock_initializer(name, symbol); - } - - #[constructor] - fn constructor(name: felt, symbol: felt, initial_supply: u256, recipient: ContractAddress) { - ERC20Library::initializer(name, symbol, initial_supply, recipient); - } - - #[view] - fn name() -> felt { - ERC20Library::name() - } - - #[view] - fn symbol() -> felt { - ERC20Library::symbol() - } - - #[view] - fn decimals() -> u8 { - ERC20Library::decimals() - } - - #[view] - fn total_supply() -> u256 { - ERC20Library::total_supply() - } - - #[view] - fn balance_of(account: ContractAddress) -> u256 { - ERC20Library::balance_of(account) - } - - #[view] - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { - ERC20Library::allowance(owner, spender) - } - - #[external] - fn transfer(recipient: ContractAddress, amount: u256) -> bool { - ERC20Library::transfer(recipient, amount) - } - - #[external] - fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { - ERC20Library::transfer_from(sender, recipient, amount) - } - - #[external] - fn approve(spender: ContractAddress, amount: u256) -> bool { - ERC20Library::approve(spender, amount) - } - - #[external] - fn increase_allowance(spender: ContractAddress, added_value: u256) -> bool { - ERC20Library::increase_allowance(spender, added_value) - } - - #[external] - fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { - ERC20Library::decrease_allowance(spender, subtracted_value) - } -} diff --git a/src/openzeppelin/token/erc20/presets/cairo_project.toml b/src/openzeppelin/token/erc20/presets/cairo_project.toml deleted file mode 100644 index 2f96b70fe..000000000 --- a/src/openzeppelin/token/erc20/presets/cairo_project.toml +++ /dev/null @@ -1,2 +0,0 @@ -[crate_roots] -presets = "." diff --git a/src/openzeppelin/token/erc20/presets/erc20_burnable.cairo b/src/openzeppelin/token/erc20/presets/erc20_burnable.cairo deleted file mode 100644 index 644dcdd6f..000000000 --- a/src/openzeppelin/token/erc20/presets/erc20_burnable.cairo +++ /dev/null @@ -1,85 +0,0 @@ -#[contract] -mod ERC20Burnable { - use erc20::ERC20Library; - use starknet::get_caller_address; - - // TMP starknet testing isn't fully functional. - // Use to ensure paths are correctly set. - #[external] - fn mock_constructor(name: felt, symbol: felt) { - ERC20Library::mock_initializer(name, symbol); - } - - #[constructor] - fn constructor(name: felt, symbol: felt, initial_supply: u256, recipient: ContractAddress) { - ERC20Library::initializer(name, symbol, initial_supply, recipient); - } - - #[view] - fn name() -> felt { - ERC20Library::name() - } - - #[view] - fn symbol() -> felt { - ERC20Library::symbol() - } - - #[view] - fn decimals() -> u8 { - ERC20Library::decimals() - } - - #[view] - fn total_supply() -> u256 { - ERC20Library::total_supply() - } - - #[view] - fn balance_of(account: ContractAddress) -> u256 { - ERC20Library::balance_of(account) - } - - #[view] - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { - ERC20Library::allowance(owner, spender) - } - - #[external] - fn transfer(recipient: ContractAddress, amount: u256) -> bool { - ERC20Library::transfer(recipient, amount) - } - - #[external] - fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { - ERC20Library::transfer_from(sender, recipient, amount) - } - - #[external] - fn approve(spender: ContractAddress, amount: u256) -> bool { - ERC20Library::approve(spender, amount) - } - - #[external] - fn increase_allowance(spender: ContractAddress, added_value: u256) -> bool { - ERC20Library::increase_allowance(spender, added_value) - } - - #[external] - fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { - ERC20Library::decrease_allowance(spender, subtracted_value) - } - - #[external] - fn burn(amount: u256) { - let caller = get_caller_address(); - ERC20Library::_burn(caller, amount); - } - - #[external] - fn burn_from(account: ContractAddress, amount: u256) { - let caller = get_caller_address(); - ERC20Library::_spend_allowance(account, caller, amount); - ERC20Library::_burn(account, amount); - } -} diff --git a/src/openzeppelin/token/erc20/presets/erc20_mintable.cairo b/src/openzeppelin/token/erc20/presets/erc20_mintable.cairo deleted file mode 100644 index d25661a88..000000000 --- a/src/openzeppelin/token/erc20/presets/erc20_mintable.cairo +++ /dev/null @@ -1,76 +0,0 @@ -#[contract] -mod ERC20Mintable { - use erc20::ERC20Library; - - // TMP starknet testing isn't fully functional. - // Use to ensure paths are correctly set. - #[external] - fn mock_constructor(name: felt, symbol: felt) { - ERC20Library::mock_initializer(name, symbol); - } - - #[constructor] - fn constructor(name: felt, symbol: felt, initial_supply: u256, recipient: ContractAddress) { - ERC20Library::initializer(name, symbol, initial_supply, recipient); - } - - #[view] - fn name() -> felt { - ERC20Library::name() - } - - #[view] - fn symbol() -> felt { - ERC20Library::symbol() - } - - #[view] - fn decimals() -> u8 { - ERC20Library::decimals() - } - - #[view] - fn total_supply() -> u256 { - ERC20Library::total_supply() - } - - #[view] - fn balance_of(account: ContractAddress) -> u256 { - ERC20Library::balance_of(account) - } - - #[view] - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { - ERC20Library::allowance(owner, spender) - } - - #[external] - fn transfer(recipient: ContractAddress, amount: u256) -> bool { - ERC20Library::transfer(recipient, amount) - } - - #[external] - fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { - ERC20Library::transfer_from(sender, recipient, amount) - } - - #[external] - fn approve(spender: ContractAddress, amount: u256) -> bool { - ERC20Library::approve(spender, amount) - } - - #[external] - fn increase_allowance(spender: ContractAddress, added_value: u256) -> bool { - ERC20Library::increase_allowance(spender, added_value) - } - - #[external] - fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { - ERC20Library::decrease_allowance(spender, subtracted_value) - } - - #[external] - fn mint(recipient: ContractAddress, amount: u256) { - ERC20Library::_mint(recipient, amount); - } -} diff --git a/src/openzeppelin/token/erc20/presets/lib.cairo b/src/openzeppelin/token/erc20/presets/lib.cairo deleted file mode 100644 index 31d88e007..000000000 --- a/src/openzeppelin/token/erc20/presets/lib.cairo +++ /dev/null @@ -1,8 +0,0 @@ -mod erc20; -use erc20::ERC20; - -mod erc20_mintable; -use erc20_mintable::ERC20Mintable; - -mod erc20_burnable; -use erc20_burnable::ERC20Burnable; diff --git a/src/openzeppelin/token/erc20/tests/test_erc20.cairo b/src/openzeppelin/token/erc20/tests/test_erc20.cairo index 7d5b903d5..76d6f076c 100644 --- a/src/openzeppelin/token/erc20/tests/test_erc20.cairo +++ b/src/openzeppelin/token/erc20/tests/test_erc20.cairo @@ -1,16 +1,429 @@ -use presets::ERC20; +use erc20::ERC20; +use starknet::contract_address_const; +use starknet_testing::set_caller_address; +use integer::u256_from_felt; const NAME: felt = 111; const SYMBOL: felt = 222; +fn setup() -> (ContractAddress, u256) { + let initial_supply: u256 = u256_from_felt(2000); + let account: ContractAddress = contract_address_const::<1>(); + // Set account as default caller + starknet_testing::set_caller_address(account); + + ERC20::initializer(NAME, SYMBOL, initial_supply, account); + (account, initial_supply) +} + +fn set_caller_as_zero() { + starknet_testing::set_caller_address(contract_address_const::<0>()); +} + #[test] #[available_gas(2000000)] fn initialize() { + let initial_supply: u256 = u256_from_felt(2000); + let account: ContractAddress = contract_address_const::<1>(); let decimals: u8 = 18_u8; - ERC20::mock_constructor(NAME, SYMBOL); + ERC20::initializer(NAME, SYMBOL, initial_supply, account); + + let owner_balance: u256 = ERC20::balance_of(account); + assert(owner_balance == initial_supply, 'Should eq inital_supply'); + assert(ERC20::total_supply() == initial_supply, 'Should eq inital_supply'); assert(ERC20::name() == NAME, 'Name should be NAME'); assert(ERC20::symbol() == SYMBOL, 'Symbol should be SYMBOL'); assert(ERC20::decimals() == decimals, 'Decimals should be 18'); } + +#[test] +#[available_gas(2000000)] +fn test_approve() { + let (owner, supply) = setup(); + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + + let success: bool = ERC20::approve(spender, amount); + assert(success, 'Should return true'); + assert(ERC20::allowance(owner, spender) == amount, 'Spender not approved correctly'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_approve_from_zero() { + let (owner, supply) = setup(); + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + + set_caller_as_zero(); + + ERC20::approve(spender, amount); + assert(ERC20::allowance(owner, spender) == amount, 'Spender not approved correctly'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_approve_to_zero() { + let (owner, supply) = setup(); + let spender: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt(100); + + ERC20::approve(spender, amount); +} + +#[test] +#[available_gas(2000000)] +fn test__approve() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + + ERC20::_approve(owner, spender, amount); + assert(ERC20::allowance(owner, spender) == amount, 'Spender not approved correctly'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test__approve_from_zero() { + let owner: ContractAddress = contract_address_const::<0>(); + let spender: ContractAddress = contract_address_const::<1>(); + let amount: u256 = u256_from_felt(100); + ERC20::_approve(owner, spender, amount); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test__approve_to_zero() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt(100); + ERC20::_approve(owner, spender, amount); +} + +#[test] +#[available_gas(2000000)] +fn test_transfer() { + let (sender, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + let success: bool = ERC20::transfer(recipient, amount); + + assert(success, 'Should return true'); + assert(ERC20::balance_of(recipient) == amount, 'Balance should eq amount'); + assert(ERC20::balance_of(sender) == supply - amount, 'Should eq supply - amount'); + assert(ERC20::total_supply() == supply, 'Total supply should not change'); +} + +#[test] +#[available_gas(2000000)] +fn test__transfer() { + let (sender, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + ERC20::_transfer(sender, recipient, amount); + + assert(ERC20::balance_of(recipient) == amount, 'Balance should eq amount'); + assert(ERC20::balance_of(sender) == supply - amount, 'Should eq supply - amount'); + assert(ERC20::total_supply() == supply, 'Total supply should not change'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test__transfer_not_enough_balance() { + let (sender, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<2>(); + let amount: u256 = supply + u256_from_felt(1); + ERC20::_transfer(sender, recipient, amount); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test__transfer_from_zero() { + let sender: ContractAddress = contract_address_const::<0>(); + let recipient: ContractAddress = contract_address_const::<1>(); + let amount: u256 = u256_from_felt(100); + ERC20::_transfer(sender, recipient, amount); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test__transfer_to_zero() { + let (sender, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt(100); + ERC20::_transfer(sender, recipient, amount); +} + +#[test] +#[available_gas(2000000)] +fn test_transfer_from() { + let (owner, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<2>(); + let spender: ContractAddress = contract_address_const::<3>(); + let amount: u256 = u256_from_felt(100); + + ERC20::approve(spender, amount); + + starknet_testing::set_caller_address(spender); + + let success: bool = ERC20::transfer_from(owner, recipient, amount); + assert(success, 'Should return true'); + + // Will dangle without setting as a var + let spender_allowance: u256 = ERC20::allowance(owner, spender); + + assert(ERC20::balance_of(recipient) == amount, 'Should eq amount'); + assert(ERC20::balance_of(owner) == supply - amount, 'Should eq suppy - amount'); + assert(spender_allowance == u256_from_felt(0), 'Should eq 0'); + assert(ERC20::total_supply() == supply, 'Total supply should not change'); +} + +#[test] +#[available_gas(2000000)] +fn test_transfer_from_doesnt_consume_infinite_allowance() { + let (owner, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<2>(); + let spender: ContractAddress = contract_address_const::<3>(); + let amount: u256 = u256_from_felt(100); + let max_u256: u256 = u256_from_felt(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) * u256_from_felt(256) + u256_from_felt(255); + + ERC20::approve(spender, max_u256); + + starknet_testing::set_caller_address(spender); + + ERC20::transfer_from(owner, recipient, amount); + + let spender_allowance: u256 = ERC20::allowance(owner, spender); + assert(spender_allowance == max_u256, 'Should remain max_uint256'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_transfer_from_greater_than_allowance() { + let (owner, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<2>(); + let spender: ContractAddress = contract_address_const::<3>(); + let amount: u256 = u256_from_felt(100); + let amount_plus_one: u256 = amount + u256_from_felt(1); + + ERC20::approve(spender, amount); + + starknet_testing::set_caller_address(spender); + + ERC20::transfer_from(owner, recipient, amount_plus_one); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_transfer_from_to_zero_address() { + let (owner, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<0>(); + let spender: ContractAddress = contract_address_const::<3>(); + let amount: u256 = u256_from_felt(100); + + ERC20::approve(spender, amount); + + starknet_testing::set_caller_address(spender); + + ERC20::transfer_from(owner, recipient, amount); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_transfer_from_from_zero_address() { + let (owner, supply) = setup(); + + let zero_address: ContractAddress = contract_address_const::<0>(); + let recipient: ContractAddress = contract_address_const::<2>(); + let spender: ContractAddress = contract_address_const::<3>(); + let amount: u256 = u256_from_felt(100); + + starknet_testing::set_caller_address(zero_address); + + ERC20::transfer_from(owner, recipient, amount); +} + +#[test] +#[available_gas(2000000)] +fn test_increase_allowance() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + + ERC20::approve(spender, amount); + let success: bool = ERC20::increase_allowance(spender, amount); + assert(success, 'Should return true'); + + + let spender_allowance: u256 = ERC20::allowance(owner, spender); + assert(spender_allowance == amount + amount, 'Should be amount * 2'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_increase_allowance_to_zero_address() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt(100); + + ERC20::increase_allowance(spender, amount); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_increase_allowance_from_zero_address() { + let (owner, supply) = setup(); + + let zero_address: ContractAddress = contract_address_const::<0>(); + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + + starknet_testing::set_caller_address(zero_address); + + ERC20::increase_allowance(spender, amount); +} + +#[test] +#[available_gas(2000000)] +fn test_decrease_allowance() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + + ERC20::approve(spender, amount); + let success: bool = ERC20::decrease_allowance(spender, amount); + assert(success, 'Should return true'); + + let spender_allowance: u256 = ERC20::allowance(owner, spender); + assert(spender_allowance == amount - amount, 'Should be 0'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_decrease_allowance_to_zero_address() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt(100); + + ERC20::decrease_allowance(spender, amount); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_decrease_allowance_from_zero_address() { + let (owner, supply) = setup(); + + let zero_address: ContractAddress = contract_address_const::<0>(); + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + + starknet_testing::set_caller_address(zero_address); + + ERC20::decrease_allowance(spender, amount); +} + +#[test] +#[available_gas(2000000)] +fn test__spend_allowance_not_unlimited() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + + ERC20::_approve(owner, spender, supply); + ERC20::_spend_allowance(owner, spender, amount); + assert(ERC20::allowance(owner, spender) == supply - amount, 'Should eq supply - amount'); +} + +#[test] +#[available_gas(2000000)] +fn test__spend_allowance_unlimited() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<2>(); + + let max_u256: u256 = u256_from_felt(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) * u256_from_felt(256) + u256_from_felt(255); + let max_minus_one: u256 = max_u256 - u256_from_felt(1); + + ERC20::_approve(owner, spender, max_u256); + ERC20::_spend_allowance(owner, spender, max_minus_one); + + assert(ERC20::allowance(owner, spender) == max_u256, 'Allowance should not change'); +} + +#[test] +#[available_gas(2000000)] +fn test__mint() { + let minter: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt(100); + + ERC20::_mint(minter, amount); + + let minter_balance: u256 = ERC20::balance_of(minter); + assert(minter_balance == amount, 'Should eq amount'); + + assert(ERC20::total_supply() == amount, 'Should eq total supply'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test__mint_to_zero() { + let minter: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt(100); + + ERC20::_mint(minter, amount); +} + +#[test] +#[available_gas(2000000)] +fn test__burn() { + let (owner, supply) = setup(); + + let amount: u256 = u256_from_felt(100); + ERC20::_burn(owner, amount); + + assert(ERC20::total_supply() == supply - amount, 'Should eq supply - amount'); + assert(ERC20::balance_of(owner) == supply - amount, 'Should eq supply - amount'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test__burn_from_zero() { + setup(); + let zero_address: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt(100); + + ERC20::_burn(zero_address, amount); +} diff --git a/src/openzeppelin/token/erc20/tests/test_erc20_burnable.cairo b/src/openzeppelin/token/erc20/tests/test_erc20_burnable.cairo deleted file mode 100644 index 53e2a53ee..000000000 --- a/src/openzeppelin/token/erc20/tests/test_erc20_burnable.cairo +++ /dev/null @@ -1,16 +0,0 @@ -use presets::ERC20Burnable; - -const NAME: felt = 111; -const SYMBOL: felt = 222; - -#[test] -#[available_gas(2000000)] -fn initialize() { - let decimals: u8 = 18_u8; - - ERC20Burnable::mock_constructor(NAME, SYMBOL); - - assert(ERC20Burnable::name() == NAME, 'Name should be NAME'); - assert(ERC20Burnable::symbol() == SYMBOL, 'Symbol should be SYMBOL'); - assert(ERC20Burnable::decimals() == decimals, 'Decimals should be 18'); -} diff --git a/src/openzeppelin/token/erc20/tests/test_erc20_library.cairo b/src/openzeppelin/token/erc20/tests/test_erc20_library.cairo deleted file mode 100644 index 519f73e17..000000000 --- a/src/openzeppelin/token/erc20/tests/test_erc20_library.cairo +++ /dev/null @@ -1,429 +0,0 @@ -use erc20::ERC20Library; -use starknet::contract_address_const; -use starknet_testing::set_caller_address; -use integer::u256_from_felt; - -const NAME: felt = 111; -const SYMBOL: felt = 222; - -fn setup() -> (ContractAddress, u256) { - let initial_supply: u256 = u256_from_felt(2000); - let account: ContractAddress = contract_address_const::<1>(); - // Set caller - starknet_testing::set_caller_address(account); - - ERC20Library::initializer(NAME, SYMBOL, initial_supply, account); - (account, initial_supply) -} - -fn set_caller_as_zero() { - starknet_testing::set_caller_address(contract_address_const::<0>()); -} - -#[test] -#[available_gas(2000000)] -fn initialize() { - let initial_supply: u256 = u256_from_felt(2000); - let account: ContractAddress = contract_address_const::<1>(); - let decimals: u8 = 18_u8; - - ERC20Library::initializer(NAME, SYMBOL, initial_supply, account); - - let owner_balance: u256 = ERC20Library::balance_of(account); - assert(owner_balance == initial_supply, 'Should eq inital_supply'); - - assert(ERC20Library::total_supply() == initial_supply, 'Should eq inital_supply'); - assert(ERC20Library::name() == NAME, 'Name should be NAME'); - assert(ERC20Library::symbol() == SYMBOL, 'Symbol should be SYMBOL'); - assert(ERC20Library::decimals() == decimals, 'Decimals should be 18'); -} - -#[test] -#[available_gas(2000000)] -fn test_approve() { - let (owner, supply) = setup(); - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - - let success: bool = ERC20Library::approve(spender, amount); - assert(success, 'Should return true'); - assert(ERC20Library::allowance(owner, spender) == amount, 'Spender not approved correctly'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_approve_from_zero() { - let (owner, supply) = setup(); - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - - set_caller_as_zero(); - - ERC20Library::approve(spender, amount); - assert(ERC20Library::allowance(owner, spender) == amount, 'Spender not approved correctly'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_approve_to_zero() { - let (owner, supply) = setup(); - let spender: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt(100); - - ERC20Library::approve(spender, amount); -} - -#[test] -#[available_gas(2000000)] -fn test__approve() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - - ERC20Library::_approve(owner, spender, amount); - assert(ERC20Library::allowance(owner, spender) == amount, 'Spender not approved correctly'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test__approve_from_zero() { - let owner: ContractAddress = contract_address_const::<0>(); - let spender: ContractAddress = contract_address_const::<1>(); - let amount: u256 = u256_from_felt(100); - ERC20Library::_approve(owner, spender, amount); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test__approve_to_zero() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt(100); - ERC20Library::_approve(owner, spender, amount); -} - -#[test] -#[available_gas(2000000)] -fn test_transfer() { - let (sender, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - let success: bool = ERC20Library::transfer(recipient, amount); - - assert(success, 'Should return true'); - assert(ERC20Library::balance_of(recipient) == amount, 'Balance should eq amount'); - assert(ERC20Library::balance_of(sender) == supply - amount, 'Should eq supply - amount'); - assert(ERC20Library::total_supply() == supply, 'Total supply should not change'); -} - -#[test] -#[available_gas(2000000)] -fn test__transfer() { - let (sender, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - ERC20Library::_transfer(sender, recipient, amount); - - assert(ERC20Library::balance_of(recipient) == amount, 'Balance should eq amount'); - assert(ERC20Library::balance_of(sender) == supply - amount, 'Should eq supply - amount'); - assert(ERC20Library::total_supply() == supply, 'Total supply should not change'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test__transfer_not_enough_balance() { - let (sender, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<2>(); - let amount: u256 = supply + u256_from_felt(1); - ERC20Library::_transfer(sender, recipient, amount); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test__transfer_from_zero() { - let sender: ContractAddress = contract_address_const::<0>(); - let recipient: ContractAddress = contract_address_const::<1>(); - let amount: u256 = u256_from_felt(100); - ERC20Library::_transfer(sender, recipient, amount); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test__transfer_to_zero() { - let (sender, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt(100); - ERC20Library::_transfer(sender, recipient, amount); -} - -#[test] -#[available_gas(2000000)] -fn test_transfer_from() { - let (owner, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<2>(); - let spender: ContractAddress = contract_address_const::<3>(); - let amount: u256 = u256_from_felt(100); - - ERC20Library::approve(spender, amount); - - starknet_testing::set_caller_address(spender); - - let success: bool = ERC20Library::transfer_from(owner, recipient, amount); - assert(success, 'Should return true'); - - // Will dangle without setting as a var - let spender_allowance: u256 = ERC20Library::allowance(owner, spender); - - assert(ERC20Library::balance_of(recipient) == amount, 'Should eq amount'); - assert(ERC20Library::balance_of(owner) == supply - amount, 'Should eq suppy - amount'); - assert(spender_allowance == u256_from_felt(0), 'Should eq 0'); - assert(ERC20Library::total_supply() == supply, 'Total supply should not change'); -} - -#[test] -#[available_gas(2000000)] -fn test_transfer_from_doesnt_consume_infinite_allowance() { - let (owner, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<2>(); - let spender: ContractAddress = contract_address_const::<3>(); - let amount: u256 = u256_from_felt(100); - let max_u256: u256 = u256_from_felt(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) * u256_from_felt(256) + u256_from_felt(255); - - ERC20Library::approve(spender, max_u256); - - starknet_testing::set_caller_address(spender); - - ERC20Library::transfer_from(owner, recipient, amount); - - let spender_allowance: u256 = ERC20Library::allowance(owner, spender); - assert(spender_allowance == max_u256, 'Should remain max_uint256'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_transfer_from_greater_than_allowance() { - let (owner, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<2>(); - let spender: ContractAddress = contract_address_const::<3>(); - let amount: u256 = u256_from_felt(100); - let amount_plus_one: u256 = amount + u256_from_felt(1); - - ERC20Library::approve(spender, amount); - - starknet_testing::set_caller_address(spender); - - ERC20Library::transfer_from(owner, recipient, amount_plus_one); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_transfer_from_to_zero_address() { - let (owner, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<0>(); - let spender: ContractAddress = contract_address_const::<3>(); - let amount: u256 = u256_from_felt(100); - - ERC20Library::approve(spender, amount); - - starknet_testing::set_caller_address(spender); - - ERC20Library::transfer_from(owner, recipient, amount); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_transfer_from_from_zero_address() { - let (owner, supply) = setup(); - - let zero_address: ContractAddress = contract_address_const::<0>(); - let recipient: ContractAddress = contract_address_const::<2>(); - let spender: ContractAddress = contract_address_const::<3>(); - let amount: u256 = u256_from_felt(100); - - starknet_testing::set_caller_address(zero_address); - - ERC20Library::transfer_from(owner, recipient, amount); -} - -#[test] -#[available_gas(2000000)] -fn test_increase_allowance() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - - ERC20Library::approve(spender, amount); - let success: bool = ERC20Library::increase_allowance(spender, amount); - assert(success, 'Should return true'); - - - let spender_allowance: u256 = ERC20Library::allowance(owner, spender); - assert(spender_allowance == amount + amount, 'Should be amount * 2'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_increase_allowance_to_zero_address() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt(100); - - ERC20Library::increase_allowance(spender, amount); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_increase_allowance_from_zero_address() { - let (owner, supply) = setup(); - - let zero_address: ContractAddress = contract_address_const::<0>(); - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - - starknet_testing::set_caller_address(zero_address); - - ERC20Library::increase_allowance(spender, amount); -} - -#[test] -#[available_gas(2000000)] -fn test_decrease_allowance() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - - ERC20Library::approve(spender, amount); - let success: bool = ERC20Library::decrease_allowance(spender, amount); - assert(success, 'Should return true'); - - let spender_allowance: u256 = ERC20Library::allowance(owner, spender); - assert(spender_allowance == amount - amount, 'Should be 0'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_decrease_allowance_to_zero_address() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt(100); - - ERC20Library::decrease_allowance(spender, amount); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_decrease_allowance_from_zero_address() { - let (owner, supply) = setup(); - - let zero_address: ContractAddress = contract_address_const::<0>(); - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - - starknet_testing::set_caller_address(zero_address); - - ERC20Library::decrease_allowance(spender, amount); -} - -#[test] -#[available_gas(2000000)] -fn test__spend_allowance_not_unlimited() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - - ERC20Library::_approve(owner, spender, supply); - ERC20Library::_spend_allowance(owner, spender, amount); - assert(ERC20Library::allowance(owner, spender) == supply - amount, 'Should eq supply - amount'); -} - -#[test] -#[available_gas(2000000)] -fn test__spend_allowance_unlimited() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<2>(); - - let max_u256: u256 = u256_from_felt(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) * u256_from_felt(256) + u256_from_felt(255); - let max_minus_one: u256 = max_u256 - u256_from_felt(1); - - ERC20Library::_approve(owner, spender, max_u256); - ERC20Library::_spend_allowance(owner, spender, max_minus_one); - - assert(ERC20Library::allowance(owner, spender) == max_u256, 'Allowance should not change'); -} - -#[test] -#[available_gas(2000000)] -fn test__mint() { - let minter: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - - ERC20Library::_mint(minter, amount); - - let minter_balance: u256 = ERC20Library::balance_of(minter); - assert(minter_balance == amount, 'Should eq amount'); - - assert(ERC20Library::total_supply() == amount, 'Should eq total supply'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test__mint_to_zero() { - let minter: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt(100); - - ERC20Library::_mint(minter, amount); -} - -#[test] -#[available_gas(2000000)] -fn test__burn() { - let (owner, supply) = setup(); - - let amount: u256 = u256_from_felt(100); - ERC20Library::_burn(owner, amount); - - assert(ERC20Library::total_supply() == supply - amount, 'Should eq supply - amount'); - assert(ERC20Library::balance_of(owner) == supply - amount, 'Should eq supply - amount'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test__burn_from_zero() { - setup(); - let zero_address: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt(100); - - ERC20Library::_burn(zero_address, amount); -} diff --git a/src/openzeppelin/token/erc20/tests/test_erc20_mintable.cairo b/src/openzeppelin/token/erc20/tests/test_erc20_mintable.cairo deleted file mode 100644 index 448bd82dd..000000000 --- a/src/openzeppelin/token/erc20/tests/test_erc20_mintable.cairo +++ /dev/null @@ -1,16 +0,0 @@ -use presets::ERC20Mintable; - -const NAME: felt = 111; -const SYMBOL: felt = 222; - -#[test] -#[available_gas(2000000)] -fn initialize() { - let decimals: u8 = 18_u8; - - ERC20Mintable::mock_constructor(NAME, SYMBOL); - - assert(ERC20Mintable::name() == NAME, 'Name should be NAME'); - assert(ERC20Mintable::symbol() == SYMBOL, 'Symbol should be SYMBOL'); - assert(ERC20Mintable::decimals() == decimals, 'Decimals should be 18'); -} From 3405063e308c470635f86965969c7b04a89f865c Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 22 Feb 2023 11:31:31 -0500 Subject: [PATCH 021/246] update cairo --- cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cairo b/cairo index 30b4acdd4..34796ef86 160000 --- a/cairo +++ b/cairo @@ -1 +1 @@ -Subproject commit 30b4acdd4df7494b7216206c2a60bfc0876361b7 +Subproject commit 34796ef8615a96ea28888421dc2bc498786d01d6 From 8fe3c820f21082c909351ae04b5f9e63dedd643e Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 22 Feb 2023 11:32:05 -0500 Subject: [PATCH 022/246] clean up test --- .../token/erc20/tests/test_erc20.cairo | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/openzeppelin/token/erc20/tests/test_erc20.cairo b/src/openzeppelin/token/erc20/tests/test_erc20.cairo index 76d6f076c..804c03879 100644 --- a/src/openzeppelin/token/erc20/tests/test_erc20.cairo +++ b/src/openzeppelin/token/erc20/tests/test_erc20.cairo @@ -10,14 +10,14 @@ fn setup() -> (ContractAddress, u256) { let initial_supply: u256 = u256_from_felt(2000); let account: ContractAddress = contract_address_const::<1>(); // Set account as default caller - starknet_testing::set_caller_address(account); + set_caller_address(account); ERC20::initializer(NAME, SYMBOL, initial_supply, account); (account, initial_supply) } fn set_caller_as_zero() { - starknet_testing::set_caller_address(contract_address_const::<0>()); + set_caller_address(contract_address_const::<0>()); } #[test] @@ -180,7 +180,7 @@ fn test_transfer_from() { ERC20::approve(spender, amount); - starknet_testing::set_caller_address(spender); + set_caller_address(spender); let success: bool = ERC20::transfer_from(owner, recipient, amount); assert(success, 'Should return true'); @@ -206,7 +206,7 @@ fn test_transfer_from_doesnt_consume_infinite_allowance() { ERC20::approve(spender, max_u256); - starknet_testing::set_caller_address(spender); + set_caller_address(spender); ERC20::transfer_from(owner, recipient, amount); @@ -227,7 +227,7 @@ fn test_transfer_from_greater_than_allowance() { ERC20::approve(spender, amount); - starknet_testing::set_caller_address(spender); + set_caller_address(spender); ERC20::transfer_from(owner, recipient, amount_plus_one); } @@ -244,7 +244,7 @@ fn test_transfer_from_to_zero_address() { ERC20::approve(spender, amount); - starknet_testing::set_caller_address(spender); + set_caller_address(spender); ERC20::transfer_from(owner, recipient, amount); } @@ -260,7 +260,7 @@ fn test_transfer_from_from_zero_address() { let spender: ContractAddress = contract_address_const::<3>(); let amount: u256 = u256_from_felt(100); - starknet_testing::set_caller_address(zero_address); + set_caller_address(zero_address); ERC20::transfer_from(owner, recipient, amount); } @@ -304,7 +304,7 @@ fn test_increase_allowance_from_zero_address() { let spender: ContractAddress = contract_address_const::<2>(); let amount: u256 = u256_from_felt(100); - starknet_testing::set_caller_address(zero_address); + set_caller_address(zero_address); ERC20::increase_allowance(spender, amount); } @@ -347,7 +347,7 @@ fn test_decrease_allowance_from_zero_address() { let spender: ContractAddress = contract_address_const::<2>(); let amount: u256 = u256_from_felt(100); - starknet_testing::set_caller_address(zero_address); + set_caller_address(zero_address); ERC20::decrease_allowance(spender, amount); } From ad928bc356d25446ee78c264d4ddd340d1a05840 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 23 Feb 2023 01:33:46 -0500 Subject: [PATCH 023/246] remove assertion --- src/openzeppelin/token/erc20/tests/test_erc20.cairo | 1 - 1 file changed, 1 deletion(-) diff --git a/src/openzeppelin/token/erc20/tests/test_erc20.cairo b/src/openzeppelin/token/erc20/tests/test_erc20.cairo index 804c03879..be8adc9e8 100644 --- a/src/openzeppelin/token/erc20/tests/test_erc20.cairo +++ b/src/openzeppelin/token/erc20/tests/test_erc20.cairo @@ -61,7 +61,6 @@ fn test_approve_from_zero() { set_caller_as_zero(); ERC20::approve(spender, amount); - assert(ERC20::allowance(owner, spender) == amount, 'Spender not approved correctly'); } #[test] From d883eb5bae10b56e36dda6b16ffaa12c253a91d2 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 23 Feb 2023 01:33:59 -0500 Subject: [PATCH 024/246] update cairo --- Cargo.lock | 1 + cairo | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 3dbb84efb..e6cd6443e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -834,6 +834,7 @@ dependencies = [ "rayon", "salsa", "thiserror", + "unescaper", ] [[package]] diff --git a/cairo b/cairo index 34796ef86..176712e8e 160000 --- a/cairo +++ b/cairo @@ -1 +1 @@ -Subproject commit 34796ef8615a96ea28888421dc2bc498786d01d6 +Subproject commit 176712e8e54404a79f42bd90251571a5d90d7250 From 543501ba9cf14dc294e3d8e70bb3a5d82b184986 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 23 Feb 2023 01:43:57 -0500 Subject: [PATCH 025/246] simplify max_u256 --- src/openzeppelin/token/erc20/tests/test_erc20.cairo | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/openzeppelin/token/erc20/tests/test_erc20.cairo b/src/openzeppelin/token/erc20/tests/test_erc20.cairo index be8adc9e8..df329dff7 100644 --- a/src/openzeppelin/token/erc20/tests/test_erc20.cairo +++ b/src/openzeppelin/token/erc20/tests/test_erc20.cairo @@ -1,6 +1,7 @@ use erc20::ERC20; use starknet::contract_address_const; use starknet_testing::set_caller_address; +use integer::u256; use integer::u256_from_felt; const NAME: felt = 111; @@ -201,7 +202,10 @@ fn test_transfer_from_doesnt_consume_infinite_allowance() { let recipient: ContractAddress = contract_address_const::<2>(); let spender: ContractAddress = contract_address_const::<3>(); let amount: u256 = u256_from_felt(100); - let max_u256: u256 = u256_from_felt(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) * u256_from_felt(256) + u256_from_felt(255); + let max_u256: u256 = u256 { + low: 0xffffffffffffffffffffffffffffffff_u128, + high: 0xffffffffffffffffffffffffffffffff_u128 + }; ERC20::approve(spender, max_u256); @@ -371,7 +375,10 @@ fn test__spend_allowance_unlimited() { let spender: ContractAddress = contract_address_const::<2>(); - let max_u256: u256 = u256_from_felt(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) * u256_from_felt(256) + u256_from_felt(255); + let max_u256: u256 = u256 { + low: 0xffffffffffffffffffffffffffffffff_u128, + high: 0xffffffffffffffffffffffffffffffff_u128 + }; let max_minus_one: u256 = max_u256 - u256_from_felt(1); ERC20::_approve(owner, spender, max_u256); From 2ecba911baf559b92295057b3dba92c10d0e1330 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 23 Feb 2023 01:48:35 -0500 Subject: [PATCH 026/246] clarify error msg --- src/openzeppelin/token/erc20/tests/test_erc20.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openzeppelin/token/erc20/tests/test_erc20.cairo b/src/openzeppelin/token/erc20/tests/test_erc20.cairo index df329dff7..a41cf5bd3 100644 --- a/src/openzeppelin/token/erc20/tests/test_erc20.cairo +++ b/src/openzeppelin/token/erc20/tests/test_erc20.cairo @@ -214,7 +214,7 @@ fn test_transfer_from_doesnt_consume_infinite_allowance() { ERC20::transfer_from(owner, recipient, amount); let spender_allowance: u256 = ERC20::allowance(owner, spender); - assert(spender_allowance == max_u256, 'Should remain max_uint256'); + assert(spender_allowance == max_u256, 'Allowance should not change'); } #[test] From 6c3978b519429a7190328b2a6fa525e5574408dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Fri, 3 Mar 2023 16:18:10 -0300 Subject: [PATCH 027/246] add erc165 + tests --- .../introspection/erc165/IERC165.cairo | 10 ----- .../introspection/erc165/cairo_project.toml | 2 + .../introspection/erc165/lib.cairo | 6 +++ .../introspection/erc165/library.cairo | 38 ---------------- .../introspection/erc165/tests.cairo | 2 + .../erc165/tests/erc165mock.cairo | 34 +++++++++++++++ .../erc165/tests/test_erc165.cairo | 43 +++++++++++++++++++ 7 files changed, 87 insertions(+), 48 deletions(-) delete mode 100644 src/openzeppelin/introspection/erc165/IERC165.cairo create mode 100644 src/openzeppelin/introspection/erc165/cairo_project.toml create mode 100644 src/openzeppelin/introspection/erc165/lib.cairo delete mode 100644 src/openzeppelin/introspection/erc165/library.cairo create mode 100644 src/openzeppelin/introspection/erc165/tests.cairo create mode 100644 src/openzeppelin/introspection/erc165/tests/erc165mock.cairo create mode 100644 src/openzeppelin/introspection/erc165/tests/test_erc165.cairo diff --git a/src/openzeppelin/introspection/erc165/IERC165.cairo b/src/openzeppelin/introspection/erc165/IERC165.cairo deleted file mode 100644 index d34be3a33..000000000 --- a/src/openzeppelin/introspection/erc165/IERC165.cairo +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (introspection/erc165/IERC165.cairo) - -%lang starknet - -@contract_interface -namespace IERC165 { - func supportsInterface(interfaceId: felt) -> (success: felt) { - } -} diff --git a/src/openzeppelin/introspection/erc165/cairo_project.toml b/src/openzeppelin/introspection/erc165/cairo_project.toml new file mode 100644 index 000000000..ba52962d0 --- /dev/null +++ b/src/openzeppelin/introspection/erc165/cairo_project.toml @@ -0,0 +1,2 @@ +[crate_roots] +erc165 = "." diff --git a/src/openzeppelin/introspection/erc165/lib.cairo b/src/openzeppelin/introspection/erc165/lib.cairo new file mode 100644 index 000000000..32e5e1525 --- /dev/null +++ b/src/openzeppelin/introspection/erc165/lib.cairo @@ -0,0 +1,6 @@ +mod tests; + +trait IERC165 { + fn supports_interface(interface_id: felt) -> bool; + fn register_interface(interface_id: felt); +} diff --git a/src/openzeppelin/introspection/erc165/library.cairo b/src/openzeppelin/introspection/erc165/library.cairo deleted file mode 100644 index 8073cef40..000000000 --- a/src/openzeppelin/introspection/erc165/library.cairo +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (introspection/erc165/library.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.math import assert_not_equal -from starkware.cairo.common.bool import TRUE - -from openzeppelin.utils.constants.library import INVALID_ID, IERC165_ID - -@storage_var -func ERC165_supported_interfaces(interface_id: felt) -> (is_supported: felt) { -} - -namespace ERC165 { - func supports_interface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - interface_id: felt - ) -> (success: felt) { - if (interface_id == IERC165_ID) { - return (success=TRUE); - } - - // Checks interface registry - let (is_supported) = ERC165_supported_interfaces.read(interface_id); - return (success=is_supported); - } - - func register_interface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - interface_id: felt - ) { - with_attr error_message("ERC165: invalid interface id") { - assert_not_equal(interface_id, INVALID_ID); - } - ERC165_supported_interfaces.write(interface_id, TRUE); - return (); - } -} diff --git a/src/openzeppelin/introspection/erc165/tests.cairo b/src/openzeppelin/introspection/erc165/tests.cairo new file mode 100644 index 000000000..517c54280 --- /dev/null +++ b/src/openzeppelin/introspection/erc165/tests.cairo @@ -0,0 +1,2 @@ +mod test_erc165; +mod erc165mock; diff --git a/src/openzeppelin/introspection/erc165/tests/erc165mock.cairo b/src/openzeppelin/introspection/erc165/tests/erc165mock.cairo new file mode 100644 index 000000000..0526945e7 --- /dev/null +++ b/src/openzeppelin/introspection/erc165/tests/erc165mock.cairo @@ -0,0 +1,34 @@ +#[contract] +mod ERC165Mock { + use erc165::IERC165; + const IERC165_ID: felt = 0x01ffc9a7; + const INVALID_ID: felt = 0xffffffff; + + struct Storage { + supported_interfaces: LegacyMap::, + } + + impl ERC165 of IERC165 { + fn supports_interface(interface_id: felt) -> bool { + if interface_id == IERC165_ID { + return true; + } + supported_interfaces::read(interface_id) + } + + fn register_interface(interface_id: felt) { + assert(interface_id != INVALID_ID, 'Invalid id'); + supported_interfaces::write(interface_id, true); + } + } + + #[view] + fn supports_interface(interface_id: felt) -> bool { + ERC165::supports_interface(interface_id) + } + + #[external] + fn register_interface(interface_id: felt) { + ERC165::register_interface(interface_id) + } +} diff --git a/src/openzeppelin/introspection/erc165/tests/test_erc165.cairo b/src/openzeppelin/introspection/erc165/tests/test_erc165.cairo new file mode 100644 index 000000000..21ca324d5 --- /dev/null +++ b/src/openzeppelin/introspection/erc165/tests/test_erc165.cairo @@ -0,0 +1,43 @@ +use erc165::tests::erc165mock::ERC165Mock; + +const ERC165_ID: felt = 0x01ffc9a7; +const INVALID_ID: felt = 0xffffffff; +const OTHER_ID: felt = 0x12345678; + + +#[test] +#[available_gas(2000000)] +fn test_default_behavior() { + let supports_default_interface: bool = ERC165Mock::supports_interface(ERC165_ID); + assert(supports_default_interface, 'Should support base interface'); +} + +#[test] +#[available_gas(2000000)] +fn test_not_registered_interface() { + let supports_unregistered_interface: bool = ERC165Mock::supports_interface(OTHER_ID); + assert(! supports_unregistered_interface, 'Should not support unregistered'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_supports_invalid_interface() { + let supports_invalid_interface: bool = ERC165Mock::supports_interface(INVALID_ID); + assert(! supports_invalid_interface, 'Should not support invalid id'); +} + +#[test] +#[available_gas(2000000)] +fn test_register_interface() { + ERC165Mock::register_interface(OTHER_ID); + let supports_new_interface: bool = ERC165Mock::supports_interface(OTHER_ID); + assert(supports_new_interface, 'Should support new interface'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_register_invalid_interface() { + ERC165Mock::register_interface(INVALID_ID); +} From 60365c8425efd3fa14e8a47dc4260b93864f393a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Fri, 3 Mar 2023 17:02:11 -0300 Subject: [PATCH 028/246] kickstart account module --- src/openzeppelin/account/cairo_project.toml | 2 + src/openzeppelin/account/lib.cairo | 6 +++ .../account/{presets => old}/Account.cairo | 0 .../{presets => old}/AddressRegistry.cairo | 0 .../account/{presets => old}/EthAccount.cairo | 0 .../account/{ => old}/IAccount.cairo | 0 .../account/{ => old}/library.cairo | 0 src/openzeppelin/account/tests.cairo | 2 + .../account/tests/account_mock.cairo | 54 +++++++++++++++++++ .../account/tests/test_account.cairo | 43 +++++++++++++++ 10 files changed, 107 insertions(+) create mode 100644 src/openzeppelin/account/cairo_project.toml create mode 100644 src/openzeppelin/account/lib.cairo rename src/openzeppelin/account/{presets => old}/Account.cairo (100%) rename src/openzeppelin/account/{presets => old}/AddressRegistry.cairo (100%) rename src/openzeppelin/account/{presets => old}/EthAccount.cairo (100%) rename src/openzeppelin/account/{ => old}/IAccount.cairo (100%) rename src/openzeppelin/account/{ => old}/library.cairo (100%) create mode 100644 src/openzeppelin/account/tests.cairo create mode 100644 src/openzeppelin/account/tests/account_mock.cairo create mode 100644 src/openzeppelin/account/tests/test_account.cairo diff --git a/src/openzeppelin/account/cairo_project.toml b/src/openzeppelin/account/cairo_project.toml new file mode 100644 index 000000000..9a0b3f035 --- /dev/null +++ b/src/openzeppelin/account/cairo_project.toml @@ -0,0 +1,2 @@ +[crate_roots] +account = "." diff --git a/src/openzeppelin/account/lib.cairo b/src/openzeppelin/account/lib.cairo new file mode 100644 index 000000000..f6bc9aeb8 --- /dev/null +++ b/src/openzeppelin/account/lib.cairo @@ -0,0 +1,6 @@ +mod tests; + +trait IAccount { + // fn supports_interface(interface_id: felt) -> bool; + // fn register_interface(interface_id: felt); +} diff --git a/src/openzeppelin/account/presets/Account.cairo b/src/openzeppelin/account/old/Account.cairo similarity index 100% rename from src/openzeppelin/account/presets/Account.cairo rename to src/openzeppelin/account/old/Account.cairo diff --git a/src/openzeppelin/account/presets/AddressRegistry.cairo b/src/openzeppelin/account/old/AddressRegistry.cairo similarity index 100% rename from src/openzeppelin/account/presets/AddressRegistry.cairo rename to src/openzeppelin/account/old/AddressRegistry.cairo diff --git a/src/openzeppelin/account/presets/EthAccount.cairo b/src/openzeppelin/account/old/EthAccount.cairo similarity index 100% rename from src/openzeppelin/account/presets/EthAccount.cairo rename to src/openzeppelin/account/old/EthAccount.cairo diff --git a/src/openzeppelin/account/IAccount.cairo b/src/openzeppelin/account/old/IAccount.cairo similarity index 100% rename from src/openzeppelin/account/IAccount.cairo rename to src/openzeppelin/account/old/IAccount.cairo diff --git a/src/openzeppelin/account/library.cairo b/src/openzeppelin/account/old/library.cairo similarity index 100% rename from src/openzeppelin/account/library.cairo rename to src/openzeppelin/account/old/library.cairo diff --git a/src/openzeppelin/account/tests.cairo b/src/openzeppelin/account/tests.cairo new file mode 100644 index 000000000..b837159e4 --- /dev/null +++ b/src/openzeppelin/account/tests.cairo @@ -0,0 +1,2 @@ +mod test_account; +mod account_mock; diff --git a/src/openzeppelin/account/tests/account_mock.cairo b/src/openzeppelin/account/tests/account_mock.cairo new file mode 100644 index 000000000..cee549e12 --- /dev/null +++ b/src/openzeppelin/account/tests/account_mock.cairo @@ -0,0 +1,54 @@ +#[account_contract] +mod Account { + use erc165::ERC165Library; + use starknet::get_caller_address; + use starknet::get_contract_address; + + struct Storage { + public_key: felt, + } + + #[constructor] + fn constructor(_public_key: felt) { + public_key::write(_public_key); + } + + #[external] + fn __execute__(amount: felt) { + let is_valid = is_valid_signature(); + assert(is_valid == true, 'Invalid signature.'); + } + + #[external] + fn set_public_key(new_public_key: felt) { + only_self(); + public_key::write(new_public_key); + } + + #[view] + fn get_public_key() -> felt { + public_key::read() + } + + #[view] + fn is_valid_signature() -> bool { + true + } + + fn only_self() { + let caller = starknet::get_caller_address(); + let self = starknet::get_contract_address(); + assert(1 == 2, 'Account: unauthorized.'); + } + + // ERC165Library + #[view] + fn supports_interface(interface_id: felt) -> bool { + ERC165Library::supports_interface(interface_id) + } + + #[external] + fn register_interface(interface_id: felt) { + ERC165Library::register_interface(interface_id); + } +} diff --git a/src/openzeppelin/account/tests/test_account.cairo b/src/openzeppelin/account/tests/test_account.cairo new file mode 100644 index 000000000..21ca324d5 --- /dev/null +++ b/src/openzeppelin/account/tests/test_account.cairo @@ -0,0 +1,43 @@ +use erc165::tests::erc165mock::ERC165Mock; + +const ERC165_ID: felt = 0x01ffc9a7; +const INVALID_ID: felt = 0xffffffff; +const OTHER_ID: felt = 0x12345678; + + +#[test] +#[available_gas(2000000)] +fn test_default_behavior() { + let supports_default_interface: bool = ERC165Mock::supports_interface(ERC165_ID); + assert(supports_default_interface, 'Should support base interface'); +} + +#[test] +#[available_gas(2000000)] +fn test_not_registered_interface() { + let supports_unregistered_interface: bool = ERC165Mock::supports_interface(OTHER_ID); + assert(! supports_unregistered_interface, 'Should not support unregistered'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_supports_invalid_interface() { + let supports_invalid_interface: bool = ERC165Mock::supports_interface(INVALID_ID); + assert(! supports_invalid_interface, 'Should not support invalid id'); +} + +#[test] +#[available_gas(2000000)] +fn test_register_interface() { + ERC165Mock::register_interface(OTHER_ID); + let supports_new_interface: bool = ERC165Mock::supports_interface(OTHER_ID); + assert(supports_new_interface, 'Should support new interface'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic] +fn test_register_invalid_interface() { + ERC165Mock::register_interface(INVALID_ID); +} From be1e9dfdab63ed4e890ff013fb338bdb1776010d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Tue, 7 Mar 2023 16:21:25 -0300 Subject: [PATCH 029/246] clean up python project. ready for rust/cairo1 --- .gitattributes | 2 +- .gitignore | 5 + MANIFEST.in | 1 - pyproject.toml | 8 - scripts/update_version.py | 33 - setup.cfg | 37 - setup.py | 13 - src/openzeppelin/__init__.py | 11 - .../access/accesscontrol/IAccessControl.cairo | 22 - .../access/accesscontrol/library.cairo | 154 --- src/openzeppelin/access/ownable/library.cairo | 89 -- src/openzeppelin/account/IAccount.cairo | 45 - src/openzeppelin/account/library.cairo | 260 ----- .../account/presets/Account.cairo | 147 --- .../account/presets/AddressRegistry.cairo | 27 - .../account/presets/EthAccount.cairo | 146 --- .../introspection/erc165/IERC165.cairo | 10 - .../introspection/erc165/library.cairo | 38 - .../security/initializable/library.cairo | 28 - .../security/pausable/library.cairo | 70 -- .../security/reentrancyguard/library.cairo | 27 - .../security/safemath/library.cairo | 103 -- src/openzeppelin/token/erc1155/IERC1155.cairo | 56 -- .../token/erc1155/IERC1155MetadataURI.cairo | 50 - .../token/erc1155/IERC1155Receiver.cairo | 36 - src/openzeppelin/token/erc1155/library.cairo | 602 ------------ .../presets/ERC1155MintableBurnable.cairo | 159 --- .../erc1155/presets/utils/ERC1155Holder.cairo | 51 - .../token/erc20/cairo_project.toml | 2 - src/openzeppelin/token/erc20/erc20.cairo | 188 ---- src/openzeppelin/token/erc20/lib.cairo | 16 - src/openzeppelin/token/erc20/tests.cairo | 1 - .../token/erc20/tests/test_erc20.cairo | 435 --------- src/openzeppelin/token/erc721/IERC721.cairo | 38 - .../token/erc721/IERC721Metadata.cairo | 49 - .../token/erc721/IERC721Receiver.cairo | 18 - .../erc721/enumerable/IERC721Enumerable.cairo | 49 - .../token/erc721/enumerable/library.cairo | 201 ---- .../ERC721EnumerableMintableBurnable.cairo | 187 ---- src/openzeppelin/token/erc721/library.cairo | 471 --------- .../presets/ERC721MintableBurnable.cairo | 161 --- .../presets/ERC721MintablePausable.cairo | 179 ---- .../erc721/presets/utils/ERC721Holder.cairo | 31 - src/openzeppelin/upgrades/library.cairo | 113 --- src/openzeppelin/upgrades/presets/Proxy.cairo | 73 -- .../utils/constants/library.cairo | 49 - .../utils/presets/UniversalDeployer.cairo | 72 -- tests/access/OwnableBaseSuite.py | 65 -- tests/access/test_AccessControl.py | 231 ----- tests/access/test_Ownable.py | 63 -- tests/account/test_Account.py | 243 ----- tests/account/test_AddressRegistry.py | 58 -- tests/account/test_EthAccount.py | 240 ----- tests/conftest.py | 6 - tests/introspection/test_ERC165.py | 68 -- tests/mocks/AccessControl.cairo | 71 -- tests/mocks/AccountReentrancy.cairo | 40 - tests/mocks/ERC1155Mock.cairo | 34 - tests/mocks/ERC165.cairo | 22 - tests/mocks/ERC721SafeMintableMock.cairo | 162 --- tests/mocks/Initializable.cairo | 18 - tests/mocks/Ownable.cairo | 41 - tests/mocks/Pausable.cairo | 65 -- tests/mocks/ProxiableImplementation.cairo | 60 -- tests/mocks/ReentrancyAttackerMock.cairo | 19 - tests/mocks/ReentrancyMock.cairo | 111 --- tests/mocks/SafeMathMock.cairo | 33 - tests/mocks/UpgradesMockV1.cairo | 66 -- tests/mocks/UpgradesMockV2.cairo | 93 -- tests/security/test_initializable.py | 24 - tests/security/test_pausable.py | 146 --- tests/security/test_reentrancy.py | 60 -- tests/security/test_safemath.py | 206 ---- tests/signers.py | 215 ---- tests/test_signers.py | 72 -- tests/token/erc1155/ERC1155BaseSuite.py | 811 --------------- tests/token/erc1155/test_ERC1155Internals.py | 67 -- .../erc1155/test_ERC1155MintableBurnable.py | 723 -------------- tests/token/erc20/ERC20BaseSuite.py | 714 -------------- tests/token/erc20/test_ERC20.py | 54 - tests/token/erc20/test_ERC20Burnable.py | 216 ---- tests/token/erc20/test_ERC20Mintable.py | 152 --- tests/token/erc20/test_ERC20Pausable.py | 194 ---- tests/token/erc20/test_ERC20Upgradeable.py | 179 ---- tests/token/erc721/ERC721BaseSuite.py | 924 ------------------ .../test_ERC721EnumerableMintableBurnable.py | 514 ---------- .../erc721/test_ERC721MintableBurnable.py | 279 ------ .../erc721/test_ERC721MintablePausable.py | 314 ------ .../erc721/test_ERC721SafeMintableMock.py | 231 ----- tests/upgrades/test_Proxy.py | 167 ---- tests/upgrades/test_upgrades.py | 368 ------- tests/utils.py | 154 --- tests/utils/test_UniversalDeployer.py | 102 -- tox.ini | 63 -- 94 files changed, 6 insertions(+), 13315 deletions(-) delete mode 100644 MANIFEST.in delete mode 100644 pyproject.toml delete mode 100644 scripts/update_version.py delete mode 100644 setup.cfg delete mode 100644 setup.py delete mode 100644 src/openzeppelin/__init__.py delete mode 100644 src/openzeppelin/access/accesscontrol/IAccessControl.cairo delete mode 100644 src/openzeppelin/access/accesscontrol/library.cairo delete mode 100644 src/openzeppelin/access/ownable/library.cairo delete mode 100644 src/openzeppelin/account/IAccount.cairo delete mode 100644 src/openzeppelin/account/library.cairo delete mode 100644 src/openzeppelin/account/presets/Account.cairo delete mode 100644 src/openzeppelin/account/presets/AddressRegistry.cairo delete mode 100644 src/openzeppelin/account/presets/EthAccount.cairo delete mode 100644 src/openzeppelin/introspection/erc165/IERC165.cairo delete mode 100644 src/openzeppelin/introspection/erc165/library.cairo delete mode 100644 src/openzeppelin/security/initializable/library.cairo delete mode 100644 src/openzeppelin/security/pausable/library.cairo delete mode 100644 src/openzeppelin/security/reentrancyguard/library.cairo delete mode 100644 src/openzeppelin/security/safemath/library.cairo delete mode 100644 src/openzeppelin/token/erc1155/IERC1155.cairo delete mode 100644 src/openzeppelin/token/erc1155/IERC1155MetadataURI.cairo delete mode 100644 src/openzeppelin/token/erc1155/IERC1155Receiver.cairo delete mode 100644 src/openzeppelin/token/erc1155/library.cairo delete mode 100644 src/openzeppelin/token/erc1155/presets/ERC1155MintableBurnable.cairo delete mode 100644 src/openzeppelin/token/erc1155/presets/utils/ERC1155Holder.cairo delete mode 100644 src/openzeppelin/token/erc20/cairo_project.toml delete mode 100644 src/openzeppelin/token/erc20/erc20.cairo delete mode 100644 src/openzeppelin/token/erc20/lib.cairo delete mode 100644 src/openzeppelin/token/erc20/tests.cairo delete mode 100644 src/openzeppelin/token/erc20/tests/test_erc20.cairo delete mode 100644 src/openzeppelin/token/erc721/IERC721.cairo delete mode 100644 src/openzeppelin/token/erc721/IERC721Metadata.cairo delete mode 100644 src/openzeppelin/token/erc721/IERC721Receiver.cairo delete mode 100644 src/openzeppelin/token/erc721/enumerable/IERC721Enumerable.cairo delete mode 100644 src/openzeppelin/token/erc721/enumerable/library.cairo delete mode 100644 src/openzeppelin/token/erc721/enumerable/presets/ERC721EnumerableMintableBurnable.cairo delete mode 100644 src/openzeppelin/token/erc721/library.cairo delete mode 100644 src/openzeppelin/token/erc721/presets/ERC721MintableBurnable.cairo delete mode 100644 src/openzeppelin/token/erc721/presets/ERC721MintablePausable.cairo delete mode 100644 src/openzeppelin/token/erc721/presets/utils/ERC721Holder.cairo delete mode 100644 src/openzeppelin/upgrades/library.cairo delete mode 100644 src/openzeppelin/upgrades/presets/Proxy.cairo delete mode 100644 src/openzeppelin/utils/constants/library.cairo delete mode 100644 src/openzeppelin/utils/presets/UniversalDeployer.cairo delete mode 100644 tests/access/OwnableBaseSuite.py delete mode 100644 tests/access/test_AccessControl.py delete mode 100644 tests/access/test_Ownable.py delete mode 100644 tests/account/test_Account.py delete mode 100644 tests/account/test_AddressRegistry.py delete mode 100644 tests/account/test_EthAccount.py delete mode 100644 tests/conftest.py delete mode 100644 tests/introspection/test_ERC165.py delete mode 100644 tests/mocks/AccessControl.cairo delete mode 100644 tests/mocks/AccountReentrancy.cairo delete mode 100644 tests/mocks/ERC1155Mock.cairo delete mode 100644 tests/mocks/ERC165.cairo delete mode 100644 tests/mocks/ERC721SafeMintableMock.cairo delete mode 100644 tests/mocks/Initializable.cairo delete mode 100644 tests/mocks/Ownable.cairo delete mode 100644 tests/mocks/Pausable.cairo delete mode 100644 tests/mocks/ProxiableImplementation.cairo delete mode 100644 tests/mocks/ReentrancyAttackerMock.cairo delete mode 100644 tests/mocks/ReentrancyMock.cairo delete mode 100644 tests/mocks/SafeMathMock.cairo delete mode 100644 tests/mocks/UpgradesMockV1.cairo delete mode 100644 tests/mocks/UpgradesMockV2.cairo delete mode 100644 tests/security/test_initializable.py delete mode 100644 tests/security/test_pausable.py delete mode 100644 tests/security/test_reentrancy.py delete mode 100644 tests/security/test_safemath.py delete mode 100644 tests/signers.py delete mode 100644 tests/test_signers.py delete mode 100644 tests/token/erc1155/ERC1155BaseSuite.py delete mode 100644 tests/token/erc1155/test_ERC1155Internals.py delete mode 100644 tests/token/erc1155/test_ERC1155MintableBurnable.py delete mode 100644 tests/token/erc20/ERC20BaseSuite.py delete mode 100644 tests/token/erc20/test_ERC20.py delete mode 100644 tests/token/erc20/test_ERC20Burnable.py delete mode 100644 tests/token/erc20/test_ERC20Mintable.py delete mode 100644 tests/token/erc20/test_ERC20Pausable.py delete mode 100644 tests/token/erc20/test_ERC20Upgradeable.py delete mode 100644 tests/token/erc721/ERC721BaseSuite.py delete mode 100644 tests/token/erc721/test_ERC721EnumerableMintableBurnable.py delete mode 100644 tests/token/erc721/test_ERC721MintableBurnable.py delete mode 100644 tests/token/erc721/test_ERC721MintablePausable.py delete mode 100644 tests/token/erc721/test_ERC721SafeMintableMock.py delete mode 100644 tests/upgrades/test_Proxy.py delete mode 100644 tests/upgrades/test_upgrades.py delete mode 100644 tests/utils.py delete mode 100644 tests/utils/test_UniversalDeployer.py delete mode 100644 tox.ini diff --git a/.gitattributes b/.gitattributes index 039e8bc61..5ab9b291a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -*.cairo linguist-language=python +*.cairo linguist-language=rust diff --git a/.gitignore b/.gitignore index 984c53af2..73cda39c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,9 @@ artifacts/ +.DS_Store + +# Cairo 1 + +corelib/ # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 90ec14aba..000000000 --- a/MANIFEST.in +++ /dev/null @@ -1 +0,0 @@ -recursive-include src/ *.cairo diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 2c63dbb2b..000000000 --- a/pyproject.toml +++ /dev/null @@ -1,8 +0,0 @@ -[build-system] -# AVOID CHANGING REQUIRES: IT WILL BE UPDATED BY PYSCAFFOLD! -requires = ["setuptools>=46.1.0", "setuptools_scm[toml]>=5", "wheel"] -build-backend = "setuptools.build_meta" - -[tool.setuptools_scm] -# See configuration details in https://github.com/pypa/setuptools_scm -version_scheme = "no-guess-dev" diff --git a/scripts/update_version.py b/scripts/update_version.py deleted file mode 100644 index b48aebb24..000000000 --- a/scripts/update_version.py +++ /dev/null @@ -1,33 +0,0 @@ -import fileinput -import itertools -import sys -from pathlib import Path - -CURRENT_VERSION = "v0.6.1" -OTHER_PATHS = ["docs/antora.yml", "README.md"] - - -def main(): - new_version = str(sys.argv[1]) - src_path = Path("src") - docs_path = Path("docs") - for p in itertools.chain( - src_path.glob("**/*.cairo"), - docs_path.glob("**/*.adoc"), OTHER_PATHS): - _update_version(p, new_version) - _update_version("scripts/update_version.py", new_version) - - -def _update_version(path, version): - with fileinput.input(path, inplace=True) as file: - for line in file: - old, new = CURRENT_VERSION, version - if path in OTHER_PATHS: - old = old.strip("v") - new = new.strip("v") - new_line = line.replace(old, new) - print(new_line, end="") - - -if __name__ == "__main__": - main() diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index f2f12ebb3..000000000 --- a/setup.cfg +++ /dev/null @@ -1,37 +0,0 @@ -[metadata] -name = openzeppelin-cairo-contracts -version = attr: openzeppelin.__version__ -description = Library for secure smart contract development written in Cairo -author = OpenZeppelin Community -author_email = maintainers@openzeppelin.org -license = MIT -long_description = file: README.md -long_description_content_type = text/markdown; charset=UTF-8 -url = https://github.com/OpenZeppelin/cairo-contracts -platforms = any -classifiers = - Operating System :: OS Independent - -[options] -zip_safe = False -packages = find_namespace: -include_package_data = True -package_dir = - =src - -install_requires = - importlib-metadata>=4.0 - -[options.packages.find] -where = src -exclude = - tests - -[options.package_data] -openzeppelin = "*.cairo" - -[options.extras_require] -testing = - setuptools - tox - pytest diff --git a/setup.py b/setup.py deleted file mode 100644 index 7a781f058..000000000 --- a/setup.py +++ /dev/null @@ -1,13 +0,0 @@ -from setuptools import setup - -if __name__ == "__main__": - try: - setup(use_scm_version={"version_scheme": "no-guess-dev"}) - except: # noqa - print( - "\n\nAn error occurred while building the project, " - "please ensure you have the most updated version of setuptools, " - "setuptools_scm and wheel with:\n" - " pip install -U setuptools setuptools_scm wheel\n\n" - ) - raise diff --git a/src/openzeppelin/__init__.py b/src/openzeppelin/__init__.py deleted file mode 100644 index 3ad9240c2..000000000 --- a/src/openzeppelin/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -"""StarkNet/Cairo development toolbelt.""" - -try: - from importlib import metadata as importlib_metadata -except ImportError: - import importlib_metadata - -try: - __version__ = importlib_metadata.version("openzeppelin-cairo-contracts") -except importlib_metadata.PackageNotFoundError: - __version__ = None diff --git a/src/openzeppelin/access/accesscontrol/IAccessControl.cairo b/src/openzeppelin/access/accesscontrol/IAccessControl.cairo deleted file mode 100644 index d45bf8f72..000000000 --- a/src/openzeppelin/access/accesscontrol/IAccessControl.cairo +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (access/accesscontrol/IAccessControl.cairo) - -%lang starknet - -@contract_interface -namespace IAccessControl { - func hasRole(role: felt, account: felt) -> (hasRole: felt) { - } - - func getRoleAdmin(role: felt) -> (admin: felt) { - } - - func grantRole(role: felt, account: felt) { - } - - func revokeRole(role: felt, account: felt) { - } - - func renounceRole(role: felt, account: felt) { - } -} diff --git a/src/openzeppelin/access/accesscontrol/library.cairo b/src/openzeppelin/access/accesscontrol/library.cairo deleted file mode 100644 index 2571877a9..000000000 --- a/src/openzeppelin/access/accesscontrol/library.cairo +++ /dev/null @@ -1,154 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (access/accesscontrol/library.cairo) - -%lang starknet - -from starkware.starknet.common.syscalls import get_caller_address -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.bool import TRUE, FALSE - -from openzeppelin.introspection.erc165.library import ERC165 -from openzeppelin.utils.constants.library import IACCESSCONTROL_ID - -// -// Events -// - -@event -func RoleGranted(role: felt, account: felt, sender: felt) { -} - -@event -func RoleRevoked(role: felt, account: felt, sender: felt) { -} - -@event -func RoleAdminChanged(role: felt, previousAdminRole: felt, newAdminRole: felt) { -} - -// -// Storage -// - -@storage_var -func AccessControl_role_admin(role: felt) -> (admin: felt) { -} - -@storage_var -func AccessControl_role_member(role: felt, account: felt) -> (has_role: felt) { -} - -namespace AccessControl { - // - // Initializer - // - - func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - ERC165.register_interface(IACCESSCONTROL_ID); - return (); - } - - // - // Modifier - // - - func assert_only_role{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - role: felt - ) { - alloc_locals; - let (caller) = get_caller_address(); - let (authorized) = has_role(role, caller); - with_attr error_message("AccessControl: caller is missing role {role}") { - assert authorized = TRUE; - } - return (); - } - - // - // Getters - // - - func has_role{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - role: felt, user: felt - ) -> (has_role: felt) { - return AccessControl_role_member.read(role, user); - } - - func get_role_admin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - role: felt - ) -> (admin: felt) { - return AccessControl_role_admin.read(role); - } - - // - // Externals - // - - func grant_role{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - role: felt, user: felt - ) { - let (admin: felt) = get_role_admin(role); - assert_only_role(admin); - _grant_role(role, user); - return (); - } - - func revoke_role{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - role: felt, user: felt - ) { - let (admin: felt) = get_role_admin(role); - assert_only_role(admin); - _revoke_role(role, user); - return (); - } - - func renounce_role{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - role: felt, user: felt - ) { - let (caller: felt) = get_caller_address(); - with_attr error_message("AccessControl: can only renounce roles for self") { - assert user = caller; - } - _revoke_role(role, user); - return (); - } - - // - // Unprotected - // - - func _grant_role{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - role: felt, user: felt - ) { - let (user_has_role: felt) = has_role(role, user); - if (user_has_role == FALSE) { - let (caller: felt) = get_caller_address(); - AccessControl_role_member.write(role, user, TRUE); - RoleGranted.emit(role, user, caller); - return (); - } - return (); - } - - func _revoke_role{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - role: felt, user: felt - ) { - let (user_has_role: felt) = has_role(role, user); - if (user_has_role == TRUE) { - let (caller: felt) = get_caller_address(); - AccessControl_role_member.write(role, user, FALSE); - RoleRevoked.emit(role, user, caller); - return (); - } - return (); - } - - func _set_role_admin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - role: felt, admin_role: felt - ) { - let (previous_admin_role: felt) = get_role_admin(role); - AccessControl_role_admin.write(role, admin_role); - RoleAdminChanged.emit(role, previous_admin_role, admin_role); - return (); - } -} diff --git a/src/openzeppelin/access/ownable/library.cairo b/src/openzeppelin/access/ownable/library.cairo deleted file mode 100644 index 36f84cf98..000000000 --- a/src/openzeppelin/access/ownable/library.cairo +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (access/ownable/library.cairo) - -%lang starknet - -from starkware.starknet.common.syscalls import get_caller_address -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.math import assert_not_zero - -// -// Events -// - -@event -func OwnershipTransferred(previousOwner: felt, newOwner: felt) { -} - -// -// Storage -// - -@storage_var -func Ownable_owner() -> (owner: felt) { -} - -namespace Ownable { - // - // Initializer - // - - func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(owner: felt) { - _transfer_ownership(owner); - return (); - } - - // - // Guards - // - - func assert_only_owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - let (owner) = Ownable.owner(); - let (caller) = get_caller_address(); - with_attr error_message("Ownable: caller is the zero address") { - assert_not_zero(caller); - } - with_attr error_message("Ownable: caller is not the owner") { - assert owner = caller; - } - return (); - } - - // - // Public - // - - func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) { - return Ownable_owner.read(); - } - - func transfer_ownership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - new_owner: felt - ) { - with_attr error_message("Ownable: new owner is the zero address") { - assert_not_zero(new_owner); - } - assert_only_owner(); - _transfer_ownership(new_owner); - return (); - } - - func renounce_ownership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - assert_only_owner(); - _transfer_ownership(0); - return (); - } - - // - // Internal - // - - func _transfer_ownership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - new_owner: felt - ) { - let (previous_owner: felt) = Ownable.owner(); - Ownable_owner.write(new_owner); - OwnershipTransferred.emit(previous_owner, new_owner); - return (); - } -} diff --git a/src/openzeppelin/account/IAccount.cairo b/src/openzeppelin/account/IAccount.cairo deleted file mode 100644 index 8a22d38c1..000000000 --- a/src/openzeppelin/account/IAccount.cairo +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (account/IAccount.cairo) - -%lang starknet - -from openzeppelin.account.library import AccountCallArray - -@contract_interface -namespace IAccount { - func isValidSignature( - hash: felt, - signature_len: felt, - signature: felt* - ) -> (isValid: felt) { - } - - func __validate__( - call_array_len: felt, - call_array: AccountCallArray*, - calldata_len: felt, - calldata: felt* - ) { - } - - // Parameter temporarily named `cls_hash` instead of `class_hash` (expected). - // See https://github.com/starkware-libs/cairo-lang/issues/100 for details. - func __validate_declare__(cls_hash: felt) { - } - - func __execute__( - call_array_len: felt, - call_array: AccountCallArray*, - calldata_len: felt, - calldata: felt* - ) -> ( - response_len: felt, - response: felt* - ) { - } - - // ERC165 - - func supportsInterface(interfaceId: felt) -> (success: felt) { - } -} diff --git a/src/openzeppelin/account/library.cairo b/src/openzeppelin/account/library.cairo deleted file mode 100644 index 6723032eb..000000000 --- a/src/openzeppelin/account/library.cairo +++ /dev/null @@ -1,260 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (account/library.cairo) - -%lang starknet - -from starkware.cairo.common.registers import get_fp_and_pc -from starkware.cairo.common.signature import verify_ecdsa_signature -from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin, BitwiseBuiltin -from starkware.cairo.common.alloc import alloc -from starkware.cairo.common.uint256 import Uint256 -from starkware.cairo.common.memcpy import memcpy -from starkware.cairo.common.math import split_felt -from starkware.cairo.common.math_cmp import is_le_felt -from starkware.cairo.common.bool import TRUE, FALSE -from starkware.starknet.common.syscalls import ( - call_contract, - get_caller_address, - get_contract_address, - get_tx_info -) -from starkware.cairo.common.cairo_secp.signature import ( - finalize_keccak, - verify_eth_signature_uint256 -) -from openzeppelin.utils.constants.library import ( - IACCOUNT_ID, - IERC165_ID, - TRANSACTION_VERSION -) - -// -// Storage -// - -@storage_var -func Account_public_key() -> (public_key: felt) { -} - -// -// Structs -// - -struct Call { - to: felt, - selector: felt, - calldata_len: felt, - calldata: felt*, -} - -// Tmp struct introduced while we wait for Cairo -// to support passing `[AccountCall]` to __execute__ -struct AccountCallArray { - to: felt, - selector: felt, - data_offset: felt, - data_len: felt, -} - -namespace Account { - // - // Initializer - // - - func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - _public_key: felt - ) { - Account_public_key.write(_public_key); - return (); - } - - // - // Guards - // - - func assert_only_self{syscall_ptr: felt*}() { - let (self) = get_contract_address(); - let (caller) = get_caller_address(); - with_attr error_message("Account: caller is not this account") { - assert self = caller; - } - return (); - } - - // - // Getters - // - - func get_public_key{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - public_key: felt - ) { - return Account_public_key.read(); - } - - func supports_interface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(interface_id: felt) -> ( - success: felt - ) { - if (interface_id == IERC165_ID) { - return (success=TRUE); - } - if (interface_id == IACCOUNT_ID) { - return (success=TRUE); - } - return (success=FALSE); - } - - // - // Setters - // - - func set_public_key{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - new_public_key: felt - ) { - assert_only_self(); - Account_public_key.write(new_public_key); - return (); - } - - // - // Business logic - // - - func is_valid_signature{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - ecdsa_ptr: SignatureBuiltin*, - range_check_ptr, - }(hash: felt, signature_len: felt, signature: felt*) -> (is_valid: felt) { - let (_public_key) = Account_public_key.read(); - - // This interface expects a signature pointer and length to make - // no assumption about signature validation schemes. - // But this implementation does, and it expects a (sig_r, sig_s) pair. - let sig_r = signature[0]; - let sig_s = signature[1]; - - verify_ecdsa_signature( - message=hash, public_key=_public_key, signature_r=sig_r, signature_s=sig_s - ); - - return (is_valid=TRUE); - } - - func is_valid_eth_signature{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - bitwise_ptr: BitwiseBuiltin*, - range_check_ptr, - }(hash: felt, signature_len: felt, signature: felt*) -> (is_valid: felt) { - alloc_locals; - let (_public_key) = get_public_key(); - let (__fp__, _) = get_fp_and_pc(); - - // This interface expects a signature pointer and length to make - // no assumption about signature validation schemes. - // But this implementation does, and it expects a the sig_v, sig_r, - // sig_s, and hash elements. - let sig_v: felt = signature[0]; - let sig_r: Uint256 = Uint256(low=signature[1], high=signature[2]); - let sig_s: Uint256 = Uint256(low=signature[3], high=signature[4]); - let (high, low) = split_felt(hash); - let msg_hash: Uint256 = Uint256(low=low, high=high); - - let (keccak_ptr: felt*) = alloc(); - local keccak_ptr_start: felt* = keccak_ptr; - - with keccak_ptr { - verify_eth_signature_uint256( - msg_hash=msg_hash, r=sig_r, s=sig_s, v=sig_v, eth_address=_public_key - ); - } - // Required to ensure sequencers cannot spoof validation check. - finalize_keccak(keccak_ptr_start=keccak_ptr_start, keccak_ptr_end=keccak_ptr); - - return (is_valid=TRUE); - } - - func execute{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - ecdsa_ptr: SignatureBuiltin*, - bitwise_ptr: BitwiseBuiltin*, - range_check_ptr, - }(call_array_len: felt, call_array: AccountCallArray*, calldata_len: felt, calldata: felt*) -> ( - response_len: felt, response: felt* - ) { - alloc_locals; - - let (tx_info) = get_tx_info(); - // Disallow deprecated tx versions - with_attr error_message("Account: deprecated tx version") { - assert is_le_felt(TRANSACTION_VERSION, tx_info.version) = TRUE; - } - - // Assert not a reentrant call - let (caller) = get_caller_address(); - with_attr error_message("Account: reentrant call") { - assert caller = 0; - } - - // TMP: Convert `AccountCallArray` to 'Call'. - let (calls: Call*) = alloc(); - _from_call_array_to_call(call_array_len, call_array, calldata, calls); - let calls_len = call_array_len; - - // Execute call - let (response: felt*) = alloc(); - let (response_len) = _execute_list(calls_len, calls, response); - - return (response_len=response_len, response=response); - } - - func _execute_list{syscall_ptr: felt*}(calls_len: felt, calls: Call*, response: felt*) -> ( - response_len: felt - ) { - alloc_locals; - - // if no more calls - if (calls_len == 0) { - return (response_len=0); - } - - // do the current call - let this_call: Call = [calls]; - let res = call_contract( - contract_address=this_call.to, - function_selector=this_call.selector, - calldata_size=this_call.calldata_len, - calldata=this_call.calldata, - ); - // copy the result in response - memcpy(response, res.retdata, res.retdata_size); - // do the next calls recursively - let (response_len) = _execute_list( - calls_len - 1, calls + Call.SIZE, response + res.retdata_size - ); - return (response_len=response_len + res.retdata_size); - } - - func _from_call_array_to_call{syscall_ptr: felt*}( - call_array_len: felt, call_array: AccountCallArray*, calldata: felt*, calls: Call* - ) { - // if no more calls - if (call_array_len == 0) { - return (); - } - - // parse the current call - assert [calls] = Call( - to=[call_array].to, - selector=[call_array].selector, - calldata_len=[call_array].data_len, - calldata=calldata + [call_array].data_offset - ); - // parse the remaining calls recursively - _from_call_array_to_call( - call_array_len - 1, call_array + AccountCallArray.SIZE, calldata, calls + Call.SIZE - ); - return (); - } -} diff --git a/src/openzeppelin/account/presets/Account.cairo b/src/openzeppelin/account/presets/Account.cairo deleted file mode 100644 index 23c10199b..000000000 --- a/src/openzeppelin/account/presets/Account.cairo +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (account/presets/Account.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin, BitwiseBuiltin -from starkware.starknet.common.syscalls import get_tx_info - -from openzeppelin.account.library import Account, AccountCallArray - - -// -// Constructor -// - -@constructor -func constructor{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - range_check_ptr -}(publicKey: felt) { - Account.initializer(publicKey); - return (); -} - -// -// Getters -// - -@view -func getPublicKey{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - range_check_ptr -} () -> (publicKey: felt) { - let (publicKey: felt) = Account.get_public_key(); - return (publicKey=publicKey); -} - -@view -func supportsInterface{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - range_check_ptr -} (interfaceId: felt) -> (success: felt) { - return Account.supports_interface(interfaceId); -} - -// -// Setters -// - -@external -func setPublicKey{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - range_check_ptr -} (newPublicKey: felt) { - Account.set_public_key(newPublicKey); - return (); -} - -// -// Business logic -// - -@view -func isValidSignature{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - ecdsa_ptr: SignatureBuiltin*, - range_check_ptr -}( - hash: felt, - signature_len: felt, - signature: felt* -) -> (isValid: felt) { - let (isValid: felt) = Account.is_valid_signature(hash, signature_len, signature); - return (isValid=isValid); -} - -@external -func __validate__{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - ecdsa_ptr: SignatureBuiltin*, - range_check_ptr -}( - call_array_len: felt, - call_array: AccountCallArray*, - calldata_len: felt, - calldata: felt* -) { - let (tx_info) = get_tx_info(); - Account.is_valid_signature(tx_info.transaction_hash, tx_info.signature_len, tx_info.signature); - return (); -} - -@external -func __validate_declare__{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - ecdsa_ptr: SignatureBuiltin*, - range_check_ptr -} (class_hash: felt) { - let (tx_info) = get_tx_info(); - Account.is_valid_signature(tx_info.transaction_hash, tx_info.signature_len, tx_info.signature); - return (); -} - -@external -func __validate_deploy__{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - ecdsa_ptr: SignatureBuiltin*, - range_check_ptr -} ( - class_hash: felt, - salt: felt, - publicKey: felt -) { - let (tx_info) = get_tx_info(); - Account.is_valid_signature(tx_info.transaction_hash, tx_info.signature_len, tx_info.signature); - return (); -} - -@external -func __execute__{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - ecdsa_ptr: SignatureBuiltin*, - bitwise_ptr: BitwiseBuiltin*, - range_check_ptr, -}( - call_array_len: felt, - call_array: AccountCallArray*, - calldata_len: felt, - calldata: felt* -) -> ( - response_len: felt, - response: felt* -) { - let (response_len, response) = Account.execute( - call_array_len, call_array, calldata_len, calldata - ); - return (response_len, response); -} diff --git a/src/openzeppelin/account/presets/AddressRegistry.cairo b/src/openzeppelin/account/presets/AddressRegistry.cairo deleted file mode 100644 index 7076bd25a..000000000 --- a/src/openzeppelin/account/presets/AddressRegistry.cairo +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (account/presets/AddressRegistry.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.starknet.common.syscalls import get_caller_address - -@storage_var -func L1_address(L2_address: felt) -> (address: felt) { -} - -@external -func get_L1_address{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - L2_address: felt -) -> (address: felt) { - return L1_address.read(L2_address); -} - -@external -func set_L1_address{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - new_L1_address: felt -) { - let (caller) = get_caller_address(); - L1_address.write(caller, new_L1_address); - return (); -} diff --git a/src/openzeppelin/account/presets/EthAccount.cairo b/src/openzeppelin/account/presets/EthAccount.cairo deleted file mode 100644 index 50277380d..000000000 --- a/src/openzeppelin/account/presets/EthAccount.cairo +++ /dev/null @@ -1,146 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (account/presets/EthAccount.cairo) - -%lang starknet -from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin, BitwiseBuiltin -from starkware.starknet.common.syscalls import get_tx_info - -from openzeppelin.account.library import Account, AccountCallArray - -// -// Constructor -// - -@constructor -func constructor{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - range_check_ptr -}(ethAddress: felt) { - Account.initializer(ethAddress); - return (); -} - -// -// Getters -// - -@view -func getEthAddress{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - range_check_ptr -} () -> (ethAddress: felt) { - let (ethAddress: felt) = Account.get_public_key(); - return (ethAddress=ethAddress); -} - -@view -func supportsInterface{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - range_check_ptr -} (interfaceId: felt) -> (success: felt) { - return Account.supports_interface(interfaceId); -} - -// -// Setters -// - -@external -func setEthAddress{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - range_check_ptr -} (newEthAddress: felt) { - Account.set_public_key(newEthAddress); - return (); -} - -// -// Business logic -// - -@view -func isValidSignature{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - bitwise_ptr: BitwiseBuiltin*, - range_check_ptr, -}( - hash: felt, - signature_len: felt, - signature: felt* -) -> (isValid: felt) { - let (isValid) = Account.is_valid_eth_signature(hash, signature_len, signature); - return (isValid=isValid); -} - -@external -func __validate__{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - bitwise_ptr: BitwiseBuiltin*, - range_check_ptr, -}( - call_array_len: felt, - call_array: AccountCallArray*, - calldata_len: felt, - calldata: felt* -) { - let (tx_info) = get_tx_info(); - Account.is_valid_eth_signature(tx_info.transaction_hash, tx_info.signature_len, tx_info.signature); - return (); -} - -@external -func __validate_declare__{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - bitwise_ptr: BitwiseBuiltin*, - range_check_ptr, -} (class_hash: felt) { - let (tx_info) = get_tx_info(); - Account.is_valid_eth_signature(tx_info.transaction_hash, tx_info.signature_len, tx_info.signature); - return (); -} - - -@external -func __validate_deploy__{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - bitwise_ptr: BitwiseBuiltin*, - range_check_ptr -} ( - class_hash: felt, - salt: felt, - ethAddress: felt -) { - let (tx_info) = get_tx_info(); - Account.is_valid_eth_signature(tx_info.transaction_hash, tx_info.signature_len, tx_info.signature); - return (); -} - -@external -func __execute__{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - ecdsa_ptr: SignatureBuiltin*, - bitwise_ptr: BitwiseBuiltin*, - range_check_ptr, -}( - call_array_len: felt, - call_array: AccountCallArray*, - calldata_len: felt, - calldata: felt* -) -> ( - response_len: felt, - response: felt* -) { - let (response_len, response) = Account.execute( - call_array_len, call_array, calldata_len, calldata - ); - return (response_len, response); -} diff --git a/src/openzeppelin/introspection/erc165/IERC165.cairo b/src/openzeppelin/introspection/erc165/IERC165.cairo deleted file mode 100644 index d34be3a33..000000000 --- a/src/openzeppelin/introspection/erc165/IERC165.cairo +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (introspection/erc165/IERC165.cairo) - -%lang starknet - -@contract_interface -namespace IERC165 { - func supportsInterface(interfaceId: felt) -> (success: felt) { - } -} diff --git a/src/openzeppelin/introspection/erc165/library.cairo b/src/openzeppelin/introspection/erc165/library.cairo deleted file mode 100644 index 8073cef40..000000000 --- a/src/openzeppelin/introspection/erc165/library.cairo +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (introspection/erc165/library.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.math import assert_not_equal -from starkware.cairo.common.bool import TRUE - -from openzeppelin.utils.constants.library import INVALID_ID, IERC165_ID - -@storage_var -func ERC165_supported_interfaces(interface_id: felt) -> (is_supported: felt) { -} - -namespace ERC165 { - func supports_interface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - interface_id: felt - ) -> (success: felt) { - if (interface_id == IERC165_ID) { - return (success=TRUE); - } - - // Checks interface registry - let (is_supported) = ERC165_supported_interfaces.read(interface_id); - return (success=is_supported); - } - - func register_interface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - interface_id: felt - ) { - with_attr error_message("ERC165: invalid interface id") { - assert_not_equal(interface_id, INVALID_ID); - } - ERC165_supported_interfaces.write(interface_id, TRUE); - return (); - } -} diff --git a/src/openzeppelin/security/initializable/library.cairo b/src/openzeppelin/security/initializable/library.cairo deleted file mode 100644 index e1af27aea..000000000 --- a/src/openzeppelin/security/initializable/library.cairo +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (security/initializable/library.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.bool import TRUE, FALSE - -@storage_var -func Initializable_initialized() -> (initialized: felt) { -} - -namespace Initializable { - func initialized{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - felt - ) { - return Initializable_initialized.read(); - } - - func initialize{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - let (is_initialized) = Initializable_initialized.read(); - with_attr error_message("Initializable: contract already initialized") { - assert is_initialized = FALSE; - } - Initializable_initialized.write(TRUE); - return (); - } -} diff --git a/src/openzeppelin/security/pausable/library.cairo b/src/openzeppelin/security/pausable/library.cairo deleted file mode 100644 index fecec4954..000000000 --- a/src/openzeppelin/security/pausable/library.cairo +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (security/pausable/library.cairo) - -%lang starknet - -from starkware.starknet.common.syscalls import get_caller_address -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.bool import TRUE, FALSE - -// -// Storage -// - -@storage_var -func Pausable_paused() -> (paused: felt) { -} - -// -// Events -// - -@event -func Paused(account: felt) { -} - -@event -func Unpaused(account: felt) { -} - -namespace Pausable { - func is_paused{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - paused: felt - ) { - return Pausable_paused.read(); - } - - func assert_not_paused{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - let (is_paused) = Pausable_paused.read(); - with_attr error_message("Pausable: paused") { - assert is_paused = FALSE; - } - return (); - } - - func assert_paused{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - let (is_paused) = Pausable_paused.read(); - with_attr error_message("Pausable: not paused") { - assert is_paused = TRUE; - } - return (); - } - - func _pause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - assert_not_paused(); - Pausable_paused.write(TRUE); - - let (account) = get_caller_address(); - Paused.emit(account); - return (); - } - - func _unpause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - assert_paused(); - Pausable_paused.write(FALSE); - - let (account) = get_caller_address(); - Unpaused.emit(account); - return (); - } -} diff --git a/src/openzeppelin/security/reentrancyguard/library.cairo b/src/openzeppelin/security/reentrancyguard/library.cairo deleted file mode 100644 index 84914aee7..000000000 --- a/src/openzeppelin/security/reentrancyguard/library.cairo +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (security/reentrancyguard/library.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.bool import TRUE, FALSE - -@storage_var -func ReentrancyGuard_entered() -> (entered: felt) { -} - -namespace ReentrancyGuard { - func start{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - let (has_entered) = ReentrancyGuard_entered.read(); - with_attr error_message("ReentrancyGuard: reentrant call") { - assert has_entered = FALSE; - } - ReentrancyGuard_entered.write(TRUE); - return (); - } - - func end{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - ReentrancyGuard_entered.write(FALSE); - return (); - } -} diff --git a/src/openzeppelin/security/safemath/library.cairo b/src/openzeppelin/security/safemath/library.cairo deleted file mode 100644 index c31c1b623..000000000 --- a/src/openzeppelin/security/safemath/library.cairo +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (security/safemath/library.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.bool import TRUE, FALSE -from starkware.cairo.common.uint256 import ( - Uint256, - uint256_check, - uint256_add, - uint256_sub, - uint256_mul, - uint256_unsigned_div_rem, - uint256_le, - uint256_lt, - uint256_eq, -) - -namespace SafeUint256 { - // Adds two integers. - // Reverts if the sum overflows. - func add{range_check_ptr}(a: Uint256, b: Uint256) -> (c: Uint256) { - uint256_check(a); - uint256_check(b); - let (c: Uint256, is_overflow) = uint256_add(a, b); - with_attr error_message("SafeUint256: addition overflow") { - assert is_overflow = FALSE; - } - return (c=c); - } - - // Subtracts two integers. - // Reverts if subtrahend (`b`) is greater than minuend (`a`). - func sub_le{range_check_ptr}(a: Uint256, b: Uint256) -> (c: Uint256) { - alloc_locals; - uint256_check(a); - uint256_check(b); - let (is_le) = uint256_le(b, a); - with_attr error_message("SafeUint256: subtraction overflow") { - assert is_le = TRUE; - } - let (c: Uint256) = uint256_sub(a, b); - return (c=c); - } - - // Subtracts two integers. - // Reverts if subtrahend (`b`) is greater than or equal to minuend (`a`). - func sub_lt{range_check_ptr}(a: Uint256, b: Uint256) -> (c: Uint256) { - alloc_locals; - uint256_check(a); - uint256_check(b); - - let (is_lt) = uint256_lt(b, a); - with_attr error_message("SafeUint256: subtraction overflow or the difference equals zero") { - assert is_lt = TRUE; - } - let (c: Uint256) = uint256_sub(a, b); - return (c=c); - } - - // Multiplies two integers. - // Reverts if product is greater than 2^256. - func mul{range_check_ptr}(a: Uint256, b: Uint256) -> (c: Uint256) { - alloc_locals; - uint256_check(a); - uint256_check(b); - let (a_zero) = uint256_eq(a, Uint256(0, 0)); - if (a_zero == TRUE) { - return (c=a); - } - - let (b_zero) = uint256_eq(b, Uint256(0, 0)); - if (b_zero == TRUE) { - return (c=b); - } - - let (c: Uint256, overflow: Uint256) = uint256_mul(a, b); - with_attr error_message("SafeUint256: multiplication overflow") { - assert overflow = Uint256(0, 0); - } - return (c=c); - } - - // Integer division of two numbers. Returns uint256 quotient and remainder. - // Reverts if divisor is zero as per OpenZeppelin's Solidity implementation. - // Cairo's `uint256_unsigned_div_rem` already checks: - // remainder < divisor - // quotient * divisor + remainder == dividend - func div_rem{range_check_ptr}(a: Uint256, b: Uint256) -> (c: Uint256, rem: Uint256) { - alloc_locals; - uint256_check(a); - uint256_check(b); - - let (is_zero) = uint256_eq(b, Uint256(0, 0)); - with_attr error_message("SafeUint256: divisor cannot be zero") { - assert is_zero = FALSE; - } - - let (c: Uint256, rem: Uint256) = uint256_unsigned_div_rem(a, b); - return (c=c, rem=rem); - } -} diff --git a/src/openzeppelin/token/erc1155/IERC1155.cairo b/src/openzeppelin/token/erc1155/IERC1155.cairo deleted file mode 100644 index 95465eb8e..000000000 --- a/src/openzeppelin/token/erc1155/IERC1155.cairo +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc1155/IERC1155.cairo) - -%lang starknet - -from starkware.cairo.common.uint256 import Uint256 - -@contract_interface -namespace IERC1155 { - func balanceOf(account: felt, id: Uint256) -> (balance: Uint256) { - } - - func balanceOfBatch( - accounts_len: felt, - accounts: felt*, - ids_len: felt, - ids: Uint256* - ) -> ( - balances_len: felt, - balances: Uint256* - ) { - } - - func isApprovedForAll(account: felt, operator: felt) -> (approved: felt) { - } - - func setApprovalForAll(operator: felt, approved: felt) { - } - - func safeTransferFrom( - from_: felt, - to: felt, - id: Uint256, - value: Uint256, - data_len: felt, - data: felt* - ) { - } - - func safeBatchTransferFrom( - from_: felt, - to: felt, - ids_len: felt, - ids: Uint256*, - values_len: felt, - values: Uint256*, - data_len: felt, - data: felt*, - ) { - } - - // ERC165 - - func supportsInterface(interfaceId: felt) -> (success: felt) { - } -} diff --git a/src/openzeppelin/token/erc1155/IERC1155MetadataURI.cairo b/src/openzeppelin/token/erc1155/IERC1155MetadataURI.cairo deleted file mode 100644 index 100f157dc..000000000 --- a/src/openzeppelin/token/erc1155/IERC1155MetadataURI.cairo +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc1155/IERC1155MetadataURI.cairo) - -%lang starknet - -from starkware.cairo.common.uint256 import Uint256 - -@contract_interface -namespace IERC1155MetadataURI { - func uri(id: Uint256) -> (uri: felt) { - } - - // ERC1155 - - func balanceOf(account: felt, id: Uint256) -> (balance: Uint256) { - } - - func balanceOfBatch(accounts_len: felt, accounts: felt*, ids_len: felt, ids: Uint256*) -> ( - balances_len: felt, balances: Uint256* - ) { - } - - func isApprovedForAll(account: felt, operator: felt) -> (approved: felt) { - } - - func setApprovalForAll(operator: felt, approved: felt) { - } - - func safeTransferFrom( - from_: felt, to: felt, id: Uint256, value: Uint256, data_len: felt, data: felt* - ) { - } - - func safeBatchTransferFrom( - from_: felt, - to: felt, - ids_len: felt, - ids: Uint256*, - values_len: felt, - values: Uint256*, - data_len: felt, - data: felt*, - ) { - } - - // ERC165 - - func supportsInterface(interfaceId: felt) -> (success: felt) { - } -} diff --git a/src/openzeppelin/token/erc1155/IERC1155Receiver.cairo b/src/openzeppelin/token/erc1155/IERC1155Receiver.cairo deleted file mode 100644 index 1466d6ba6..000000000 --- a/src/openzeppelin/token/erc1155/IERC1155Receiver.cairo +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc1155/IERC1155Receiver.cairo) - -%lang starknet - -from starkware.cairo.common.uint256 import Uint256 - -@contract_interface -namespace IERC1155Receiver { - func onERC1155Received( - operator: felt, - from_: felt, - id: Uint256, - value: Uint256, - data_len: felt, - data: felt* - ) -> (selector: felt) { - } - - func onERC1155BatchReceived( - operator: felt, - from_: felt, - ids_len: felt, - ids: Uint256*, - values_len: felt, - values: Uint256*, - data_len: felt, - data: felt*, - ) -> (selector: felt) { - } - - // ERC165 - - func supportsInterface(interfaceId: felt) -> (success: felt) { - } -} diff --git a/src/openzeppelin/token/erc1155/library.cairo b/src/openzeppelin/token/erc1155/library.cairo deleted file mode 100644 index 27622e84a..000000000 --- a/src/openzeppelin/token/erc1155/library.cairo +++ /dev/null @@ -1,602 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc1155/library.cairo) - -%lang starknet - -from starkware.starknet.common.syscalls import get_caller_address -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.math import assert_not_zero, assert_not_equal -from starkware.cairo.common.alloc import alloc -from starkware.cairo.common.uint256 import Uint256, uint256_check -from starkware.cairo.common.bool import TRUE - -from openzeppelin.introspection.erc165.IERC165 import IERC165 -from openzeppelin.introspection.erc165.library import ERC165 -from openzeppelin.token.erc1155.IERC1155Receiver import IERC1155Receiver -from openzeppelin.security.safemath.library import SafeUint256 -from openzeppelin.utils.constants.library import ( - IERC1155_ID, - IERC1155_METADATA_ID, - IERC1155_RECEIVER_ID, - IACCOUNT_ID, - ON_ERC1155_RECEIVED_SELECTOR, - ON_ERC1155_BATCH_RECEIVED_SELECTOR, -) - -// -// Events -// - -@event -func TransferSingle( - operator: felt, - from_: felt, - to: felt, - id: Uint256, - value: Uint256 -) { -} - -@event -func TransferBatch( - operator: felt, - from_: felt, - to: felt, - ids_len: felt, - ids: Uint256*, - values_len: felt, - values: Uint256*, -) { -} - -@event -func ApprovalForAll(account: felt, operator: felt, approved: felt) { -} - -@event -func URI(value: felt, id: Uint256) { -} - -// -// Storage -// - -@storage_var -func ERC1155_balances(id: Uint256, account: felt) -> (balance: Uint256) { -} - -@storage_var -func ERC1155_operator_approvals(account: felt, operator: felt) -> (approved: felt) { -} - -@storage_var -func ERC1155_uri() -> (uri: felt) { -} - -namespace ERC1155 { - // - // Initializer - // - - func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(uri: felt) { - _set_uri(uri); - ERC165.register_interface(IERC1155_ID); - ERC165.register_interface(IERC1155_METADATA_ID); - return (); - } - - // - // Modifiers - // - - func assert_owner_or_approved{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - owner: felt - ) { - let (caller) = get_caller_address(); - if (caller == owner) { - return (); - } - let (approved) = ERC1155.is_approved_for_all(owner, caller); - with_attr error_message("ERC1155: caller is not owner nor approved") { - assert approved = TRUE; - } - return (); - } - - // - // Getters - // - - // This implementation returns the same URI for *all* token types. It relies - // on the token type ID substitution mechanism - // https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. - // - // Clients calling this function must replace the `\{id\}` substring with the - // actual token type ID. - func uri{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(id: Uint256) -> ( - uri: felt - ) { - return ERC1155_uri.read(); - } - - func balance_of{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - account: felt, id: Uint256 - ) -> (balance: Uint256) { - with_attr error_message("ERC1155: address zero is not a valid owner") { - assert_not_zero(account); - } - _check_id(id); - return ERC1155_balances.read(id, account); - } - - func balance_of_batch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - accounts_len: felt, accounts: felt*, ids_len: felt, ids: Uint256* - ) -> (balances_len: felt, balances: Uint256*) { - alloc_locals; - // Check args are equal length arrays - with_attr error_message("ERC1155: accounts and ids length mismatch") { - assert ids_len = accounts_len; - } - // Allocate memory - let (local balances: Uint256*) = alloc(); - // Call iterator - _balance_of_batch_iter(accounts_len, accounts, ids, balances); - return (accounts_len, balances); - } - - func is_approved_for_all{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - account: felt, operator: felt - ) -> (approved: felt) { - return ERC1155_operator_approvals.read(account, operator); - } - - // - // Externals - // - - func set_approval_for_all{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - operator: felt, approved: felt - ) { - let (caller) = get_caller_address(); - with_attr error_message("ERC1155: cannot approve from the zero address") { - assert_not_zero(caller); - } - _set_approval_for_all(caller, operator, approved); - return (); - } - - func safe_transfer_from{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - from_: felt, to: felt, id: Uint256, value: Uint256, data_len: felt, data: felt* - ) { - let (caller) = get_caller_address(); - with_attr error_message("ERC1155: cannot call transfer from the zero address") { - assert_not_zero(caller); - } - assert_owner_or_approved(from_); - _safe_transfer_from(from_, to, id, value, data_len, data); - return (); - } - - func safe_batch_transfer_from{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - from_: felt, - to: felt, - ids_len: felt, - ids: Uint256*, - values_len: felt, - values: Uint256*, - data_len: felt, - data: felt*, - ) { - let (caller) = get_caller_address(); - with_attr error_message("ERC1155: cannot call transfer from the zero address") { - assert_not_zero(caller); - } - assert_owner_or_approved(from_); - _safe_batch_transfer_from(from_, to, ids_len, ids, values_len, values, data_len, data); - return (); - } - - // - // Internals - // - - func _safe_transfer_from{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - from_: felt, to: felt, id: Uint256, value: Uint256, data_len: felt, data: felt* - ) { - alloc_locals; - // Validate input - with_attr error_message("ERC1155: transfer to the zero address") { - assert_not_zero(to); - } - _check_id(id); - _check_value(value); - - // Deduct from sender - let (from_balance: Uint256) = ERC1155_balances.read(id, from_); - with_attr error_message("ERC1155: insufficient balance for transfer") { - let (new_balance: Uint256) = SafeUint256.sub_le(from_balance, value); - } - ERC1155_balances.write(id, from_, new_balance); - - // Add to receiver - _add_to_receiver(id, value, to); - - // Emit events and check - let (operator) = get_caller_address(); - TransferSingle.emit(operator, from_, to, id, value); - - _do_safe_transfer_acceptance_check(operator, from_, to, id, value, data_len, data); - return (); - } - - func _safe_batch_transfer_from{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - from_: felt, - to: felt, - ids_len: felt, - ids: Uint256*, - values_len: felt, - values: Uint256*, - data_len: felt, - data: felt*, - ) { - alloc_locals; - // Check args - with_attr error_message("ERC1155: transfer to the zero address") { - assert_not_zero(to); - } - with_attr error_message("ERC1155: ids and values length mismatch") { - assert ids_len = values_len; - } - // Recursive call - _safe_batch_transfer_from_iter(from_, to, ids_len, ids, values); - - // Emit events and check - let (operator) = get_caller_address(); - TransferBatch.emit(operator, from_, to, ids_len, ids, values_len, values); - - _do_safe_batch_transfer_acceptance_check( - operator, from_, to, ids_len, ids, values_len, values, data_len, data - ); - return (); - } - - func _mint{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - to: felt, id: Uint256, value: Uint256, data_len: felt, data: felt* - ) { - // Validate input - with_attr error_message("ERC1155: mint to the zero address") { - assert_not_zero(to); - } - _check_id(id); - _check_value(value); - - // Add to minter, check for overflow - _add_to_receiver(id, value, to); - - // Emit events and check - let (operator) = get_caller_address(); - TransferSingle.emit(operator=operator, from_=0, to=to, id=id, value=value); - _do_safe_transfer_acceptance_check( - operator=operator, from_=0, to=to, id=id, value=value, data_len=data_len, data=data - ); - return (); - } - - func _mint_batch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - to: felt, - ids_len: felt, - ids: Uint256*, - values_len: felt, - values: Uint256*, - data_len: felt, - data: felt*, - ) { - alloc_locals; - // Cannot mint to zero address - with_attr error_message("ERC1155: mint to the zero address") { - assert_not_zero(to); - } - // Check args are equal length arrays - with_attr error_message("ERC1155: ids and values length mismatch") { - assert ids_len = values_len; - } - - // Recursive call - _mint_batch_iter(to, ids_len, ids, values); - - // Emit events and check - let (operator) = get_caller_address(); - TransferBatch.emit( - operator=operator, - from_=0, - to=to, - ids_len=ids_len, - ids=ids, - values_len=values_len, - values=values, - ); - _do_safe_batch_transfer_acceptance_check( - operator=operator, - from_=0, - to=to, - ids_len=ids_len, - ids=ids, - values_len=values_len, - values=values, - data_len=data_len, - data=data, - ); - return (); - } - - func _burn{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - from_: felt, id: Uint256, value: Uint256 - ) { - alloc_locals; - // Validate input - with_attr error_message("ERC1155: burn from the zero address") { - assert_not_zero(from_); - } - _check_id(id); - _check_value(value); - - // Deduct from burner - let (from_balance: Uint256) = ERC1155_balances.read(id, from_); - with_attr error_message("ERC1155: burn value exceeds balance") { - let (new_balance: Uint256) = SafeUint256.sub_le(from_balance, value); - } - - ERC1155_balances.write(id, from_, new_balance); - - let (operator) = get_caller_address(); - TransferSingle.emit(operator=operator, from_=from_, to=0, id=id, value=value); - return (); - } - - func _burn_batch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - from_: felt, ids_len: felt, ids: Uint256*, values_len: felt, values: Uint256* - ) { - alloc_locals; - with_attr error_message("ERC1155: burn from the zero address") { - assert_not_zero(from_); - } - with_attr error_message("ERC1155: ids and values length mismatch") { - assert ids_len = values_len; - } - - // Recursive call - _burn_batch_iter(from_, ids_len, ids, values); - let (operator) = get_caller_address(); - TransferBatch.emit( - operator=operator, - from_=from_, - to=0, - ids_len=ids_len, - ids=ids, - values_len=values_len, - values=values, - ); - return (); - } - - func _set_approval_for_all{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - owner: felt, operator: felt, approved: felt - ) { - // check approved is bool - with_attr error_message("ERC1155: approval is not boolean") { - assert approved * (approved - 1) = 0; - } - - // caller/owner already checked non-0 - with_attr error_message("ERC1155: setting approval status for zero address") { - assert_not_zero(operator); - } - - with_attr error_message("ERC1155: setting approval status for self") { - assert_not_equal(owner, operator); - } - - ERC1155_operator_approvals.write(owner, operator, approved); - ApprovalForAll.emit(owner, operator, approved); - return (); - } - - func _set_uri{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(uri: felt) { - ERC1155_uri.write(uri); - return (); - } -} - -// -// Private -// - -func _do_safe_transfer_acceptance_check{ - syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr -}( - operator: felt, from_: felt, to: felt, id: Uint256, value: Uint256, data_len: felt, data: felt* -) { - // Confirm supports IERC1155receiver interface - let (is_supported) = IERC165.supportsInterface(to, IERC1155_RECEIVER_ID); - if (is_supported == TRUE) { - let (selector) = IERC1155Receiver.onERC1155Received( - to, operator, from_, id, value, data_len, data - ); - - // Confirm onERC1155Recieved selector returned - with_attr error_message("ERC1155: ERC1155Receiver rejected tokens") { - assert selector = ON_ERC1155_RECEIVED_SELECTOR; - } - return (); - } - - // Alternatively confirm account - let (is_account) = IERC165.supportsInterface(to, IACCOUNT_ID); - with_attr error_message("ERC1155: transfer to non-ERC1155Receiver implementer") { - assert is_account = TRUE; - } - return (); -} - -func _do_safe_batch_transfer_acceptance_check{ - syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr -}( - operator: felt, - from_: felt, - to: felt, - ids_len: felt, - ids: Uint256*, - values_len: felt, - values: Uint256*, - data_len: felt, - data: felt*, -) { - // Confirm supports IERC1155receiver interface - let (is_supported) = IERC165.supportsInterface(to, IERC1155_RECEIVER_ID); - if (is_supported == TRUE) { - let (selector) = IERC1155Receiver.onERC1155BatchReceived( - contract_address=to, - operator=operator, - from_=from_, - ids_len=ids_len, - ids=ids, - values_len=values_len, - values=values, - data_len=data_len, - data=data, - ); - // Confirm onBatchERC1155Recieved selector returned - with_attr error_message("ERC1155: ERC1155Receiver rejected tokens") { - assert selector = ON_ERC1155_BATCH_RECEIVED_SELECTOR; - } - return (); - } - - // Alternatively confirm account - let (is_account) = IERC165.supportsInterface(to, IACCOUNT_ID); - with_attr error_message("ERC1155: transfer to non-ERC1155Receiver implementer") { - assert is_account = TRUE; - } - return (); -} - -func _balance_of_batch_iter{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - len: felt, accounts: felt*, ids: Uint256*, batch_balances: Uint256* -) { - if (len == 0) { - return (); - } - // Read current entries - let id: Uint256 = [ids]; - _check_id(id); - let account: felt = [accounts]; - - // Get balance - let (balance: Uint256) = ERC1155.balance_of(account, id); - assert [batch_balances] = balance; - return _balance_of_batch_iter( - len - 1, accounts + 1, ids + Uint256.SIZE, batch_balances + Uint256.SIZE - ); -} - -func _safe_batch_transfer_from_iter{ - syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr -}(from_: felt, to: felt, len: felt, ids: Uint256*, values: Uint256*) { - // Base case - alloc_locals; - if (len == 0) { - return (); - } - - // Read current entries, perform Uint256 checks - let id = [ids]; - let value = [values]; - _check_id(id); - _check_value(value); - - // deduct from sender - let (from_balance: Uint256) = ERC1155_balances.read(id, from_); - with_attr error_message("ERC1155: insufficient balance for transfer") { - let (new_balance: Uint256) = SafeUint256.sub_le(from_balance, value); - } - ERC1155_balances.write(id, from_, new_balance); - - _add_to_receiver(id, value, to); - - // Recursive call - return _safe_batch_transfer_from_iter( - from_, to, len - 1, ids + Uint256.SIZE, values + Uint256.SIZE - ); -} - -func _mint_batch_iter{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - to: felt, len: felt, ids: Uint256*, values: Uint256* -) { - // Base case - alloc_locals; - if (len == 0) { - return (); - } - - // Read current entries - let id: Uint256 = [ids]; - let value: Uint256 = [values]; - _check_id(id); - _check_value(value); - - _add_to_receiver(id, value, to); - - // Recursive call - return _mint_batch_iter(to, len - 1, ids + Uint256.SIZE, values + Uint256.SIZE); -} - -func _burn_batch_iter{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - from_: felt, len: felt, ids: Uint256*, values: Uint256* -) { - // Base case - alloc_locals; - if (len == 0) { - return (); - } - - // Read current entries - let id: Uint256 = [ids]; - let value: Uint256 = [values]; - _check_id(id); - _check_value(value); - - // Deduct from burner - let (from_balance: Uint256) = ERC1155_balances.read(id, from_); - with_attr error_message("ERC1155: burn value exceeds balance") { - let (new_balance: Uint256) = SafeUint256.sub_le(from_balance, value); - } - ERC1155_balances.write(id, from_, new_balance); - - // Recursive call - return _burn_batch_iter(from_, len - 1, ids + Uint256.SIZE, values + Uint256.SIZE); -} - -func _add_to_receiver{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - id: Uint256, value: Uint256, receiver: felt -) { - let (receiver_balance: Uint256) = ERC1155_balances.read(id, receiver); - with_attr error_message("ERC1155: balance overflow") { - let (new_balance: Uint256) = SafeUint256.add(receiver_balance, value); - } - ERC1155_balances.write(id, receiver, new_balance); - return (); -} - -func _check_id{range_check_ptr}(id: Uint256) { - with_attr error_message("ERC1155: token_id is not a valid Uint256") { - uint256_check(id); - } - return (); -} - -func _check_value{range_check_ptr}(value: Uint256) { - with_attr error_message("ERC1155: value is not a valid Uint256") { - uint256_check(value); - } - return (); -} diff --git a/src/openzeppelin/token/erc1155/presets/ERC1155MintableBurnable.cairo b/src/openzeppelin/token/erc1155/presets/ERC1155MintableBurnable.cairo deleted file mode 100644 index e280effae..000000000 --- a/src/openzeppelin/token/erc1155/presets/ERC1155MintableBurnable.cairo +++ /dev/null @@ -1,159 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc1155/presets/ERC1155MintableBurnable.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.uint256 import Uint256 - -from openzeppelin.access.ownable.library import Ownable -from openzeppelin.token.erc1155.library import ERC1155 -from openzeppelin.introspection.erc165.library import ERC165 - -// -// Constructor -// - -@constructor -func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - uri: felt, owner: felt -) { - ERC1155.initializer(uri); - Ownable.initializer(owner); - return (); -} - -// -// Getters -// - -@view -func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - interfaceId: felt -) -> (success: felt) { - return ERC165.supports_interface(interfaceId); -} - -@view -func uri{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(id: Uint256) -> ( - uri: felt -) { - return ERC1155.uri(id); -} - -@view -func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - account: felt, id: Uint256 -) -> (balance: Uint256) { - return ERC1155.balance_of(account, id); -} - -@view -func balanceOfBatch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - accounts_len: felt, accounts: felt*, ids_len: felt, ids: Uint256* -) -> (balances_len: felt, balances: Uint256*) { - return ERC1155.balance_of_batch(accounts_len, accounts, ids_len, ids); -} - -@view -func isApprovedForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - account: felt, operator: felt -) -> (approved: felt) { - return ERC1155.is_approved_for_all(account, operator); -} - -@view -func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) { - return Ownable.owner(); -} - -// -// Externals -// - -@external -func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - operator: felt, approved: felt -) { - ERC1155.set_approval_for_all(operator, approved); - return (); -} - -@external -func safeTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - from_: felt, to: felt, id: Uint256, value: Uint256, data_len: felt, data: felt* -) { - ERC1155.safe_transfer_from(from_, to, id, value, data_len, data); - return (); -} - -@external -func safeBatchTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - from_: felt, - to: felt, - ids_len: felt, - ids: Uint256*, - values_len: felt, - values: Uint256*, - data_len: felt, - data: felt*, -) { - ERC1155.safe_batch_transfer_from(from_, to, ids_len, ids, values_len, values, data_len, data); - return (); -} - -@external -func mint{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - to: felt, id: Uint256, value: Uint256, data_len: felt, data: felt* -) { - Ownable.assert_only_owner(); - ERC1155._mint(to, id, value, data_len, data); - return (); -} - -@external -func mintBatch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - to: felt, - ids_len: felt, - ids: Uint256*, - values_len: felt, - values: Uint256*, - data_len: felt, - data: felt*, -) { - Ownable.assert_only_owner(); - ERC1155._mint_batch(to, ids_len, ids, values_len, values, data_len, data); - return (); -} - -@external -func burn{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - from_: felt, id: Uint256, value: Uint256 -) { - ERC1155.assert_owner_or_approved(owner=from_); - ERC1155._burn(from_, id, value); - return (); -} - -@external -func burnBatch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - from_: felt, ids_len: felt, ids: Uint256*, values_len: felt, values: Uint256* -) { - ERC1155.assert_owner_or_approved(owner=from_); - ERC1155._burn_batch(from_, ids_len, ids, values_len, values); - return (); -} - -@external -func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - newOwner: felt -) { - Ownable.transfer_ownership(newOwner); - return (); -} - -@external -func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - Ownable.renounce_ownership(); - return (); -} diff --git a/src/openzeppelin/token/erc1155/presets/utils/ERC1155Holder.cairo b/src/openzeppelin/token/erc1155/presets/utils/ERC1155Holder.cairo deleted file mode 100644 index a369cc754..000000000 --- a/src/openzeppelin/token/erc1155/presets/utils/ERC1155Holder.cairo +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc1155/presets/utils/ERC1155Holder.cairo) - -%lang starknet - -from starkware.cairo.common.uint256 import Uint256 -from starkware.cairo.common.bool import TRUE, FALSE - -from openzeppelin.utils.constants.library import ( - IERC1155_RECEIVER_ID, - ON_ERC1155_RECEIVED_SELECTOR, - ON_ERC1155_BATCH_RECEIVED_SELECTOR, -) - -@view -func onERC1155Received( - operator: felt, from_: felt, id: Uint256, value: Uint256, data_len: felt, data: felt* -) -> (selector: felt) { - if (data_len == 0) { - return (ON_ERC1155_RECEIVED_SELECTOR,); - } else { - return (FALSE,); - } -} - -@view -func onERC1155BatchReceived( - operator: felt, - from_: felt, - ids_len: felt, - ids: Uint256*, - values_len: felt, - values: Uint256*, - data_len: felt, - data: felt*, -) -> (selector: felt) { - if (data_len == 0) { - return (ON_ERC1155_BATCH_RECEIVED_SELECTOR,); - } else { - return (FALSE,); - } -} - -@view -func supportsInterface(interfaceId: felt) -> (success: felt) { - if (interfaceId == IERC1155_RECEIVER_ID) { - return (TRUE,); - } else { - return (FALSE,); - } -} diff --git a/src/openzeppelin/token/erc20/cairo_project.toml b/src/openzeppelin/token/erc20/cairo_project.toml deleted file mode 100644 index e3dce5a73..000000000 --- a/src/openzeppelin/token/erc20/cairo_project.toml +++ /dev/null @@ -1,2 +0,0 @@ -[crate_roots] -erc20 = "." diff --git a/src/openzeppelin/token/erc20/erc20.cairo b/src/openzeppelin/token/erc20/erc20.cairo deleted file mode 100644 index 82aed427f..000000000 --- a/src/openzeppelin/token/erc20/erc20.cairo +++ /dev/null @@ -1,188 +0,0 @@ -#[contract] -mod ERC20 { - use erc20::IERC20; - use starknet::get_caller_address; - use starknet::contract_address_const; - use starknet::ContractAddressZeroable; - use zeroable::Zeroable; - - struct Storage { - _name: felt, - _symbol: felt, - _total_supply: u256, - _balances: LegacyMap::, - _allowances: LegacyMap::<(ContractAddress, ContractAddress), u256>, - } - - #[event] - fn Transfer(from: ContractAddress, to: ContractAddress, value: u256) {} - - #[event] - fn Approval(owner: ContractAddress, spender: ContractAddress, value: u256) {} - - impl ERC20 of IERC20 { - fn name() -> felt { - _name::read() - } - - fn symbol() -> felt { - _symbol::read() - } - - fn decimals() -> u8 { - 18_u8 - } - - fn total_supply() -> u256 { - _total_supply::read() - } - - fn balance_of(account: ContractAddress) -> u256 { - _balances::read(account) - } - - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { - _allowances::read((owner, spender)) - } - - fn transfer(recipient: ContractAddress, amount: u256) -> bool { - let sender = get_caller_address(); - _transfer(sender, recipient, amount); - true - } - - fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { - let caller = get_caller_address(); - _spend_allowance(sender, caller, amount); - _transfer(sender, recipient, amount); - true - } - - fn approve(spender: ContractAddress, amount: u256) -> bool { - let caller = get_caller_address(); - _approve(caller, spender, amount); - true - } - } - - #[constructor] - fn constructor(name: felt, symbol: felt, initial_supply: u256, recipient: ContractAddress) { - initializer(name, symbol, initial_supply, recipient); - } - - #[view] - fn name() -> felt { - ERC20::name() - } - - #[view] - fn symbol() -> felt { - ERC20::symbol() - } - - #[view] - fn decimals() -> u8 { - ERC20::decimals() - } - - #[view] - fn total_supply() -> u256 { - ERC20::total_supply() - } - - #[view] - fn balance_of(account: ContractAddress) -> u256 { - ERC20::balance_of(account) - } - - #[view] - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { - ERC20::allowance(owner, spender) - } - - #[external] - fn transfer(recipient: ContractAddress, amount: u256) -> bool { - ERC20::transfer(recipient, amount) - } - - #[external] - fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { - ERC20::transfer_from(sender, recipient, amount) - } - - #[external] - fn approve(spender: ContractAddress, amount: u256) -> bool { - ERC20::approve(spender, amount) - } - - #[external] - fn increase_allowance(spender: ContractAddress, added_value: u256) -> bool { - _increase_allowance(spender, added_value) - } - - #[external] - fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { - _decrease_allowance(spender, subtracted_value) - } - - /// - /// Internals - /// - - fn initializer(name_: felt, symbol_: felt, initial_supply: u256, recipient: ContractAddress) { - _name::write(name_); - _symbol::write(symbol_); - _mint(recipient, initial_supply); - } - - fn _increase_allowance(spender: ContractAddress, added_value: u256) -> bool { - let caller = get_caller_address(); - _approve(caller, spender, _allowances::read((caller, spender)) + added_value); - true - } - - fn _decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { - let caller = get_caller_address(); - _approve(caller, spender, _allowances::read((caller, spender)) - subtracted_value); - true - } - - fn _mint(recipient: ContractAddress, amount: u256) { - assert(!recipient.is_zero(), 'ERC20: mint to 0'); - _total_supply::write(_total_supply::read() + amount); - _balances::write(recipient, _balances::read(recipient) + amount); - Transfer(contract_address_const::<0>(), recipient, amount); - } - - fn _burn(account: ContractAddress, amount: u256) { - assert(!account.is_zero(), 'ERC20: burn from 0'); - _total_supply::write(_total_supply::read() - amount); - _balances::write(account, _balances::read(account) - amount); - Transfer(account, contract_address_const::<0>(), amount); - } - - fn _approve(owner: ContractAddress, spender: ContractAddress, amount: u256) { - assert(!owner.is_zero(), 'ERC20: approve from 0'); - assert(!spender.is_zero(), 'ERC20: approve to 0'); - _allowances::write((owner, spender), amount); - Approval(owner, spender, amount); - } - - fn _transfer(sender: ContractAddress, recipient: ContractAddress, amount: u256) { - assert(!sender.is_zero(), 'ERC20: transfer from 0'); - assert(!recipient.is_zero(), 'ERC20: transfer to 0'); - _balances::write(sender, _balances::read(sender) - amount); - _balances::write(recipient, _balances::read(recipient) + amount); - Transfer(sender, recipient, amount); - } - - fn _spend_allowance(owner: ContractAddress, spender: ContractAddress, amount: u256) { - let current_allowance = _allowances::read((owner, spender)); - let ONES_MASK = 0xffffffffffffffffffffffffffffffff_u128; - let is_unlimited_allowance = - current_allowance.low == ONES_MASK & current_allowance.high == ONES_MASK; - if !is_unlimited_allowance { - _approve(owner, spender, current_allowance - amount); - } - } -} diff --git a/src/openzeppelin/token/erc20/lib.cairo b/src/openzeppelin/token/erc20/lib.cairo deleted file mode 100644 index 29d85d5fe..000000000 --- a/src/openzeppelin/token/erc20/lib.cairo +++ /dev/null @@ -1,16 +0,0 @@ -mod erc20; -use erc20::ERC20; - -mod tests; - -trait IERC20 { - fn name() -> felt; - fn symbol() -> felt; - fn decimals() -> u8; - fn total_supply() -> u256; - fn balance_of(account: ContractAddress) -> u256; - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256; - fn transfer(recipient: ContractAddress, amount: u256) -> bool; - fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool; - fn approve(spender: ContractAddress, amount: u256) -> bool; -} diff --git a/src/openzeppelin/token/erc20/tests.cairo b/src/openzeppelin/token/erc20/tests.cairo deleted file mode 100644 index 0117a1281..000000000 --- a/src/openzeppelin/token/erc20/tests.cairo +++ /dev/null @@ -1 +0,0 @@ -mod test_erc20; diff --git a/src/openzeppelin/token/erc20/tests/test_erc20.cairo b/src/openzeppelin/token/erc20/tests/test_erc20.cairo deleted file mode 100644 index a41cf5bd3..000000000 --- a/src/openzeppelin/token/erc20/tests/test_erc20.cairo +++ /dev/null @@ -1,435 +0,0 @@ -use erc20::ERC20; -use starknet::contract_address_const; -use starknet_testing::set_caller_address; -use integer::u256; -use integer::u256_from_felt; - -const NAME: felt = 111; -const SYMBOL: felt = 222; - -fn setup() -> (ContractAddress, u256) { - let initial_supply: u256 = u256_from_felt(2000); - let account: ContractAddress = contract_address_const::<1>(); - // Set account as default caller - set_caller_address(account); - - ERC20::initializer(NAME, SYMBOL, initial_supply, account); - (account, initial_supply) -} - -fn set_caller_as_zero() { - set_caller_address(contract_address_const::<0>()); -} - -#[test] -#[available_gas(2000000)] -fn initialize() { - let initial_supply: u256 = u256_from_felt(2000); - let account: ContractAddress = contract_address_const::<1>(); - let decimals: u8 = 18_u8; - - ERC20::initializer(NAME, SYMBOL, initial_supply, account); - - let owner_balance: u256 = ERC20::balance_of(account); - assert(owner_balance == initial_supply, 'Should eq inital_supply'); - - assert(ERC20::total_supply() == initial_supply, 'Should eq inital_supply'); - assert(ERC20::name() == NAME, 'Name should be NAME'); - assert(ERC20::symbol() == SYMBOL, 'Symbol should be SYMBOL'); - assert(ERC20::decimals() == decimals, 'Decimals should be 18'); -} - -#[test] -#[available_gas(2000000)] -fn test_approve() { - let (owner, supply) = setup(); - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - - let success: bool = ERC20::approve(spender, amount); - assert(success, 'Should return true'); - assert(ERC20::allowance(owner, spender) == amount, 'Spender not approved correctly'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_approve_from_zero() { - let (owner, supply) = setup(); - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - - set_caller_as_zero(); - - ERC20::approve(spender, amount); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_approve_to_zero() { - let (owner, supply) = setup(); - let spender: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt(100); - - ERC20::approve(spender, amount); -} - -#[test] -#[available_gas(2000000)] -fn test__approve() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - - ERC20::_approve(owner, spender, amount); - assert(ERC20::allowance(owner, spender) == amount, 'Spender not approved correctly'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test__approve_from_zero() { - let owner: ContractAddress = contract_address_const::<0>(); - let spender: ContractAddress = contract_address_const::<1>(); - let amount: u256 = u256_from_felt(100); - ERC20::_approve(owner, spender, amount); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test__approve_to_zero() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt(100); - ERC20::_approve(owner, spender, amount); -} - -#[test] -#[available_gas(2000000)] -fn test_transfer() { - let (sender, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - let success: bool = ERC20::transfer(recipient, amount); - - assert(success, 'Should return true'); - assert(ERC20::balance_of(recipient) == amount, 'Balance should eq amount'); - assert(ERC20::balance_of(sender) == supply - amount, 'Should eq supply - amount'); - assert(ERC20::total_supply() == supply, 'Total supply should not change'); -} - -#[test] -#[available_gas(2000000)] -fn test__transfer() { - let (sender, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - ERC20::_transfer(sender, recipient, amount); - - assert(ERC20::balance_of(recipient) == amount, 'Balance should eq amount'); - assert(ERC20::balance_of(sender) == supply - amount, 'Should eq supply - amount'); - assert(ERC20::total_supply() == supply, 'Total supply should not change'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test__transfer_not_enough_balance() { - let (sender, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<2>(); - let amount: u256 = supply + u256_from_felt(1); - ERC20::_transfer(sender, recipient, amount); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test__transfer_from_zero() { - let sender: ContractAddress = contract_address_const::<0>(); - let recipient: ContractAddress = contract_address_const::<1>(); - let amount: u256 = u256_from_felt(100); - ERC20::_transfer(sender, recipient, amount); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test__transfer_to_zero() { - let (sender, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt(100); - ERC20::_transfer(sender, recipient, amount); -} - -#[test] -#[available_gas(2000000)] -fn test_transfer_from() { - let (owner, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<2>(); - let spender: ContractAddress = contract_address_const::<3>(); - let amount: u256 = u256_from_felt(100); - - ERC20::approve(spender, amount); - - set_caller_address(spender); - - let success: bool = ERC20::transfer_from(owner, recipient, amount); - assert(success, 'Should return true'); - - // Will dangle without setting as a var - let spender_allowance: u256 = ERC20::allowance(owner, spender); - - assert(ERC20::balance_of(recipient) == amount, 'Should eq amount'); - assert(ERC20::balance_of(owner) == supply - amount, 'Should eq suppy - amount'); - assert(spender_allowance == u256_from_felt(0), 'Should eq 0'); - assert(ERC20::total_supply() == supply, 'Total supply should not change'); -} - -#[test] -#[available_gas(2000000)] -fn test_transfer_from_doesnt_consume_infinite_allowance() { - let (owner, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<2>(); - let spender: ContractAddress = contract_address_const::<3>(); - let amount: u256 = u256_from_felt(100); - let max_u256: u256 = u256 { - low: 0xffffffffffffffffffffffffffffffff_u128, - high: 0xffffffffffffffffffffffffffffffff_u128 - }; - - ERC20::approve(spender, max_u256); - - set_caller_address(spender); - - ERC20::transfer_from(owner, recipient, amount); - - let spender_allowance: u256 = ERC20::allowance(owner, spender); - assert(spender_allowance == max_u256, 'Allowance should not change'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_transfer_from_greater_than_allowance() { - let (owner, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<2>(); - let spender: ContractAddress = contract_address_const::<3>(); - let amount: u256 = u256_from_felt(100); - let amount_plus_one: u256 = amount + u256_from_felt(1); - - ERC20::approve(spender, amount); - - set_caller_address(spender); - - ERC20::transfer_from(owner, recipient, amount_plus_one); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_transfer_from_to_zero_address() { - let (owner, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<0>(); - let spender: ContractAddress = contract_address_const::<3>(); - let amount: u256 = u256_from_felt(100); - - ERC20::approve(spender, amount); - - set_caller_address(spender); - - ERC20::transfer_from(owner, recipient, amount); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_transfer_from_from_zero_address() { - let (owner, supply) = setup(); - - let zero_address: ContractAddress = contract_address_const::<0>(); - let recipient: ContractAddress = contract_address_const::<2>(); - let spender: ContractAddress = contract_address_const::<3>(); - let amount: u256 = u256_from_felt(100); - - set_caller_address(zero_address); - - ERC20::transfer_from(owner, recipient, amount); -} - -#[test] -#[available_gas(2000000)] -fn test_increase_allowance() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - - ERC20::approve(spender, amount); - let success: bool = ERC20::increase_allowance(spender, amount); - assert(success, 'Should return true'); - - - let spender_allowance: u256 = ERC20::allowance(owner, spender); - assert(spender_allowance == amount + amount, 'Should be amount * 2'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_increase_allowance_to_zero_address() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt(100); - - ERC20::increase_allowance(spender, amount); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_increase_allowance_from_zero_address() { - let (owner, supply) = setup(); - - let zero_address: ContractAddress = contract_address_const::<0>(); - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - - set_caller_address(zero_address); - - ERC20::increase_allowance(spender, amount); -} - -#[test] -#[available_gas(2000000)] -fn test_decrease_allowance() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - - ERC20::approve(spender, amount); - let success: bool = ERC20::decrease_allowance(spender, amount); - assert(success, 'Should return true'); - - let spender_allowance: u256 = ERC20::allowance(owner, spender); - assert(spender_allowance == amount - amount, 'Should be 0'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_decrease_allowance_to_zero_address() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt(100); - - ERC20::decrease_allowance(spender, amount); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_decrease_allowance_from_zero_address() { - let (owner, supply) = setup(); - - let zero_address: ContractAddress = contract_address_const::<0>(); - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - - set_caller_address(zero_address); - - ERC20::decrease_allowance(spender, amount); -} - -#[test] -#[available_gas(2000000)] -fn test__spend_allowance_not_unlimited() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - - ERC20::_approve(owner, spender, supply); - ERC20::_spend_allowance(owner, spender, amount); - assert(ERC20::allowance(owner, spender) == supply - amount, 'Should eq supply - amount'); -} - -#[test] -#[available_gas(2000000)] -fn test__spend_allowance_unlimited() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<2>(); - - let max_u256: u256 = u256 { - low: 0xffffffffffffffffffffffffffffffff_u128, - high: 0xffffffffffffffffffffffffffffffff_u128 - }; - let max_minus_one: u256 = max_u256 - u256_from_felt(1); - - ERC20::_approve(owner, spender, max_u256); - ERC20::_spend_allowance(owner, spender, max_minus_one); - - assert(ERC20::allowance(owner, spender) == max_u256, 'Allowance should not change'); -} - -#[test] -#[available_gas(2000000)] -fn test__mint() { - let minter: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt(100); - - ERC20::_mint(minter, amount); - - let minter_balance: u256 = ERC20::balance_of(minter); - assert(minter_balance == amount, 'Should eq amount'); - - assert(ERC20::total_supply() == amount, 'Should eq total supply'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test__mint_to_zero() { - let minter: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt(100); - - ERC20::_mint(minter, amount); -} - -#[test] -#[available_gas(2000000)] -fn test__burn() { - let (owner, supply) = setup(); - - let amount: u256 = u256_from_felt(100); - ERC20::_burn(owner, amount); - - assert(ERC20::total_supply() == supply - amount, 'Should eq supply - amount'); - assert(ERC20::balance_of(owner) == supply - amount, 'Should eq supply - amount'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test__burn_from_zero() { - setup(); - let zero_address: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt(100); - - ERC20::_burn(zero_address, amount); -} diff --git a/src/openzeppelin/token/erc721/IERC721.cairo b/src/openzeppelin/token/erc721/IERC721.cairo deleted file mode 100644 index 48a4c62e6..000000000 --- a/src/openzeppelin/token/erc721/IERC721.cairo +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc721/IERC721.cairo) - -%lang starknet - -from starkware.cairo.common.uint256 import Uint256 - -@contract_interface -namespace IERC721 { - func balanceOf(owner: felt) -> (balance: Uint256) { - } - - func ownerOf(tokenId: Uint256) -> (owner: felt) { - } - - func safeTransferFrom(from_: felt, to: felt, tokenId: Uint256, data_len: felt, data: felt*) { - } - - func transferFrom(from_: felt, to: felt, tokenId: Uint256) { - } - - func approve(approved: felt, tokenId: Uint256) { - } - - func setApprovalForAll(operator: felt, approved: felt) { - } - - func getApproved(tokenId: Uint256) -> (approved: felt) { - } - - func isApprovedForAll(owner: felt, operator: felt) -> (approved: felt) { - } - - // ERC165 - - func supportsInterface(interfaceId: felt) -> (success: felt) { - } -} diff --git a/src/openzeppelin/token/erc721/IERC721Metadata.cairo b/src/openzeppelin/token/erc721/IERC721Metadata.cairo deleted file mode 100644 index 65cdac38e..000000000 --- a/src/openzeppelin/token/erc721/IERC721Metadata.cairo +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc721/IERC721Metadata.cairo) - -%lang starknet - -from starkware.cairo.common.uint256 import Uint256 - -@contract_interface -namespace IERC721Metadata { - func name() -> (name: felt) { - } - - func symbol() -> (symbol: felt) { - } - - func tokenURI(tokenId: Uint256) -> (tokenURI: felt) { - } - - /// IERC721 - - func balanceOf(owner: felt) -> (balance: Uint256) { - } - - func ownerOf(tokenId: Uint256) -> (owner: felt) { - } - - func safeTransferFrom(from_: felt, to: felt, tokenId: Uint256, data_len: felt, data: felt*) { - } - - func transferFrom(from_: felt, to: felt, tokenId: Uint256) { - } - - func approve(approved: felt, tokenId: Uint256) { - } - - func setApprovalForAll(operator: felt, approved: felt) { - } - - func getApproved(tokenId: Uint256) -> (approved: felt) { - } - - func isApprovedForAll(owner: felt, operator: felt) -> (approved: felt) { - } - - // ERC165 - - func supportsInterface(interfaceId: felt) -> (success: felt) { - } -} diff --git a/src/openzeppelin/token/erc721/IERC721Receiver.cairo b/src/openzeppelin/token/erc721/IERC721Receiver.cairo deleted file mode 100644 index 30cfb36fd..000000000 --- a/src/openzeppelin/token/erc721/IERC721Receiver.cairo +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc721/IERC721Receiver.cairo) - -%lang starknet - -from starkware.cairo.common.uint256 import Uint256 - -@contract_interface -namespace IERC721Receiver { - func onERC721Received( - operator: felt, - from_: felt, - tokenId: Uint256, - data_len: felt, - data: felt* - ) -> (selector: felt) { - } -} diff --git a/src/openzeppelin/token/erc721/enumerable/IERC721Enumerable.cairo b/src/openzeppelin/token/erc721/enumerable/IERC721Enumerable.cairo deleted file mode 100644 index 06e82584f..000000000 --- a/src/openzeppelin/token/erc721/enumerable/IERC721Enumerable.cairo +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc721/enumerable/IERC721Enumerable.cairo) - -%lang starknet - -from starkware.cairo.common.uint256 import Uint256 - -@contract_interface -namespace IERC721Enumerable { - func totalSupply() -> (totalSupply: Uint256) { - } - - func tokenByIndex(index: Uint256) -> (tokenId: Uint256) { - } - - func tokenOfOwnerByIndex(owner: felt, index: Uint256) -> (tokenId: Uint256) { - } - - /// IERC721 - - func balanceOf(owner: felt) -> (balance: Uint256) { - } - - func ownerOf(tokenId: Uint256) -> (owner: felt) { - } - - func safeTransferFrom(from_: felt, to: felt, tokenId: Uint256, data_len: felt, data: felt*) { - } - - func transferFrom(from_: felt, to: felt, tokenId: Uint256) { - } - - func approve(approved: felt, tokenId: Uint256) { - } - - func setApprovalForAll(operator: felt, approved: felt) { - } - - func getApproved(tokenId: Uint256) -> (approved: felt) { - } - - func isApprovedForAll(owner: felt, operator: felt) -> (approved: felt) { - } - - // ERC165 - - func supportsInterface(interfaceId: felt) -> (success: felt) { - } -} diff --git a/src/openzeppelin/token/erc721/enumerable/library.cairo b/src/openzeppelin/token/erc721/enumerable/library.cairo deleted file mode 100644 index 2ffb05023..000000000 --- a/src/openzeppelin/token/erc721/enumerable/library.cairo +++ /dev/null @@ -1,201 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc721/enumerable/library.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.bool import TRUE, FALSE -from starkware.cairo.common.uint256 import Uint256, uint256_lt, uint256_eq, uint256_check - -from openzeppelin.introspection.erc165.library import ERC165 -from openzeppelin.security.safemath.library import SafeUint256 -from openzeppelin.token.erc721.library import ERC721 -from openzeppelin.utils.constants.library import IERC721_ENUMERABLE_ID - -// -// Storage -// - -@storage_var -func ERC721Enumerable_all_tokens_len() -> (total_supply: Uint256) { -} - -@storage_var -func ERC721Enumerable_all_tokens(index: Uint256) -> (token_id: Uint256) { -} - -@storage_var -func ERC721Enumerable_all_tokens_index(token_id: Uint256) -> (index: Uint256) { -} - -@storage_var -func ERC721Enumerable_owned_tokens(owner: felt, index: Uint256) -> (token_id: Uint256) { -} - -@storage_var -func ERC721Enumerable_owned_tokens_index(token_id: Uint256) -> (index: Uint256) { -} - -namespace ERC721Enumerable { - // - // Constructor - // - - func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - ERC165.register_interface(IERC721_ENUMERABLE_ID); - return (); - } - - // - // Getters - // - - func total_supply{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - total_supply: Uint256 - ) { - return ERC721Enumerable_all_tokens_len.read(); - } - - func token_by_index{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - index: Uint256 - ) -> (token_id: Uint256) { - alloc_locals; - uint256_check(index); - // Ensures index argument is less than total_supply - let (len: Uint256) = ERC721Enumerable.total_supply(); - let (is_lt) = uint256_lt(index, len); - with_attr error_message("ERC721Enumerable: global index out of bounds") { - assert is_lt = TRUE; - } - - return ERC721Enumerable_all_tokens.read(index); - } - - func token_of_owner_by_index{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - owner: felt, index: Uint256 - ) -> (token_id: Uint256) { - alloc_locals; - uint256_check(index); - // Ensures index argument is less than owner's balance - let (len: Uint256) = ERC721.balance_of(owner); - let (is_lt) = uint256_lt(index, len); - with_attr error_message("ERC721Enumerable: owner index out of bounds") { - assert is_lt = TRUE; - } - - return ERC721Enumerable_owned_tokens.read(owner, index); - } - - // - // Externals - // - func transfer_from{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - from_: felt, to: felt, token_id: Uint256 - ) { - _remove_token_from_owner_enumeration(from_, token_id); - _add_token_to_owner_enumeration(to, token_id); - ERC721.transfer_from(from_, to, token_id); - return (); - } - - func safe_transfer_from{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - from_: felt, to: felt, token_id: Uint256, data_len: felt, data: felt* - ) { - _remove_token_from_owner_enumeration(from_, token_id); - _add_token_to_owner_enumeration(to, token_id); - ERC721.safe_transfer_from(from_, to, token_id, data_len, data); - return (); - } - - // - // Internals - // - - func _mint{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - to: felt, token_id: Uint256 - ) { - _add_token_to_all_tokens_enumeration(token_id); - _add_token_to_owner_enumeration(to, token_id); - ERC721._mint(to, token_id); - return (); - } - - func _burn{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}(token_id: Uint256) { - let (from_) = ERC721.owner_of(token_id); - _remove_token_from_owner_enumeration(from_, token_id); - _remove_token_from_all_tokens_enumeration(token_id); - ERC721._burn(token_id); - return (); - } -} - -// -// Private -// - -func _add_token_to_all_tokens_enumeration{ - pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr -}(token_id: Uint256) { - let (supply: Uint256) = ERC721Enumerable_all_tokens_len.read(); - ERC721Enumerable_all_tokens.write(supply, token_id); - ERC721Enumerable_all_tokens_index.write(token_id, supply); - - let (new_supply: Uint256) = SafeUint256.add(supply, Uint256(1, 0)); - ERC721Enumerable_all_tokens_len.write(new_supply); - return (); -} - -func _remove_token_from_all_tokens_enumeration{ - pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr -}(token_id: Uint256) { - alloc_locals; - let (supply: Uint256) = ERC721Enumerable_all_tokens_len.read(); - let (last_token_index: Uint256) = SafeUint256.sub_le(supply, Uint256(1, 0)); - let (token_index: Uint256) = ERC721Enumerable_all_tokens_index.read(token_id); - let (last_token_id: Uint256) = ERC721Enumerable_all_tokens.read(last_token_index); - - ERC721Enumerable_all_tokens.write(last_token_index, Uint256(0, 0)); - ERC721Enumerable_all_tokens_index.write(token_id, Uint256(0, 0)); - ERC721Enumerable_all_tokens_len.write(last_token_index); - - let (is_equal) = uint256_eq(last_token_index, token_index); - if (is_equal == FALSE) { - ERC721Enumerable_all_tokens_index.write(last_token_id, token_index); - ERC721Enumerable_all_tokens.write(token_index, last_token_id); - return (); - } - return (); -} - -func _add_token_to_owner_enumeration{ - pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr -}(to: felt, token_id: Uint256) { - let (length: Uint256) = ERC721.balance_of(to); - ERC721Enumerable_owned_tokens.write(to, length, token_id); - ERC721Enumerable_owned_tokens_index.write(token_id, length); - return (); -} - -func _remove_token_from_owner_enumeration{ - pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr -}(from_: felt, token_id: Uint256) { - alloc_locals; - let (last_token_index: Uint256) = ERC721.balance_of(from_); - // the index starts at zero therefore the user's last token index is their balance minus one - let (last_token_index) = SafeUint256.sub_le(last_token_index, Uint256(1, 0)); - let (token_index: Uint256) = ERC721Enumerable_owned_tokens_index.read(token_id); - - // If index is last, we can just set the return values to zero - let (is_equal) = uint256_eq(token_index, last_token_index); - if (is_equal == TRUE) { - ERC721Enumerable_owned_tokens_index.write(token_id, Uint256(0, 0)); - ERC721Enumerable_owned_tokens.write(from_, last_token_index, Uint256(0, 0)); - return (); - } - - // If index is not last, reposition owner's last token to the removed token's index - let (last_token_id: Uint256) = ERC721Enumerable_owned_tokens.read(from_, last_token_index); - ERC721Enumerable_owned_tokens.write(from_, token_index, last_token_id); - ERC721Enumerable_owned_tokens_index.write(last_token_id, token_index); - return (); -} diff --git a/src/openzeppelin/token/erc721/enumerable/presets/ERC721EnumerableMintableBurnable.cairo b/src/openzeppelin/token/erc721/enumerable/presets/ERC721EnumerableMintableBurnable.cairo deleted file mode 100644 index 44ac448ea..000000000 --- a/src/openzeppelin/token/erc721/enumerable/presets/ERC721EnumerableMintableBurnable.cairo +++ /dev/null @@ -1,187 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc721/enumerable/presets/ERC721EnumerableMintableBurnable.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.uint256 import Uint256 - -from openzeppelin.access.ownable.library import Ownable -from openzeppelin.introspection.erc165.library import ERC165 -from openzeppelin.token.erc721.library import ERC721 -from openzeppelin.token.erc721.enumerable.library import ERC721Enumerable - -// -// Constructor -// - -@constructor -func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - name: felt, symbol: felt, owner: felt -) { - ERC721.initializer(name, symbol); - ERC721Enumerable.initializer(); - Ownable.initializer(owner); - return (); -} - -// -// Getters -// - -@view -func totalSupply{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}() -> ( - totalSupply: Uint256 -) { - let (totalSupply: Uint256) = ERC721Enumerable.total_supply(); - return (totalSupply=totalSupply); -} - -@view -func tokenByIndex{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - index: Uint256 -) -> (tokenId: Uint256) { - let (tokenId: Uint256) = ERC721Enumerable.token_by_index(index); - return (tokenId=tokenId); -} - -@view -func tokenOfOwnerByIndex{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - owner: felt, index: Uint256 -) -> (tokenId: Uint256) { - let (tokenId: Uint256) = ERC721Enumerable.token_of_owner_by_index(owner, index); - return (tokenId=tokenId); -} - -@view -func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - interfaceId: felt -) -> (success: felt) { - return ERC165.supports_interface(interfaceId); -} - -@view -func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) { - return ERC721.name(); -} - -@view -func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) { - return ERC721.symbol(); -} - -@view -func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(owner: felt) -> ( - balance: Uint256 -) { - return ERC721.balance_of(owner); -} - -@view -func ownerOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(tokenId: Uint256) -> ( - owner: felt -) { - return ERC721.owner_of(tokenId); -} - -@view -func getApproved{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - tokenId: Uint256 -) -> (approved: felt) { - return ERC721.get_approved(tokenId); -} - -@view -func isApprovedForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - owner: felt, operator: felt -) -> (approved: felt) { - return ERC721.is_approved_for_all(owner, operator); -} - -@view -func tokenURI{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - tokenId: Uint256 -) -> (tokenURI: felt) { - let (tokenURI: felt) = ERC721.token_uri(tokenId); - return (tokenURI=tokenURI); -} - -@view -func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) { - return Ownable.owner(); -} - -// -// Externals -// - -@external -func approve{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - to: felt, tokenId: Uint256 -) { - ERC721.approve(to, tokenId); - return (); -} - -@external -func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - operator: felt, approved: felt -) { - ERC721.set_approval_for_all(operator, approved); - return (); -} - -@external -func transferFrom{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - from_: felt, to: felt, tokenId: Uint256 -) { - ERC721Enumerable.transfer_from(from_, to, tokenId); - return (); -} - -@external -func safeTransferFrom{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - from_: felt, to: felt, tokenId: Uint256, data_len: felt, data: felt* -) { - ERC721Enumerable.safe_transfer_from(from_, to, tokenId, data_len, data); - return (); -} - -@external -func mint{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - to: felt, tokenId: Uint256 -) { - Ownable.assert_only_owner(); - ERC721Enumerable._mint(to, tokenId); - return (); -} - -@external -func burn{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}(tokenId: Uint256) { - ERC721.assert_only_token_owner(tokenId); - ERC721Enumerable._burn(tokenId); - return (); -} - -@external -func setTokenURI{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - tokenId: Uint256, tokenURI: felt -) { - Ownable.assert_only_owner(); - ERC721._set_token_uri(tokenId, tokenURI); - return (); -} - -@external -func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - newOwner: felt -) { - Ownable.transfer_ownership(newOwner); - return (); -} - -@external -func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - Ownable.renounce_ownership(); - return (); -} diff --git a/src/openzeppelin/token/erc721/library.cairo b/src/openzeppelin/token/erc721/library.cairo deleted file mode 100644 index 3a4b58f95..000000000 --- a/src/openzeppelin/token/erc721/library.cairo +++ /dev/null @@ -1,471 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc721/library.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.starknet.common.syscalls import get_caller_address -from starkware.cairo.common.math import assert_not_zero, assert_not_equal -from starkware.cairo.common.bool import TRUE, FALSE -from starkware.cairo.common.uint256 import Uint256, uint256_check - -from openzeppelin.introspection.erc165.library import ERC165 -from openzeppelin.introspection.erc165.IERC165 import IERC165 -from openzeppelin.security.safemath.library import SafeUint256 -from openzeppelin.token.erc721.IERC721Receiver import IERC721Receiver -from openzeppelin.utils.constants.library import ( - IERC721_ID, - IERC721_METADATA_ID, - IERC721_RECEIVER_ID, - IACCOUNT_ID, -) - -// -// Events -// - -@event -func Transfer(from_: felt, to: felt, tokenId: Uint256) { -} - -@event -func Approval(owner: felt, approved: felt, tokenId: Uint256) { -} - -@event -func ApprovalForAll(owner: felt, operator: felt, approved: felt) { -} - -// -// Storage -// - -@storage_var -func ERC721_name() -> (name: felt) { -} - -@storage_var -func ERC721_symbol() -> (symbol: felt) { -} - -@storage_var -func ERC721_owners(token_id: Uint256) -> (owner: felt) { -} - -@storage_var -func ERC721_balances(account: felt) -> (balance: Uint256) { -} - -@storage_var -func ERC721_token_approvals(token_id: Uint256) -> (approved: felt) { -} - -@storage_var -func ERC721_operator_approvals(owner: felt, operator: felt) -> (approved: felt) { -} - -@storage_var -func ERC721_token_uri(token_id: Uint256) -> (token_uri: felt) { -} - -namespace ERC721 { - // - // Constructor - // - - func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - name: felt, symbol: felt - ) { - ERC721_name.write(name); - ERC721_symbol.write(symbol); - ERC165.register_interface(IERC721_ID); - ERC165.register_interface(IERC721_METADATA_ID); - return (); - } - - // - // Getters - // - - func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) { - return ERC721_name.read(); - } - - func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - symbol: felt - ) { - return ERC721_symbol.read(); - } - - func balance_of{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - owner: felt - ) -> (balance: Uint256) { - with_attr error_message("ERC721: balance query for the zero address") { - assert_not_zero(owner); - } - return ERC721_balances.read(owner); - } - - func owner_of{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - token_id: Uint256 - ) -> (owner: felt) { - with_attr error_message("ERC721: token_id is not a valid Uint256") { - uint256_check(token_id); - } - let (owner) = ERC721_owners.read(token_id); - with_attr error_message("ERC721: owner query for nonexistent token") { - assert_not_zero(owner); - } - return (owner=owner); - } - - func get_approved{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - token_id: Uint256 - ) -> (approved: felt) { - with_attr error_message("ERC721: token_id is not a valid Uint256") { - uint256_check(token_id); - } - let exists = _exists(token_id); - with_attr error_message("ERC721: approved query for nonexistent token") { - assert exists = TRUE; - } - - return ERC721_token_approvals.read(token_id); - } - - func is_approved_for_all{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - owner: felt, operator: felt - ) -> (approved: felt) { - return ERC721_operator_approvals.read(owner, operator); - } - - func token_uri{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - token_id: Uint256 - ) -> (token_uri: felt) { - let exists = _exists(token_id); - with_attr error_message("ERC721_Metadata: URI query for nonexistent token") { - assert exists = TRUE; - } - - // if tokenURI is not set, it will return 0 - return ERC721_token_uri.read(token_id); - } - - // - // Externals - // - - func approve{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - to: felt, token_id: Uint256 - ) { - with_attr error_mesage("ERC721: token_id is not a valid Uint256") { - uint256_check(token_id); - } - - // Checks caller is not zero address - let (caller) = get_caller_address(); - with_attr error_message("ERC721: cannot approve from the zero address") { - assert_not_zero(caller); - } - - // Ensures 'owner' does not equal 'to' - let (owner) = ERC721_owners.read(token_id); - with_attr error_message("ERC721: approval to current owner") { - assert_not_equal(owner, to); - } - - // Checks that either caller equals owner or - // caller isApprovedForAll on behalf of owner - if (caller == owner) { - _approve(to, token_id); - return (); - } else { - let (is_approved) = ERC721_operator_approvals.read(owner, caller); - with_attr error_message("ERC721: approve caller is not owner nor approved for all") { - assert_not_zero(is_approved); - } - _approve(to, token_id); - return (); - } - } - - func set_approval_for_all{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - operator: felt, approved: felt - ) { - // Ensures caller is neither zero address nor operator - let (caller) = get_caller_address(); - with_attr error_message("ERC721: either the caller or operator is the zero address") { - assert_not_zero(caller * operator); - } - // note this pattern as we'll frequently use it: - // instead of making an `assert_not_zero` call for each address - // we can always briefly write `assert_not_zero(a0 * a1 * ... * aN)`. - // This is because these addresses are field elements, - // meaning that a*0==0 for all a in the field, - // and a*b==0 implies that at least one of a,b are zero in the field - with_attr error_message("ERC721: approve to caller") { - assert_not_equal(caller, operator); - } - - // Make sure `approved` is a boolean (0 or 1) - with_attr error_message("ERC721: approved is not a Cairo boolean") { - assert approved * (1 - approved) = 0; - } - - ERC721_operator_approvals.write(owner=caller, operator=operator, value=approved); - ApprovalForAll.emit(caller, operator, approved); - return (); - } - - func transfer_from{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - from_: felt, to: felt, token_id: Uint256 - ) { - alloc_locals; - with_attr error_message("ERC721: token_id is not a valid Uint256") { - uint256_check(token_id); - } - let (caller) = get_caller_address(); - let is_approved = _is_approved_or_owner(caller, token_id); - with_attr error_message( - "ERC721: either is not approved or the caller is the zero address") { - assert_not_zero(caller * is_approved); - } - // Note that if either `is_approved` or `caller` equals `0`, - // then this method should fail. - // The `caller` address and `is_approved` boolean are both field elements - // meaning that a*0==0 for all a in the field, - // therefore a*b==0 implies that at least one of a,b is zero in the field - - _transfer(from_, to, token_id); - return (); - } - - func safe_transfer_from{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - from_: felt, to: felt, token_id: Uint256, data_len: felt, data: felt* - ) { - alloc_locals; - with_attr error_message("ERC721: token_id is not a valid Uint256") { - uint256_check(token_id); - } - let (caller) = get_caller_address(); - let is_approved = _is_approved_or_owner(caller, token_id); - with_attr error_message( - "ERC721: either is not approved or the caller is the zero address") { - assert_not_zero(caller * is_approved); - } - // Note that if either `is_approved` or `caller` equals `0`, - // then this method should fail. - // The `caller` address and `is_approved` boolean are both field elements - // meaning that a*0==0 for all a in the field, - // therefore a*b==0 implies that at least one of a,b is zero in the field - - _safe_transfer(from_, to, token_id, data_len, data); - return (); - } - - // - // Internals - // - - func assert_only_token_owner{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - token_id: Uint256 - ) { - uint256_check(token_id); - let (caller) = get_caller_address(); - let (owner) = owner_of(token_id); - // Note `owner_of` checks that the owner is not the zero address - with_attr error_message("ERC721: caller is not the token owner") { - assert caller = owner; - } - return (); - } - - func _is_approved_or_owner{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - spender: felt, token_id: Uint256 - ) -> felt { - alloc_locals; - - let exists = _exists(token_id); - with_attr error_message("ERC721: token id does not exist") { - assert exists = TRUE; - } - - let (owner) = owner_of(token_id); - if (owner == spender) { - return TRUE; - } - - let (approved_addr) = get_approved(token_id); - if (approved_addr == spender) { - return TRUE; - } - - let (is_operator) = is_approved_for_all(owner, spender); - if (is_operator == TRUE) { - return TRUE; - } - - return FALSE; - } - - func _exists{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - token_id: Uint256 - ) -> felt { - let (exists) = ERC721_owners.read(token_id); - if (exists == FALSE) { - return FALSE; - } - - return TRUE; - } - - func _approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - to: felt, token_id: Uint256 - ) { - ERC721_token_approvals.write(token_id, to); - let (owner) = owner_of(token_id); - Approval.emit(owner, to, token_id); - return (); - } - - func _transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - from_: felt, to: felt, token_id: Uint256 - ) { - // ownerOf ensures 'from_' is not the zero address - let (owner) = owner_of(token_id); - with_attr error_message("ERC721: transfer from incorrect owner") { - assert owner = from_; - } - - with_attr error_message("ERC721: cannot transfer to the zero address") { - assert_not_zero(to); - } - - // Clear approvals - _approve(0, token_id); - - // Decrease owner balance - let (owner_bal) = ERC721_balances.read(from_); - let (new_balance: Uint256) = SafeUint256.sub_le(owner_bal, Uint256(1, 0)); - ERC721_balances.write(from_, new_balance); - - // Increase receiver balance - let (receiver_bal) = ERC721_balances.read(to); - let (new_balance: Uint256) = SafeUint256.add(receiver_bal, Uint256(1, 0)); - ERC721_balances.write(to, new_balance); - - // Update token_id owner - ERC721_owners.write(token_id, to); - Transfer.emit(from_, to, token_id); - return (); - } - - func _safe_transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - from_: felt, to: felt, token_id: Uint256, data_len: felt, data: felt* - ) { - _transfer(from_, to, token_id); - - let (success) = _check_onERC721Received(from_, to, token_id, data_len, data); - with_attr error_message("ERC721: transfer to non ERC721Receiver implementer") { - assert_not_zero(success); - } - return (); - } - - func _mint{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - to: felt, token_id: Uint256 - ) { - with_attr error_message("ERC721: token_id is not a valid Uint256") { - uint256_check(token_id); - } - with_attr error_message("ERC721: cannot mint to the zero address") { - assert_not_zero(to); - } - - // Ensures token_id is unique - let exists = _exists(token_id); - with_attr error_message("ERC721: token already minted") { - assert exists = FALSE; - } - - let (balance: Uint256) = ERC721_balances.read(to); - let (new_balance: Uint256) = SafeUint256.add(balance, Uint256(1, 0)); - ERC721_balances.write(to, new_balance); - ERC721_owners.write(token_id, to); - Transfer.emit(0, to, token_id); - return (); - } - - func _safe_mint{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - to: felt, token_id: Uint256, data_len: felt, data: felt* - ) { - with_attr error_message("ERC721: token_id is not a valid Uint256") { - uint256_check(token_id); - } - _mint(to, token_id); - - let (success) = _check_onERC721Received(0, to, token_id, data_len, data); - with_attr error_message("ERC721: transfer to non ERC721Receiver implementer") { - assert_not_zero(success); - } - return (); - } - - func _burn{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}(token_id: Uint256) { - alloc_locals; - with_attr error_message("ERC721: token_id is not a valid Uint256") { - uint256_check(token_id); - } - let (owner) = owner_of(token_id); - - // Clear approvals - _approve(0, token_id); - - // Decrease owner balance - let (balance: Uint256) = ERC721_balances.read(owner); - let (new_balance: Uint256) = SafeUint256.sub_le(balance, Uint256(1, 0)); - ERC721_balances.write(owner, new_balance); - - // Delete owner - ERC721_owners.write(token_id, 0); - Transfer.emit(owner, 0, token_id); - return (); - } - - func _set_token_uri{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - token_id: Uint256, token_uri: felt - ) { - uint256_check(token_id); - let exists = _exists(token_id); - with_attr error_message("ERC721_Metadata: set token URI for nonexistent token") { - assert exists = TRUE; - } - - ERC721_token_uri.write(token_id, token_uri); - return (); - } -} - -// -// Private -// - -func _check_onERC721Received{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - from_: felt, to: felt, token_id: Uint256, data_len: felt, data: felt* -) -> (success: felt) { - let (caller) = get_caller_address(); - let (is_supported) = IERC165.supportsInterface(to, IERC721_RECEIVER_ID); - if (is_supported == TRUE) { - let (selector) = IERC721Receiver.onERC721Received( - to, caller, from_, token_id, data_len, data - ); - - with_attr error_message("ERC721: transfer to non ERC721Receiver implementer") { - assert selector = IERC721_RECEIVER_ID; - } - return (success=TRUE); - } - - let (is_account) = IERC165.supportsInterface(to, IACCOUNT_ID); - return (success=is_account); -} diff --git a/src/openzeppelin/token/erc721/presets/ERC721MintableBurnable.cairo b/src/openzeppelin/token/erc721/presets/ERC721MintableBurnable.cairo deleted file mode 100644 index b66910951..000000000 --- a/src/openzeppelin/token/erc721/presets/ERC721MintableBurnable.cairo +++ /dev/null @@ -1,161 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc721/presets/ERC721MintableBurnable.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.uint256 import Uint256 - -from openzeppelin.access.ownable.library import Ownable -from openzeppelin.introspection.erc165.library import ERC165 -from openzeppelin.token.erc721.library import ERC721 - -// -// Constructor -// - -@constructor -func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - name: felt, symbol: felt, owner: felt -) { - ERC721.initializer(name, symbol); - Ownable.initializer(owner); - return (); -} - -// -// Getters -// - -@view -func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - interfaceId: felt -) -> (success: felt) { - return ERC165.supports_interface(interfaceId); -} - -@view -func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) { - return ERC721.name(); -} - -@view -func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) { - return ERC721.symbol(); -} - -@view -func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(owner: felt) -> ( - balance: Uint256 -) { - return ERC721.balance_of(owner); -} - -@view -func ownerOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(tokenId: Uint256) -> ( - owner: felt -) { - return ERC721.owner_of(tokenId); -} - -@view -func getApproved{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - tokenId: Uint256 -) -> (approved: felt) { - return ERC721.get_approved(tokenId); -} - -@view -func isApprovedForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - owner: felt, operator: felt -) -> (approved: felt) { - return ERC721.is_approved_for_all(owner, operator); -} - -@view -func tokenURI{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - tokenId: Uint256 -) -> (tokenURI: felt) { - let (tokenURI: felt) = ERC721.token_uri(tokenId); - return (tokenURI=tokenURI); -} - -@view -func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) { - return Ownable.owner(); -} - -// -// Externals -// - -@external -func approve{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - to: felt, tokenId: Uint256 -) { - ERC721.approve(to, tokenId); - return (); -} - -@external -func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - operator: felt, approved: felt -) { - ERC721.set_approval_for_all(operator, approved); - return (); -} - -@external -func transferFrom{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - from_: felt, to: felt, tokenId: Uint256 -) { - ERC721.transfer_from(from_, to, tokenId); - return (); -} - -@external -func safeTransferFrom{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - from_: felt, to: felt, tokenId: Uint256, data_len: felt, data: felt* -) { - ERC721.safe_transfer_from(from_, to, tokenId, data_len, data); - return (); -} - -@external -func mint{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - to: felt, tokenId: Uint256 -) { - Ownable.assert_only_owner(); - ERC721._mint(to, tokenId); - return (); -} - -@external -func burn{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}(tokenId: Uint256) { - ERC721.assert_only_token_owner(tokenId); - ERC721._burn(tokenId); - return (); -} - -@external -func setTokenURI{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - tokenId: Uint256, tokenURI: felt -) { - Ownable.assert_only_owner(); - ERC721._set_token_uri(tokenId, tokenURI); - return (); -} - -@external -func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - newOwner: felt -) { - Ownable.transfer_ownership(newOwner); - return (); -} - -@external -func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - Ownable.renounce_ownership(); - return (); -} diff --git a/src/openzeppelin/token/erc721/presets/ERC721MintablePausable.cairo b/src/openzeppelin/token/erc721/presets/ERC721MintablePausable.cairo deleted file mode 100644 index 6d9ca1b60..000000000 --- a/src/openzeppelin/token/erc721/presets/ERC721MintablePausable.cairo +++ /dev/null @@ -1,179 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc721/presets/ERC721MintablePausable.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.uint256 import Uint256 - -from openzeppelin.access.ownable.library import Ownable -from openzeppelin.introspection.erc165.library import ERC165 -from openzeppelin.security.pausable.library import Pausable -from openzeppelin.token.erc721.library import ERC721 - -// -// Constructor -// - -@constructor -func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - name: felt, symbol: felt, owner: felt -) { - ERC721.initializer(name, symbol); - Ownable.initializer(owner); - return (); -} - -// -// Getters -// - -@view -func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - interfaceId: felt -) -> (success: felt) { - return ERC165.supports_interface(interfaceId); -} - -@view -func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) { - return ERC721.name(); -} - -@view -func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) { - return ERC721.symbol(); -} - -@view -func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(owner: felt) -> ( - balance: Uint256 -) { - return ERC721.balance_of(owner); -} - -@view -func ownerOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(tokenId: Uint256) -> ( - owner: felt -) { - return ERC721.owner_of(tokenId); -} - -@view -func getApproved{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - tokenId: Uint256 -) -> (approved: felt) { - return ERC721.get_approved(tokenId); -} - -@view -func isApprovedForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - owner: felt, operator: felt -) -> (approved: felt) { - return ERC721.is_approved_for_all(owner, operator); -} - -@view -func tokenURI{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - tokenId: Uint256 -) -> (tokenURI: felt) { - let (tokenURI: felt) = ERC721.token_uri(tokenId); - return (tokenURI=tokenURI); -} - -@view -func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) { - return Ownable.owner(); -} - -@view -func paused{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (paused: felt) { - return Pausable.is_paused(); -} - -// -// Externals -// - -@external -func approve{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - to: felt, tokenId: Uint256 -) { - Pausable.assert_not_paused(); - ERC721.approve(to, tokenId); - return (); -} - -@external -func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - operator: felt, approved: felt -) { - Pausable.assert_not_paused(); - ERC721.set_approval_for_all(operator, approved); - return (); -} - -@external -func transferFrom{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - from_: felt, to: felt, tokenId: Uint256 -) { - Pausable.assert_not_paused(); - ERC721.transfer_from(from_, to, tokenId); - return (); -} - -@external -func safeTransferFrom{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - from_: felt, to: felt, tokenId: Uint256, data_len: felt, data: felt* -) { - Pausable.assert_not_paused(); - ERC721.safe_transfer_from(from_, to, tokenId, data_len, data); - return (); -} - -@external -func mint{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - to: felt, tokenId: Uint256 -) { - Pausable.assert_not_paused(); - Ownable.assert_only_owner(); - ERC721._mint(to, tokenId); - return (); -} - -@external -func setTokenURI{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - tokenId: Uint256, tokenURI: felt -) { - Ownable.assert_only_owner(); - ERC721._set_token_uri(tokenId, tokenURI); - return (); -} - -@external -func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - newOwner: felt -) { - Ownable.transfer_ownership(newOwner); - return (); -} - -@external -func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - Ownable.renounce_ownership(); - return (); -} - -@external -func pause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - Ownable.assert_only_owner(); - Pausable._pause(); - return (); -} - -@external -func unpause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - Ownable.assert_only_owner(); - Pausable._unpause(); - return (); -} diff --git a/src/openzeppelin/token/erc721/presets/utils/ERC721Holder.cairo b/src/openzeppelin/token/erc721/presets/utils/ERC721Holder.cairo deleted file mode 100644 index f29530a97..000000000 --- a/src/openzeppelin/token/erc721/presets/utils/ERC721Holder.cairo +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (token/erc721/presets/utils/ERC721Holder.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.uint256 import Uint256 - -from openzeppelin.utils.constants.library import IERC721_RECEIVER_ID - -from openzeppelin.introspection.erc165.library import ERC165 - -@view -func onERC721Received( - operator: felt, from_: felt, tokenId: Uint256, data_len: felt, data: felt* -) -> (selector: felt) { - return (selector=IERC721_RECEIVER_ID); -} - -@view -func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - interfaceId: felt -) -> (success: felt) { - return ERC165.supports_interface(interfaceId); -} - -@constructor -func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - ERC165.register_interface(IERC721_RECEIVER_ID); - return (); -} diff --git a/src/openzeppelin/upgrades/library.cairo b/src/openzeppelin/upgrades/library.cairo deleted file mode 100644 index e94d9e9d7..000000000 --- a/src/openzeppelin/upgrades/library.cairo +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (upgrades/library.cairo) - -%lang starknet - -from starkware.starknet.common.syscalls import get_caller_address -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.bool import TRUE, FALSE -from starkware.cairo.common.math import assert_not_zero - -// -// Events -// - -@event -func Upgraded(implementation: felt) { -} - -@event -func AdminChanged(previousAdmin: felt, newAdmin: felt) { -} - -// -// Storage variables -// - -@storage_var -func Proxy_implementation_hash() -> (implementation: felt) { -} - -@storage_var -func Proxy_admin() -> (admin: felt) { -} - -@storage_var -func Proxy_initialized() -> (initialized: felt) { -} - -namespace Proxy { - // - // Initializer - // - - func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - proxy_admin: felt - ) { - let (initialized) = Proxy_initialized.read(); - with_attr error_message("Proxy: contract already initialized") { - assert initialized = FALSE; - } - - Proxy_initialized.write(TRUE); - _set_admin(proxy_admin); - return (); - } - - // - // Guards - // - - func assert_only_admin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - let (caller) = get_caller_address(); - let (admin) = Proxy_admin.read(); - with_attr error_message("Proxy: caller is not admin") { - assert admin = caller; - } - return (); - } - - // - // Getters - // - - func get_implementation_hash{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - ) -> (implementation: felt) { - return Proxy_implementation_hash.read(); - } - - func get_admin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - admin: felt - ) { - return Proxy_admin.read(); - } - - // - // Unprotected - // - - func _set_admin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - new_admin: felt - ) { - let (previous_admin) = get_admin(); - Proxy_admin.write(new_admin); - AdminChanged.emit(previous_admin, new_admin); - return (); - } - - // - // Upgrade - // - - func _set_implementation_hash{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - new_implementation: felt - ) { - with_attr error_message("Proxy: implementation hash cannot be zero") { - assert_not_zero(new_implementation); - } - - Proxy_implementation_hash.write(new_implementation); - Upgraded.emit(new_implementation); - return (); - } -} diff --git a/src/openzeppelin/upgrades/presets/Proxy.cairo b/src/openzeppelin/upgrades/presets/Proxy.cairo deleted file mode 100644 index b0360acf4..000000000 --- a/src/openzeppelin/upgrades/presets/Proxy.cairo +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (upgrades/presets/Proxy.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.starknet.common.syscalls import library_call, library_call_l1_handler - -from openzeppelin.upgrades.library import Proxy - -// @dev Cairo doesn't support native decoding like Solidity yet, -// that's why we pass three arguments for calldata instead of one -// @param implementation_hash the implementation contract hash -// @param selector the implementation initializer function selector -// @param calldata_len the calldata length for the initializer -// @param calldata an array of felt containing the raw calldata -@constructor -func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - implementation_hash: felt, selector: felt, - calldata_len: felt, calldata: felt* -) { - alloc_locals; - Proxy._set_implementation_hash(implementation_hash); - - if (selector != 0) { - // Initialize proxy from implementation - library_call( - class_hash=implementation_hash, - function_selector=selector, - calldata_size=calldata_len, - calldata=calldata, - ); - } - - return (); -} - -// -// Fallback functions -// - -@external -@raw_input -@raw_output -func __default__{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - selector: felt, calldata_size: felt, calldata: felt* -) -> (retdata_size: felt, retdata: felt*) { - let (class_hash) = Proxy.get_implementation_hash(); - - let (retdata_size: felt, retdata: felt*) = library_call( - class_hash=class_hash, - function_selector=selector, - calldata_size=calldata_size, - calldata=calldata, - ); - return (retdata_size, retdata); -} - -@l1_handler -@raw_input -func __l1_default__{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - selector: felt, calldata_size: felt, calldata: felt* -) { - let (class_hash) = Proxy.get_implementation_hash(); - - library_call_l1_handler( - class_hash=class_hash, - function_selector=selector, - calldata_size=calldata_size, - calldata=calldata, - ); - return (); -} diff --git a/src/openzeppelin/utils/constants/library.cairo b/src/openzeppelin/utils/constants/library.cairo deleted file mode 100644 index 29c10b6d8..000000000 --- a/src/openzeppelin/utils/constants/library.cairo +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (utils/constants/library.cairo) - -%lang starknet - -// -// Numbers -// - -const UINT8_MAX = 255; - -// -// Interface Ids -// - -// ERC165 -const IERC165_ID = 0x01ffc9a7; -const INVALID_ID = 0xffffffff; - -// Account -const IACCOUNT_ID = 0xa66bd575; - -// ERC721 -const IERC721_ID = 0x80ac58cd; -const IERC721_RECEIVER_ID = 0x150b7a02; -const IERC721_METADATA_ID = 0x5b5e139f; -const IERC721_ENUMERABLE_ID = 0x780e9d63; - -// ERC1155 -const IERC1155_ID = 0xd9b67a26; -const IERC1155_METADATA_ID = 0x0e89341c; -const IERC1155_RECEIVER_ID = 0x4e2312e0; -const ON_ERC1155_RECEIVED_SELECTOR = 0xf23a6e61; -const ON_ERC1155_BATCH_RECEIVED_SELECTOR = 0xbc197c81; - -// AccessControl -const IACCESSCONTROL_ID = 0x7965db0b; - -// -// Roles -// - -const DEFAULT_ADMIN_ROLE = 0; - -// -// Starknet -// - -const TRANSACTION_VERSION = 1; diff --git a/src/openzeppelin/utils/presets/UniversalDeployer.cairo b/src/openzeppelin/utils/presets/UniversalDeployer.cairo deleted file mode 100644 index a3eb80574..000000000 --- a/src/openzeppelin/utils/presets/UniversalDeployer.cairo +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (utils/presets/UniversalDeployer.cairo) - -%lang starknet - -from starkware.starknet.common.syscalls import get_caller_address, deploy -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.hash import hash2 -from starkware.cairo.common.bool import FALSE, TRUE - -@event -func ContractDeployed( - address: felt, - deployer: felt, - unique: felt, - classHash: felt, - calldata_len: felt, - calldata: felt*, - salt: felt -) { -} - -@external -func deployContract{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - range_check_ptr -}( - classHash: felt, - salt: felt, - unique: felt, - calldata_len: felt, - calldata: felt* -) -> (address: felt) { - alloc_locals; - let (deployer) = get_caller_address(); - - local _salt; - local from_zero; - if (unique == TRUE) { - let (unique_salt) = hash2{hash_ptr=pedersen_ptr}(deployer, salt); - _salt = unique_salt; - from_zero = FALSE; - tempvar _pedersen = pedersen_ptr; - } else { - _salt = salt; - from_zero = TRUE; - tempvar _pedersen = pedersen_ptr; - } - - let pedersen_ptr = _pedersen; - - let (address) = deploy( - class_hash=classHash, - contract_address_salt=_salt, - constructor_calldata_size=calldata_len, - constructor_calldata=calldata, - deploy_from_zero=from_zero, - ); - - ContractDeployed.emit( - address=address, - deployer=deployer, - unique=unique, - classHash=classHash, - calldata_len=calldata_len, - calldata=calldata, - salt=salt - ); - - return (address=address); -} diff --git a/tests/access/OwnableBaseSuite.py b/tests/access/OwnableBaseSuite.py deleted file mode 100644 index 63b513dd4..000000000 --- a/tests/access/OwnableBaseSuite.py +++ /dev/null @@ -1,65 +0,0 @@ -import pytest -from signers import MockSigner -from nile.utils import ZERO_ADDRESS -from utils import assert_event_emitted - - -signer = MockSigner(123456789987654321) - - -class OwnableBase: - @pytest.mark.asyncio - async def test_constructor(self, contract_factory): - ownable, owner, *_ = contract_factory - expected = await ownable.owner().call() - assert expected.result.owner == owner.contract_address - - - @pytest.mark.asyncio - async def test_transferOwnership(self, contract_factory): - ownable, owner, *_ = contract_factory - new_owner = 123 - await signer.send_transaction(owner, ownable.contract_address, 'transferOwnership', [new_owner]) - executed_info = await ownable.owner().call() - assert executed_info.result == (new_owner,) - - - @pytest.mark.asyncio - async def test_transferOwnership_emits_event(self, contract_factory): - ownable, owner, *_ = contract_factory - new_owner = 123 - tx_exec_info = await signer.send_transaction(owner, ownable.contract_address, 'transferOwnership', [new_owner]) - - assert_event_emitted( - tx_exec_info, - from_address=ownable.contract_address, - name='OwnershipTransferred', - data=[ - owner.contract_address, - new_owner - ] - ) - - - @pytest.mark.asyncio - async def test_renounceOwnership(self, contract_factory): - ownable, owner, *_ = contract_factory - await signer.send_transaction(owner, ownable.contract_address, 'renounceOwnership', []) - executed_info = await ownable.owner().call() - assert executed_info.result == (ZERO_ADDRESS,) - - - @pytest.mark.asyncio - async def test_renounceOwnership_emits_event(self, contract_factory): - ownable, owner, *_ = contract_factory - tx_exec_info = await signer.send_transaction(owner, ownable.contract_address, 'renounceOwnership', []) - - assert_event_emitted( - tx_exec_info, - from_address=ownable.contract_address, - name='OwnershipTransferred', - data=[ - owner.contract_address, - ZERO_ADDRESS - ] - ) diff --git a/tests/access/test_AccessControl.py b/tests/access/test_AccessControl.py deleted file mode 100644 index e1f81fc08..000000000 --- a/tests/access/test_AccessControl.py +++ /dev/null @@ -1,231 +0,0 @@ -import pytest -from pathlib import Path -from signers import MockSigner -from nile.utils import TRUE, FALSE, assert_revert -from utils import ( - State, Account, assert_event_emitted, get_contract_class, cached_contract -) - -DEFAULT_ADMIN_ROLE = 0 -SOME_OTHER_ROLE = 42 -IACCESSCONTROL_ID = 0x7965db0b - -signer = MockSigner(123456789987654321) - - -@pytest.fixture(scope='module') -def contract_classes(): - return { - Path(key).stem: get_contract_class(key) - for key in [ - 'Account', - 'AccessControl', - ] - } - - -@pytest.fixture(scope='module') -async def accesscontrol_init(contract_classes): - starknet = await State.init() - account1 = await Account.deploy(signer.public_key) - account2 = await Account.deploy(signer.public_key) - accesscontrol = await starknet.deploy( - contract_class=contract_classes['AccessControl'], - constructor_calldata=[account1.contract_address] - ) - return starknet.state, accesscontrol, account1, account2 - - -@pytest.fixture -def accesscontrol_factory(contract_classes, accesscontrol_init): - state, accesscontrol, account1, account2 = accesscontrol_init - _state = state.copy() - accesscontrol = cached_contract( - _state, contract_classes['AccessControl'], accesscontrol) - account1 = cached_contract(_state, Account.get_class, account1) - account2 = cached_contract(_state, Account.get_class, account2) - return accesscontrol, account1, account2 - - -@pytest.mark.asyncio -async def test_initializer(accesscontrol_factory): - accesscontrol, _, _ = accesscontrol_factory - - execution_info = await accesscontrol.supportsInterface(IACCESSCONTROL_ID).execute() - assert execution_info.result == (TRUE,) - - -@ pytest.mark.asyncio -async def test_grant_role(accesscontrol_factory): - accesscontrol, account1, account2 = accesscontrol_factory - - tx_exec_info = await signer.send_transaction( - account1, - accesscontrol.contract_address, - 'grantRole', - [ - DEFAULT_ADMIN_ROLE, - account2.contract_address - ] - ) - - assert_event_emitted( - tx_exec_info, - from_address=accesscontrol.contract_address, - name='RoleGranted', - data=[ - DEFAULT_ADMIN_ROLE, # role - account2.contract_address, # account - account1.contract_address # sender - ] - ) - - expected = await accesscontrol.hasRole(DEFAULT_ADMIN_ROLE, account2.contract_address).execute() - assert expected.result.hasRole == TRUE - - -@ pytest.mark.asyncio -async def test_grant_role_unauthorized(accesscontrol_factory): - accesscontrol, _, account2 = accesscontrol_factory - - await assert_revert( - signer.send_transaction(account2, accesscontrol.contract_address, 'grantRole', [ - DEFAULT_ADMIN_ROLE, account2.contract_address]), - reverted_with="AccessControl: caller is missing role {}".format( - DEFAULT_ADMIN_ROLE) - ) - - -@ pytest.mark.asyncio -async def test_revoke_role(accesscontrol_factory): - accesscontrol, account1, account2 = accesscontrol_factory - - await signer.send_transaction(account1, accesscontrol.contract_address, 'grantRole', [DEFAULT_ADMIN_ROLE, account2.contract_address]) - - tx_exec_info = await signer.send_transaction(account2, accesscontrol.contract_address, 'revokeRole', [DEFAULT_ADMIN_ROLE, account1.contract_address]) - assert_event_emitted( - tx_exec_info, - from_address=accesscontrol.contract_address, - name='RoleRevoked', - data=[ - DEFAULT_ADMIN_ROLE, # role - account1.contract_address, # account - account2.contract_address # sender - ] - ) - expected = await accesscontrol.hasRole(DEFAULT_ADMIN_ROLE, account1.contract_address).execute() - assert expected.result.hasRole == FALSE - - -@ pytest.mark.asyncio -async def test_revoke_role_unauthorized(accesscontrol_factory): - accesscontrol, account1, account2 = accesscontrol_factory - - await assert_revert( - signer.send_transaction( - account2, - accesscontrol.contract_address, - 'revokeRole', - [ - DEFAULT_ADMIN_ROLE, - account1.contract_address - ] - ), - reverted_with="AccessControl: caller is missing role {}".format( - DEFAULT_ADMIN_ROLE - ) - ) - - -@ pytest.mark.asyncio -async def test_renounce_role(accesscontrol_factory): - accesscontrol, account1, _ = accesscontrol_factory - - tx_exec_info = await signer.send_transaction( - account1, - accesscontrol.contract_address, - 'renounceRole', - [ - DEFAULT_ADMIN_ROLE, - account1.contract_address - ] - ) - - assert_event_emitted( - tx_exec_info, - from_address=accesscontrol.contract_address, - name='RoleRevoked', - data=[ - DEFAULT_ADMIN_ROLE, # role - account1.contract_address, # account - account1.contract_address # sender - ] - ) - - expected = await accesscontrol.hasRole(DEFAULT_ADMIN_ROLE, account1.contract_address).execute() - assert expected.result.hasRole == FALSE - - -@ pytest.mark.asyncio -async def test_renounce_role_unauthorized(accesscontrol_factory): - accesscontrol, account1, account2 = accesscontrol_factory - - await assert_revert( - signer.send_transaction( - account1, - accesscontrol.contract_address, - 'renounceRole', - [ - DEFAULT_ADMIN_ROLE, - account2.contract_address - ] - ), - reverted_with="AccessControl: can only renounce roles for self" - ) - - -@ pytest.mark.asyncio -async def test_set_role_admin(accesscontrol_factory): - accesscontrol, account1, account2 = accesscontrol_factory - - tx_exec_info = await signer.send_transaction( - account1, - accesscontrol.contract_address, - 'setRoleAdmin', - [DEFAULT_ADMIN_ROLE, SOME_OTHER_ROLE] - ) - - assert_event_emitted( - tx_exec_info, - from_address=accesscontrol.contract_address, - name='RoleAdminChanged', - data=[ - DEFAULT_ADMIN_ROLE, # role - DEFAULT_ADMIN_ROLE, # previousAdminRole - SOME_OTHER_ROLE # newAdminRole - ] - ) - - expected = await accesscontrol.getRoleAdmin(DEFAULT_ADMIN_ROLE).execute() - assert expected.result.admin == SOME_OTHER_ROLE - - # test role admin cycle - await signer.send_transaction( - account1, - accesscontrol.contract_address, - 'grantRole', - [SOME_OTHER_ROLE, account2.contract_address] - ) - - expected = await accesscontrol.hasRole(SOME_OTHER_ROLE, account2.contract_address).execute() - assert expected.result.hasRole == TRUE - - await signer.send_transaction( - account2, - accesscontrol.contract_address, - 'revokeRole', - [DEFAULT_ADMIN_ROLE, account1.contract_address] - ) - - expected = await accesscontrol.hasRole(DEFAULT_ADMIN_ROLE, account1.contract_address).execute() - assert expected.result.hasRole == FALSE diff --git a/tests/access/test_Ownable.py b/tests/access/test_Ownable.py deleted file mode 100644 index d9541faef..000000000 --- a/tests/access/test_Ownable.py +++ /dev/null @@ -1,63 +0,0 @@ -import pytest -from signers import MockSigner -from nile.utils import assert_revert -from utils import State, Account, get_contract_class, cached_contract -from OwnableBaseSuite import OwnableBase - - -signer = MockSigner(123456789987654321) - -@pytest.fixture(scope='module') -def contract_classes(): - return ( - Account.get_class, - get_contract_class('Ownable') - ) - - -@pytest.fixture(scope='module') -async def ownable_init(contract_classes): - account_cls, ownable_cls = contract_classes - starknet = await State.init() - owner = await Account.deploy(signer.public_key) - ownable = await starknet.deploy( - contract_class=ownable_cls, - constructor_calldata=[owner.contract_address] - ) - not_owner = await Account.deploy(signer.public_key) - return starknet.state, ownable, owner, not_owner - - -@pytest.fixture -def contract_factory(contract_classes, ownable_init): - account_cls, ownable_cls = contract_classes - state, ownable, owner, not_owner = ownable_init - _state = state.copy() - owner = cached_contract(_state, account_cls, owner) - ownable = cached_contract(_state, ownable_cls, ownable) - not_owner = cached_contract(_state, account_cls, not_owner) - return ownable, owner, not_owner - - -class TestOwnable(OwnableBase): - @pytest.mark.asyncio - async def test_contract_without_owner(self, contract_factory): - ownable, owner, _ = contract_factory - await signer.send_transaction(owner, ownable.contract_address, 'renounceOwnership', []) - - # Protected function should not be called from zero address - await assert_revert( - ownable.protected_function().execute(), - reverted_with="Ownable: caller is the zero address" - ) - - - @pytest.mark.asyncio - async def test_contract_caller_not_owner(self, contract_factory): - ownable, owner, not_owner = contract_factory - - # Protected function should only be called from owner - await assert_revert( - signer.send_transaction(not_owner, ownable.contract_address, 'protected_function', []), - reverted_with="Ownable: caller is not the owner" - ) diff --git a/tests/account/test_Account.py b/tests/account/test_Account.py deleted file mode 100644 index 67cae651c..000000000 --- a/tests/account/test_Account.py +++ /dev/null @@ -1,243 +0,0 @@ -import pytest -from signers import MockSigner, get_raw_invoke -from nile.utils import TRUE, assert_revert -from utils import get_contract_class, cached_contract, State, Account, IACCOUNT_ID - - -signer = MockSigner(123456789987654321) -other = MockSigner(987654321123456789) - - -@pytest.fixture(scope='module') -def contract_classes(): - account_cls = Account.get_class - init_cls = get_contract_class("Initializable") - attacker_cls = get_contract_class("AccountReentrancy") - - return account_cls, init_cls, attacker_cls - - -@pytest.fixture(scope='module') -async def account_init(contract_classes): - _, init_cls, attacker_cls = contract_classes - starknet = await State.init() - account1 = await Account.deploy(signer.public_key) - account2 = await Account.deploy(signer.public_key) - - initializable1 = await starknet.deploy( - contract_class=init_cls, - constructor_calldata=[], - ) - initializable2 = await starknet.deploy( - contract_class=init_cls, - constructor_calldata=[], - ) - attacker = await starknet.deploy( - contract_class=attacker_cls, - constructor_calldata=[], - ) - - return starknet.state, account1, account2, initializable1, initializable2, attacker - - -@pytest.fixture -def account_factory(contract_classes, account_init): - account_cls, init_cls, attacker_cls = contract_classes - state, account1, account2, initializable1, initializable2, attacker = account_init - _state = state.copy() - account1 = cached_contract(_state, account_cls, account1) - account2 = cached_contract(_state, account_cls, account2) - initializable1 = cached_contract(_state, init_cls, initializable1) - initializable2 = cached_contract(_state, init_cls, initializable2) - attacker = cached_contract(_state, attacker_cls, attacker) - - return account1, account2, initializable1, initializable2, attacker - - -@pytest.mark.asyncio -async def test_counterfactual_deployment(account_factory): - account, *_ = account_factory - await signer.declare_class(account, "Account") - - execution_info = await signer.deploy_account(account.state, [signer.public_key]) - address = execution_info.validate_info.contract_address - - execution_info = await signer.send_transaction(account, address, 'getPublicKey', []) - assert execution_info.call_info.retdata[1] == signer.public_key - - -@pytest.mark.asyncio -async def test_constructor(account_factory): - account, *_ = account_factory - - execution_info = await account.getPublicKey().call() - assert execution_info.result == (signer.public_key,) - - execution_info = await account.supportsInterface(IACCOUNT_ID).call() - assert execution_info.result == (TRUE,) - - -@pytest.mark.asyncio -async def test_is_valid_signature(account_factory): - account, *_ = account_factory - hash = 0x23564 - signature = signer.sign(hash) - - execution_info = await account.isValidSignature(hash, signature).call() - assert execution_info.result == (TRUE,) - - # should revert if signature is not correct - await assert_revert( - account.isValidSignature(hash + 1, signature).call(), - reverted_with=( - f"Signature {tuple(signature)}, is invalid, with respect to the public key {signer.public_key}, " - f"and the message hash {hash + 1}." - ) - ) - - -@pytest.mark.asyncio -async def test_declare(account_factory): - account, *_ = account_factory - - # regular declare works - await signer.declare_class(account, "ERC20") - - # wrong signer fails - await assert_revert( - other.declare_class(account, "ERC20"), - reverted_with="is invalid, with respect to the public key" - ) - - -@pytest.mark.asyncio -async def test_execute(account_factory): - account, _, initializable, *_ = account_factory - - execution_info = await initializable.initialized().call() - assert execution_info.result == (0,) - - await signer.send_transaction(account, initializable.contract_address, 'initialize', []) - - execution_info = await initializable.initialized().call() - assert execution_info.result == (1,) - - # wrong signer fails - await assert_revert( - other.send_transaction(account, initializable.contract_address, 'initialize', []), - reverted_with="is invalid, with respect to the public key" - ) - - -@pytest.mark.asyncio -async def test_multicall(account_factory): - account, _, initializable_1, initializable_2, _ = account_factory - - execution_info = await initializable_1.initialized().call() - assert execution_info.result == (0,) - execution_info = await initializable_2.initialized().call() - assert execution_info.result == (0,) - - await signer.send_transactions( - account, - [ - (initializable_1.contract_address, 'initialize', []), - (initializable_2.contract_address, 'initialize', []) - ] - ) - - execution_info = await initializable_1.initialized().call() - assert execution_info.result == (1,) - execution_info = await initializable_2.initialized().call() - assert execution_info.result == (1,) - - -@pytest.mark.asyncio -async def test_return_value(account_factory): - account, _, initializable, *_ = account_factory - - # initialize, set `initialized = 1` - await signer.send_transaction(account, initializable.contract_address, 'initialize', []) - - read_info = await signer.send_transaction(account, initializable.contract_address, 'initialized', []) - call_info = await initializable.initialized().call() - (call_result, ) = call_info.result - assert read_info.call_info.retdata[1] == call_result # 1 - - -@ pytest.mark.asyncio -async def test_nonce(account_factory): - account, _, initializable, *_ = account_factory - - # bump nonce - await signer.send_transaction(account, initializable.contract_address, 'initialized', []) - - # get nonce - args = [(initializable.contract_address, 'initialized', [])] - raw_invocation = get_raw_invoke(account, args) - current_nonce = await raw_invocation.state.state.get_nonce_at(account.contract_address) - - # lower nonce - await assert_revert( - signer.send_transaction(account, initializable.contract_address, 'initialize', [], nonce=current_nonce - 1), - reverted_with="Invalid transaction nonce. Expected: {}, got: {}.".format( - current_nonce, current_nonce - 1 - ) - ) - - # higher nonce - await assert_revert( - signer.send_transaction(account, initializable.contract_address, 'initialize', [], nonce=current_nonce + 1), - reverted_with="Invalid transaction nonce. Expected: {}, got: {}.".format( - current_nonce, current_nonce + 1 - ) - ) - - # right nonce - await signer.send_transaction(account, initializable.contract_address, 'initialize', [], nonce=current_nonce) - - execution_info = await initializable.initialized().call() - assert execution_info.result == (1,) - - -@pytest.mark.asyncio -async def test_public_key_setter(account_factory): - account, *_ = account_factory - - execution_info = await account.getPublicKey().call() - assert execution_info.result == (signer.public_key,) - - # set new pubkey - await signer.send_transaction(account, account.contract_address, 'setPublicKey', [other.public_key]) - - execution_info = await account.getPublicKey().call() - assert execution_info.result == (other.public_key,) - - -@pytest.mark.asyncio -async def test_public_key_setter_different_account(account_factory): - account, bad_account, *_ = account_factory - - # set new pubkey - await assert_revert( - signer.send_transaction(bad_account, account.contract_address, 'setPublicKey', [other.public_key]), - reverted_with="Account: caller is not this account" - ) - - -@pytest.mark.asyncio -async def test_account_takeover_with_reentrant_call(account_factory): - account, _, _, _, attacker = account_factory - - await assert_revert( - signer.send_transaction( - account, attacker.contract_address, 'account_takeover', []), - reverted_with="Account: reentrant call" - ) - - execution_info = await account.getPublicKey().call() - assert execution_info.result == (signer.public_key,) - - -async def test_interface(): - assert get_contract_class("IAccount") diff --git a/tests/account/test_AddressRegistry.py b/tests/account/test_AddressRegistry.py deleted file mode 100644 index 0e202f86d..000000000 --- a/tests/account/test_AddressRegistry.py +++ /dev/null @@ -1,58 +0,0 @@ -import pytest -from signers import MockSigner -from utils import get_contract_class, cached_contract, State, Account - - -signer = MockSigner(123456789987654321) -L1_ADDRESS = 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984 -ANOTHER_ADDRESS = 0xd9e1ce17f2641f24ae83637ab66a2cca9c378b9f - -@pytest.fixture(scope='module') -async def registry_factory(): - # contract classes - registry_cls = get_contract_class("AddressRegistry") - account_cls = Account.get_class - - # deployments - starknet = await State.init() - account = await Account.deploy(signer.public_key) - registry = await starknet.deploy( - contract_class=registry_cls, - constructor_calldata=[] - ) - - # cache contracts - state = starknet.state.copy() - account = cached_contract(state, account_cls, account) - registry = cached_contract(state, registry_cls, registry) - - return account, registry - - -@pytest.mark.asyncio -async def test_set_address(registry_factory): - account, registry = registry_factory - - await signer.send_transaction( - account, registry.contract_address, 'set_L1_address', [L1_ADDRESS] - ) - execution_info = await registry.get_L1_address(account.contract_address).call() - assert execution_info.result == (L1_ADDRESS,) - - -@pytest.mark.asyncio -async def test_update_address(registry_factory): - account, registry = registry_factory - - await signer.send_transaction( - account, registry.contract_address, 'set_L1_address', [L1_ADDRESS] - ) - execution_info = await registry.get_L1_address(account.contract_address).call() - assert execution_info.result == (L1_ADDRESS,) - - # set new address - await signer.send_transaction( - account, registry.contract_address, 'set_L1_address', [ANOTHER_ADDRESS] - ) - execution_info = await registry.get_L1_address(account.contract_address).call() - assert execution_info.result == (ANOTHER_ADDRESS,) diff --git a/tests/account/test_EthAccount.py b/tests/account/test_EthAccount.py deleted file mode 100644 index 9c1aada21..000000000 --- a/tests/account/test_EthAccount.py +++ /dev/null @@ -1,240 +0,0 @@ -import pytest -from nile.utils import assert_revert, TRUE, FALSE -from utils import get_contract_class, cached_contract, State, IACCOUNT_ID -from signers import MockEthSigner, get_raw_invoke - -private_key = b'\x01' * 32 -signer = MockEthSigner(b'\x01' * 32) -other = MockEthSigner(b'\x02' * 32) - - -@pytest.fixture(scope='module') -def contract_defs(): - account_cls = get_contract_class('EthAccount') - init_cls = get_contract_class("Initializable") - attacker_cls = get_contract_class("AccountReentrancy") - - return account_cls, init_cls, attacker_cls - - -@pytest.fixture(scope='module') -async def account_init(contract_defs): - account_cls, init_cls, attacker_cls = contract_defs - starknet = await State.init() - - account1 = await starknet.deploy( - contract_class=account_cls, - constructor_calldata=[signer.eth_address] - ) - account2 = await starknet.deploy( - contract_class=account_cls, - constructor_calldata=[signer.eth_address] - ) - initializable1 = await starknet.deploy( - contract_class=init_cls, - constructor_calldata=[], - ) - initializable2 = await starknet.deploy( - contract_class=init_cls, - constructor_calldata=[], - ) - attacker = await starknet.deploy( - contract_class=attacker_cls, - constructor_calldata=[], - ) - - return starknet.state, account1, account2, initializable1, initializable2, attacker - - -@pytest.fixture -def account_factory(contract_defs, account_init): - account_cls, init_cls, attacker_cls = contract_defs - state, account1, account2, initializable1, initializable2, attacker = account_init - _state = state.copy() - account1 = cached_contract(_state, account_cls, account1) - account2 = cached_contract(_state, account_cls, account2) - initializable1 = cached_contract(_state, init_cls, initializable1) - initializable2 = cached_contract(_state, init_cls, initializable2) - attacker = cached_contract(_state, attacker_cls, attacker) - - return account1, account2, initializable1, initializable2, attacker - - -@pytest.mark.asyncio -async def test_counterfactual_deployment(account_factory): - account, *_ = account_factory - await signer.declare_class(account, "EthAccount") - - execution_info = await signer.deploy_account(account.state, [signer.eth_address]) - address = execution_info.validate_info.contract_address - - execution_info = await signer.send_transaction(account, address, 'getEthAddress', []) - assert execution_info.call_info.retdata[1] == signer.eth_address - - -@pytest.mark.asyncio -async def test_constructor(account_factory): - account, *_ = account_factory - - execution_info = await account.getEthAddress().call() - assert execution_info.result == (signer.eth_address,) - - execution_info = await account.supportsInterface(IACCOUNT_ID).call() - assert execution_info.result == (TRUE,) - - -@pytest.mark.asyncio -async def test_is_valid_signature(account_factory): - account, *_ = account_factory - hash = 0x23564 - signature = signer.sign(hash) - - execution_info = await account.isValidSignature(hash, signature).call() - assert execution_info.result == (TRUE,) - - # should revert if signature is not correct - await assert_revert( - account.isValidSignature(hash + 1, signature).call(), - reverted_with="Invalid signature" - ) - - -@pytest.mark.asyncio -async def test_declare(account_factory): - account, *_ = account_factory - - # regular declare works - await signer.declare_class(account, "ERC20") - - # wrong signer fails - await assert_revert( - other.declare_class(account, "ERC20"), - reverted_with="Invalid signature" - ) - - -@pytest.mark.asyncio -async def test_execute(account_factory): - account, _, initializable, *_ = account_factory - - execution_info = await initializable.initialized().call() - assert execution_info.result == (FALSE,) - - await signer.send_transaction(account, initializable.contract_address, 'initialize', []) - - execution_info = await initializable.initialized().call() - assert execution_info.result == (TRUE,) - - # wrong signer fails - await assert_revert( - other.send_transaction(account, initializable.contract_address, 'initialize', []), - reverted_with="Invalid signature" - ) - - -@pytest.mark.asyncio -async def test_multicall(account_factory): - account, _, initializable_1, initializable_2, _ = account_factory - - execution_info = await initializable_1.initialized().call() - assert execution_info.result == (FALSE,) - execution_info = await initializable_2.initialized().call() - assert execution_info.result == (FALSE,) - - await signer.send_transactions( - account, - [ - (initializable_1.contract_address, 'initialize', []), - (initializable_2.contract_address, 'initialize', []) - ] - ) - - execution_info = await initializable_1.initialized().call() - assert execution_info.result == (TRUE,) - execution_info = await initializable_2.initialized().call() - assert execution_info.result == (TRUE,) - - -@pytest.mark.asyncio -async def test_return_value(account_factory): - account, _, initializable, *_ = account_factory - - # initialize, set `initialized = 1` - await signer.send_transaction(account, initializable.contract_address, 'initialize', []) - - read_info = await signer.send_transaction(account, initializable.contract_address, 'initialized', []) - call_info = await initializable.initialized().call() - (call_result, ) = call_info.result - assert read_info.call_info.retdata[1] == call_result # 1 - - -@ pytest.mark.asyncio -async def test_nonce(account_factory): - account, _, initializable, *_ = account_factory - - # bump nonce - await signer.send_transaction(account, initializable.contract_address, 'initialized', []) - - args = [(initializable.contract_address, 'initialized', [])] - raw_invocation = get_raw_invoke(account, args) - current_nonce = await raw_invocation.state.state.get_nonce_at(account.contract_address) - - # lower nonce - await assert_revert( - signer.send_transaction(account, initializable.contract_address, 'initialize', [], current_nonce - 1), - reverted_with="Invalid transaction nonce. Expected: {}, got: {}.".format( - current_nonce, current_nonce - 1 - ) - ) - - # higher nonce - await assert_revert( - signer.send_transaction(account, initializable.contract_address, 'initialize', [], current_nonce + 1), - reverted_with="Invalid transaction nonce. Expected: {}, got: {}.".format( - current_nonce, current_nonce + 1 - ) - ) - - # right nonce - await signer.send_transaction(account, initializable.contract_address, 'initialize', [], current_nonce) - - execution_info = await initializable.initialized().call() - assert execution_info.result == (TRUE,) - - -@pytest.mark.asyncio -async def test_eth_address_setter(account_factory): - account, *_ = account_factory - - execution_info = await account.getEthAddress().call() - assert execution_info.result == (signer.eth_address,) - - # set new pubkey - await signer.send_transaction(account, account.contract_address, 'setEthAddress', [other.eth_address]) - - execution_info = await account.getEthAddress().call() - assert execution_info.result == (other.eth_address,) - - -@pytest.mark.asyncio -async def test_eth_address_setter_different_account(account_factory): - account, bad_account, *_ = account_factory - - # set new pubkey - await assert_revert( - signer.send_transaction(bad_account, account.contract_address, 'setEthAddress', [signer.eth_address]), - reverted_with="Account: caller is not this account" - ) - - -@pytest.mark.asyncio -async def test_account_takeover_with_reentrant_call(account_factory): - account, _, _, _, attacker = account_factory - - await assert_revert( - signer.send_transaction(account, attacker.contract_address, 'account_takeover', []), - reverted_with="Account: reentrant call" - ) - - execution_info = await account.getEthAddress().call() - assert execution_info.result == (signer.eth_address,) diff --git a/tests/conftest.py b/tests/conftest.py deleted file mode 100644 index 56a1af737..000000000 --- a/tests/conftest.py +++ /dev/null @@ -1,6 +0,0 @@ -import pytest -import asyncio - -@pytest.fixture(scope='module') -def event_loop(): - return asyncio.new_event_loop() diff --git a/tests/introspection/test_ERC165.py b/tests/introspection/test_ERC165.py deleted file mode 100644 index 52a8962a8..000000000 --- a/tests/introspection/test_ERC165.py +++ /dev/null @@ -1,68 +0,0 @@ -import pytest -from nile.utils import assert_revert, TRUE, FALSE -from utils import ( - get_contract_class, - cached_contract, - State -) - - -# interface ids -ERC165_ID = 0x01ffc9a7 -INVALID_ID = 0xffffffff -OTHER_ID = 0x12345678 - - -@pytest.fixture -async def erc165_factory(): - # class - erc165_cls = get_contract_class("tests/mocks/ERC165.cairo", is_path=True) - - # deployment - starknet = await State.init() - erc165 = await starknet.deploy(contract_class=erc165_cls) - - # cache - state = starknet.state.copy() - erc165 = cached_contract(state, erc165_cls, erc165) - return erc165 - - -@pytest.mark.asyncio -async def test_165_interface(erc165_factory): - erc165 = erc165_factory - - execution_info = await erc165.supportsInterface(ERC165_ID).call() - assert execution_info.result == (TRUE,) - - -@pytest.mark.asyncio -async def test_invalid_id(erc165_factory): - erc165 = erc165_factory - - execution_info = await erc165.supportsInterface(INVALID_ID).call() - assert execution_info.result == (FALSE,) - - -@pytest.mark.asyncio -async def test_register_interface(erc165_factory): - erc165 = erc165_factory - - execution_info = await erc165.supportsInterface(OTHER_ID).call() - assert execution_info.result == (FALSE,) - - # register interface - await erc165.registerInterface(OTHER_ID).execute() - - execution_info = await erc165.supportsInterface(OTHER_ID).call() - assert execution_info.result == (TRUE,) - - -@pytest.mark.asyncio -async def test_register_invalid_interface(erc165_factory): - erc165 = erc165_factory - - await assert_revert( - erc165.registerInterface(INVALID_ID).execute(), - reverted_with="ERC165: invalid interface id" - ) diff --git a/tests/mocks/AccessControl.cairo b/tests/mocks/AccessControl.cairo deleted file mode 100644 index b0edacfc1..000000000 --- a/tests/mocks/AccessControl.cairo +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-License-Identifier: MIT - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.starknet.common.syscalls import get_caller_address -from openzeppelin.access.accesscontrol.library import AccessControl -from openzeppelin.introspection.erc165.library import ERC165 -from openzeppelin.utils.constants.library import DEFAULT_ADMIN_ROLE - -@constructor -func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(admin: felt) { - AccessControl.initializer(); - AccessControl._grant_role(DEFAULT_ADMIN_ROLE, admin); - return (); -} - -@view -func hasRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - role: felt, user: felt -) -> (hasRole: felt) { - let (hasRole) = AccessControl.has_role(role, user); - return (hasRole=hasRole); -} - -@view -func getRoleAdmin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(role: felt) -> ( - admin: felt -) { - return AccessControl.get_role_admin(role); -} - -@external -func grantRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - role: felt, user: felt -) { - AccessControl.grant_role(role, user); - return (); -} - -@external -func revokeRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - role: felt, user: felt -) { - AccessControl.revoke_role(role, user); - return (); -} - -@external -func renounceRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - role: felt, user: felt -) { - AccessControl.renounce_role(role, user); - return (); -} - -// ONLY FOR MOCKS, DON'T EXPOSE IN PRODUCTION -@external -func setRoleAdmin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - role: felt, admin: felt -) { - AccessControl._set_role_admin(role, admin); - return (); -} - -@view -func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - interfaceId: felt -) -> (success: felt) { - return ERC165.supports_interface(interfaceId); -} diff --git a/tests/mocks/AccountReentrancy.cairo b/tests/mocks/AccountReentrancy.cairo deleted file mode 100644 index ed03f78b9..000000000 --- a/tests/mocks/AccountReentrancy.cairo +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: MIT - -%lang starknet - -from starkware.starknet.common.syscalls import ( - call_contract, - get_caller_address, -) -from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin - -from starkware.cairo.common.alloc import alloc - -const EXECUTE = 617075754465154585683856897856256838130216341506379215893724690153393808813; -const SET_PUBLIC_KEY = 332268845949430430346835224631316185987738351560356300584998172574125127129; - -@external -func account_takeover{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - alloc_locals; - let (caller) = get_caller_address(); - let (call_calldata: felt*) = alloc(); - - // call_array - assert call_calldata[0] = 1; - assert call_calldata[1] = caller; - assert call_calldata[2] = SET_PUBLIC_KEY; - assert call_calldata[3] = 0; - assert call_calldata[4] = 1; - - // calldata - assert call_calldata[5] = 1; - // new public key - assert call_calldata[6] = 123; - - - call_contract( - contract_address=caller, function_selector=EXECUTE, calldata_size=7, calldata=call_calldata - ); - - return (); -} diff --git a/tests/mocks/ERC1155Mock.cairo b/tests/mocks/ERC1155Mock.cairo deleted file mode 100644 index 17cbe5504..000000000 --- a/tests/mocks/ERC1155Mock.cairo +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: MIT - -// This mock is to test functions from the ERC1155 library -// that are not exposed in any preset like `setURI` - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.uint256 import Uint256 - -from openzeppelin.token.erc1155.library import ERC1155 - - -@constructor -func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - uri: felt, owner: felt -) { - ERC1155.initializer(uri); - return (); -} - -@view -func uri{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(id: Uint256) -> ( - uri: felt -) { - return ERC1155.uri(id); -} - - -@external -func setURI{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(uri: felt) { - ERC1155._set_uri(uri); - return (); -} diff --git a/tests/mocks/ERC165.cairo b/tests/mocks/ERC165.cairo deleted file mode 100644 index 8771eac32..000000000 --- a/tests/mocks/ERC165.cairo +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: MIT - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin - -from openzeppelin.introspection.erc165.library import ERC165 - -@view -func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - interfaceId: felt -) -> (success: felt) { - return ERC165.supports_interface(interfaceId); -} - -@external -func registerInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - interfaceId: felt -) { - ERC165.register_interface(interfaceId); - return (); -} diff --git a/tests/mocks/ERC721SafeMintableMock.cairo b/tests/mocks/ERC721SafeMintableMock.cairo deleted file mode 100644 index a854dc10f..000000000 --- a/tests/mocks/ERC721SafeMintableMock.cairo +++ /dev/null @@ -1,162 +0,0 @@ -// SPDX-License-Identifier: MIT - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin -from starkware.cairo.common.uint256 import Uint256 - -from openzeppelin.token.erc721.library import ERC721 -from openzeppelin.introspection.erc165.library import ERC165 -from openzeppelin.access.ownable.library import Ownable - -// -// Constructor -// - -@constructor -func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - name: felt, symbol: felt, owner: felt -) { - ERC721.initializer(name, symbol); - Ownable.initializer(owner); - return (); -} - -// -// Getters -// - -@view -func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - interfaceId: felt -) -> (success: felt) { - return ERC165.supports_interface(interfaceId); -} - -@view -func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) { - return ERC721.name(); -} - -@view -func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) { - return ERC721.symbol(); -} - -@view -func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(owner: felt) -> ( - balance: Uint256 -) { - return ERC721.balance_of(owner); -} - -@view -func ownerOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(tokenId: Uint256) -> ( - owner: felt -) { - return ERC721.owner_of(tokenId); -} - -@view -func getApproved{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - tokenId: Uint256 -) -> (approved: felt) { - return ERC721.get_approved(tokenId); -} - -@view -func isApprovedForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - owner: felt, operator: felt -) -> (approved: felt) { - return ERC721.is_approved_for_all(owner, operator); -} - -@view -func tokenURI{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - tokenId: Uint256 -) -> (tokenURI: felt) { - let (tokenURI: felt) = ERC721.token_uri(tokenId); - return (tokenURI=tokenURI); -} - -@view -func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) { - return Ownable.owner(); -} - -// -// Externals -// - -@external -func approve{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - to: felt, tokenId: Uint256 -) { - ERC721.approve(to, tokenId); - return (); -} - -@external -func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - operator: felt, approved: felt -) { - ERC721.set_approval_for_all(operator, approved); - return (); -} - -@external -func transferFrom{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - from_: felt, to: felt, tokenId: Uint256 -) { - ERC721.transfer_from(from_, to, tokenId); - return (); -} - -@external -func safeTransferFrom{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - from_: felt, to: felt, tokenId: Uint256, data_len: felt, data: felt* -) { - ERC721.safe_transfer_from(from_, to, tokenId, data_len, data); - return (); -} - -@external -func mint{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - to: felt, tokenId: Uint256 -) { - Ownable.assert_only_owner(); - ERC721._mint(to, tokenId); - return (); -} - -@external -func safeMint{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - to: felt, tokenId: Uint256, data_len: felt, data: felt* -) { - Ownable.assert_only_owner(); - ERC721._safe_mint(to, tokenId, data_len, data); - return (); -} - -@external -func setTokenURI{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - tokenId: Uint256, tokenURI: felt -) { - Ownable.assert_only_owner(); - ERC721._set_token_uri(tokenId, tokenURI); - return (); -} - -@external -func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - newOwner: felt -) { - Ownable.transfer_ownership(newOwner); - return (); -} - -@external -func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - Ownable.renounce_ownership(); - return (); -} diff --git a/tests/mocks/Initializable.cairo b/tests/mocks/Initializable.cairo deleted file mode 100644 index 7cddc5fc8..000000000 --- a/tests/mocks/Initializable.cairo +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: MIT - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin - -from openzeppelin.security.initializable.library import Initializable - -@view -func initialized{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (initialized: felt) { - return Initializable.initialized(); -} - -@external -func initialize{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - Initializable.initialize(); - return (); -} diff --git a/tests/mocks/Ownable.cairo b/tests/mocks/Ownable.cairo deleted file mode 100644 index 04902e0ad..000000000 --- a/tests/mocks/Ownable.cairo +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: MIT - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.starknet.common.syscalls import get_caller_address -from openzeppelin.access.ownable.library import Ownable -from starkware.cairo.common.bool import TRUE - -@constructor -func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(owner: felt) { - Ownable.initializer(owner); - return (); -} - -@external -func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) { - return Ownable.owner(); -} - -@external -func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - new_owner: felt -) { - Ownable.transfer_ownership(new_owner); - return (); -} - -@external -func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - Ownable.renounce_ownership(); - return (); -} - -@external -func protected_function{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - success: felt -) { - Ownable.assert_only_owner(); - return (success=TRUE); -} diff --git a/tests/mocks/Pausable.cairo b/tests/mocks/Pausable.cairo deleted file mode 100644 index cbe7be839..000000000 --- a/tests/mocks/Pausable.cairo +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: MIT - -%lang starknet - -from starkware.starknet.common.syscalls import get_caller_address -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.bool import TRUE, FALSE - -from openzeppelin.security.pausable.library import Pausable - -@storage_var -func drastic_measure_taken() -> (success: felt) { -} - -@storage_var -func counter() -> (count: felt) { -} - -@view -func isPaused{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - paused: felt -) { - return Pausable.is_paused(); -} - -@view -func getCount{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (count: felt) { - return counter.read(); -} - -@view -func getDrasticMeasureTaken{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - success: felt -) { - return drastic_measure_taken.read(); -} - -@external -func normalProcess{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - Pausable.assert_not_paused(); - - let (currentCount) = counter.read(); - counter.write(currentCount + 1); - return (); -} - -@external -func drasticMeasure{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - Pausable.assert_paused(); - - drastic_measure_taken.write(TRUE); - return (); -} - -@external -func pause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - Pausable._pause(); - return (); -} - -@external -func unpause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - Pausable._unpause(); - return (); -} diff --git a/tests/mocks/ProxiableImplementation.cairo b/tests/mocks/ProxiableImplementation.cairo deleted file mode 100644 index 9e5118879..000000000 --- a/tests/mocks/ProxiableImplementation.cairo +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: MIT - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin - -from openzeppelin.upgrades.library import Proxy - -// -// Storage -// - -@storage_var -func value() -> (val: felt) { -} - -// -// Initializer -// - -@external -func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - proxy_admin: felt -) { - Proxy.initializer(proxy_admin); - return (); -} - -// -// Getters -// - -@view -func getValue{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (val: felt) { - return value.read(); -} - -@view -func getAdmin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - admin: felt -) { - return Proxy.get_admin(); -} - -// -// Setters -// - -@external -func setValue{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(val: felt) { - value.write(val); - return (); -} - -@external -func setAdmin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(address: felt) { - Proxy.assert_only_admin(); - Proxy._set_admin(address); - return (); -} diff --git a/tests/mocks/ReentrancyAttackerMock.cairo b/tests/mocks/ReentrancyAttackerMock.cairo deleted file mode 100644 index bda88116e..000000000 --- a/tests/mocks/ReentrancyAttackerMock.cairo +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.starknet.common.syscalls import get_caller_address - -@contract_interface -namespace IReentrancyGuard { - func callback() { - } -} - -@external -func call_sender{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - let (caller) = get_caller_address(); - IReentrancyGuard.callback(contract_address=caller); - return (); -} diff --git a/tests/mocks/ReentrancyMock.cairo b/tests/mocks/ReentrancyMock.cairo deleted file mode 100644 index 2c029af78..000000000 --- a/tests/mocks/ReentrancyMock.cairo +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-License-Identifier: MIT - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.math_cmp import is_le -from starkware.cairo.common.bool import TRUE -from starkware.starknet.common.syscalls import get_contract_address - -from openzeppelin.security.reentrancyguard.library import ReentrancyGuard - -@contract_interface -namespace IReentrancyGuardAttacker { - func call_sender() { - } -} - -@contract_interface -namespace IReentrancyGuard { - func count_this_recursive(n: felt) { - } -} - -@storage_var -func counter() -> (count: felt) { -} - -@constructor -func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - initial_number: felt -) { - counter.write(initial_number); - return (); -} - -@view -func current_count{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - count: felt -) { - return counter.read(); -} - -@external -func callback{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - ReentrancyGuard.start(); - _count(); - ReentrancyGuard.end(); - return (); -} - -@external -func count_local_recursive{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - n: felt -) { - alloc_locals; - ReentrancyGuard.start(); - let greater_zero = is_le(1, n); - if (greater_zero == TRUE) { - _count(); - count_local_recursive(n - 1); - tempvar syscall_ptr = syscall_ptr; - tempvar pedersen_ptr = pedersen_ptr; - tempvar range_check_ptr = range_check_ptr; - } else { - tempvar syscall_ptr = syscall_ptr; - tempvar pedersen_ptr = pedersen_ptr; - tempvar range_check_ptr = range_check_ptr; - } - ReentrancyGuard.end(); - return (); -} - -@external -func count_this_recursive{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - n: felt -) { - alloc_locals; - ReentrancyGuard.start(); - let greater_zero = is_le(1, n); - if (greater_zero == TRUE) { - _count(); - let (contract_address) = get_contract_address(); - IReentrancyGuard.count_this_recursive(contract_address=contract_address, n=n - 1); - tempvar syscall_ptr = syscall_ptr; - tempvar pedersen_ptr = pedersen_ptr; - tempvar range_check_ptr = range_check_ptr; - } else { - tempvar syscall_ptr = syscall_ptr; - tempvar pedersen_ptr = pedersen_ptr; - tempvar range_check_ptr = range_check_ptr; - } - ReentrancyGuard.end(); - return (); -} - -@external -func count_and_call{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - attacker: felt -) { - ReentrancyGuard.start(); - _count(); - IReentrancyGuardAttacker.call_sender(attacker); - ReentrancyGuard.end(); - return (); -} - -func _count{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - let (current_count) = counter.read(); - counter.write(current_count + 1); - return (); -} diff --git a/tests/mocks/SafeMathMock.cairo b/tests/mocks/SafeMathMock.cairo deleted file mode 100644 index 51efa8ec9..000000000 --- a/tests/mocks/SafeMathMock.cairo +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: MIT - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin -from starkware.cairo.common.uint256 import Uint256 - -from openzeppelin.security.safemath.library import SafeUint256 - -@view -func uint256_add{range_check_ptr}(a: Uint256, b: Uint256) -> (c: Uint256) { - return SafeUint256.add(a, b); -} - -@view -func uint256_sub_le{range_check_ptr}(a: Uint256, b: Uint256) -> (c: Uint256) { - return SafeUint256.sub_le(a, b); -} - -@view -func uint256_sub_lt{range_check_ptr}(a: Uint256, b: Uint256) -> (c: Uint256) { - return SafeUint256.sub_lt(a, b); -} - -@view -func uint256_mul{range_check_ptr}(a: Uint256, b: Uint256) -> (c: Uint256) { - return SafeUint256.mul(a, b); -} - -@view -func uint256_div{range_check_ptr}(a: Uint256, b: Uint256) -> (c: Uint256, rem: Uint256) { - return SafeUint256.div_rem(a, b); -} diff --git a/tests/mocks/UpgradesMockV1.cairo b/tests/mocks/UpgradesMockV1.cairo deleted file mode 100644 index 3ca050412..000000000 --- a/tests/mocks/UpgradesMockV1.cairo +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-License-Identifier: MIT - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin - -from openzeppelin.upgrades.library import Proxy, Proxy_initialized - -// -// Storage -// - -@storage_var -func value_1() -> (val: felt) { -} - -// -// Initializer -// - -@external -func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - proxy_admin: felt -) { - Proxy.initializer(proxy_admin); - return (); -} - -@view -func initialized{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}() --> (initialized: felt) { - let (initialized) = Proxy_initialized.read(); - return (initialized=initialized); -} - -// -// Upgrades -// - -@external -func upgrade{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - new_implementation: felt -) { - Proxy.assert_only_admin(); - Proxy._set_implementation_hash(new_implementation); - return (); -} - -// -// Getters -// - -@view -func getValue1{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (val: felt) { - return value_1.read(); -} - -// -// Setters -// - -@external -func setValue1{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(val: felt) { - value_1.write(val); - return (); -} diff --git a/tests/mocks/UpgradesMockV2.cairo b/tests/mocks/UpgradesMockV2.cairo deleted file mode 100644 index 78b49ced2..000000000 --- a/tests/mocks/UpgradesMockV2.cairo +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: MIT - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin - -from openzeppelin.upgrades.library import Proxy - -// -// Storage -// - -@storage_var -func value_1() -> (val: felt) { -} - -@storage_var -func value_2() -> (val: felt) { -} - -// -// Initializer -// - -@external -func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - proxy_admin: felt -) { - Proxy.initializer(proxy_admin); - return (); -} - -// -// Upgrades -// - -@external -func upgrade{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - new_implementation: felt -) { - Proxy.assert_only_admin(); - Proxy._set_implementation_hash(new_implementation); - return (); -} - -// -// Getters -// - -@view -func getValue1{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (val: felt) { - return value_1.read(); -} - -@view -func getValue2{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (val: felt) { - return value_2.read(); -} - -@view -func getImplementationHash{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - implementation: felt -) { - return Proxy.get_implementation_hash(); -} - -@view -func getAdmin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (admin: felt) { - return Proxy.get_admin(); -} - -// -// Setters -// - -@external -func setValue1{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(val: felt) { - value_1.write(val); - return (); -} - -@external -func setValue2{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(val: felt) { - value_2.write(val); - return (); -} - -@external -func setAdmin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(new_admin: felt) { - Proxy.assert_only_admin(); - Proxy._set_admin(new_admin); - return (); -} diff --git a/tests/security/test_initializable.py b/tests/security/test_initializable.py deleted file mode 100644 index 602a0bf77..000000000 --- a/tests/security/test_initializable.py +++ /dev/null @@ -1,24 +0,0 @@ -import pytest -from nile.utils import TRUE, FALSE, assert_revert -from utils import get_contract_class, State - - -@pytest.mark.asyncio -async def test_initializer(): - starknet = await State.init() - initializable = await starknet.deploy( - contract_class=get_contract_class("Initializable") - ) - expected = await initializable.initialized().call() - assert expected.result == (FALSE,) - - await initializable.initialize().execute() - - expected = await initializable.initialized().call() - assert expected.result == (TRUE,) - - # second initialize invocation should revert - await assert_revert( - initializable.initialize().execute(), - reverted_with="Initializable: contract already initialized" - ) diff --git a/tests/security/test_pausable.py b/tests/security/test_pausable.py deleted file mode 100644 index a7e391ff4..000000000 --- a/tests/security/test_pausable.py +++ /dev/null @@ -1,146 +0,0 @@ -import pytest -from signers import MockSigner -from nile.utils import TRUE, FALSE, assert_revert -from utils import ( - get_contract_class, cached_contract, assert_event_emitted, State, Account -) - - -signer = MockSigner(12345678987654321) - -@pytest.fixture -async def pausable_factory(): - # class - pausable_cls = get_contract_class("Pausable") - account_cls = Account.get_class - - # deploy - starknet = await State.init() - account = await Account.deploy(signer.public_key) - pausable = await starknet.deploy( - contract_class=pausable_cls, - constructor_calldata=[] - ) - state = starknet.state.copy() - - # cache - pausable = cached_contract(state, pausable_cls, pausable) - account = cached_contract(state, account_cls, account) - - return pausable, account - - -@pytest.mark.asyncio -async def test_pausable_when_unpaused(pausable_factory): - contract, _ = pausable_factory - - execution_info = await contract.isPaused().call() - assert execution_info.result.paused == FALSE - - execution_info = await contract.getCount().call() - assert execution_info.result.count == 0 - - # check that function executes when unpaused - await contract.normalProcess().execute() - - execution_info = await contract.getCount().call() - assert execution_info.result.count == 1 - - await assert_revert( - contract.drasticMeasure().execute(), - reverted_with="Pausable: not paused" - ) - -@pytest.mark.asyncio -async def test_pausable_when_paused(pausable_factory): - contract, _ = pausable_factory - - execution_info = await contract.isPaused().call() - assert execution_info.result.paused == FALSE - - # pause - await contract.pause().execute() - - execution_info = await contract.isPaused().call() - assert execution_info.result.paused == TRUE - - await assert_revert( - contract.normalProcess().execute(), - reverted_with="Pausable: paused" - ) - - execution_info = await contract.getDrasticMeasureTaken().call() - assert execution_info.result.success == FALSE - - # drastic measure - await contract.drasticMeasure().execute() - - execution_info = await contract.getDrasticMeasureTaken().call() - assert execution_info.result.success == TRUE - - # unpause - await contract.unpause().execute() - - execution_info = await contract.isPaused().call() - assert execution_info.result.paused == FALSE - - # check normal process after unpausing - await contract.normalProcess().execute() - - execution_info = await contract.getCount().call() - assert execution_info.result.count == 1 - - await assert_revert( - contract.drasticMeasure().execute(), - reverted_with="Pausable: not paused" - ) - -@pytest.mark.asyncio -async def test_pausable_pause_when_paused(pausable_factory): - contract, _ = pausable_factory - - # pause - await contract.pause().execute() - - # re-pause - await assert_revert( - contract.pause().execute(), - reverted_with="Pausable: paused" - ) - - # unpause - await contract.unpause().execute() - - # re-unpause - await assert_revert( - contract.unpause().execute(), - reverted_with="Pausable: not paused" - ) - -@pytest.mark.asyncio -async def test_pausable_emits_events(pausable_factory): - contract, account = pausable_factory - - # pause - tx_exec_info = await signer.send_transaction( - account, contract.contract_address, 'pause', [] - ) - - assert_event_emitted( - tx_exec_info, - from_address=contract.contract_address, - name='Paused', - data=[account.contract_address] - ) - - # unpause - tx_exec_info = await signer.send_transaction( - account, contract.contract_address, 'unpause', [] - ) - - assert_event_emitted( - tx_exec_info, - from_address=contract.contract_address, - name='Unpaused', - data=[account.contract_address] - ) diff --git a/tests/security/test_reentrancy.py b/tests/security/test_reentrancy.py deleted file mode 100644 index 8c096fe9d..000000000 --- a/tests/security/test_reentrancy.py +++ /dev/null @@ -1,60 +0,0 @@ -import pytest -from nile.utils import assert_revert -from utils import get_contract_class, State - -INITIAL_COUNTER = 0 - - -@pytest.fixture(scope='module') -async def reentrancy_mock(): - starknet = await State.init() - contract = await starknet.deploy( - contract_class=get_contract_class("ReentrancyMock"), - constructor_calldata=[INITIAL_COUNTER] - ) - - return contract, starknet - - -@pytest.mark.asyncio -async def test_reentrancy_guard_deploy(reentrancy_mock): - contract, _ = reentrancy_mock - response = await contract.current_count().call() - - assert response.result == (INITIAL_COUNTER,) - - -@pytest.mark.asyncio -async def test_reentrancy_guard_remote_callback(reentrancy_mock): - contract, starknet = reentrancy_mock - attacker = await starknet.deploy("tests/mocks/ReentrancyAttackerMock.cairo") - # should not allow remote callback - await assert_revert( - contract.count_and_call(attacker.contract_address).execute(), - reverted_with="ReentrancyGuard: reentrant call" - ) - - -@pytest.mark.asyncio -async def test_reentrancy_guard_local_recursion(reentrancy_mock): - contract, _ = reentrancy_mock - # should not allow local recursion - await assert_revert( - contract.count_local_recursive(10).execute(), - reverted_with="ReentrancyGuard: reentrant call" - ) - # should not allow indirect local recursion - await assert_revert( - contract.count_this_recursive(10).execute(), - reverted_with="ReentrancyGuard: reentrant call" - ) - - -@pytest.mark.asyncio -async def test_reentrancy_guard(reentrancy_mock): - contract, _ = reentrancy_mock - # should allow non reentrant call - await contract.callback().execute() - response = await contract.current_count().call() - - assert response.result == (1,) diff --git a/tests/security/test_safemath.py b/tests/security/test_safemath.py deleted file mode 100644 index f551eb47f..000000000 --- a/tests/security/test_safemath.py +++ /dev/null @@ -1,206 +0,0 @@ -import pytest -from nile.utils import ( - MAX_UINT256, assert_revert, add_uint, sub_uint, - mul_uint, div_rem_uint, to_uint -) -from utils import get_contract_class, State - - -@pytest.fixture(scope='module') -async def safemath_mock(): - starknet = await State.init() - safemath = await starknet.deploy( - contract_class=get_contract_class("SafeMathMock") - ) - - return safemath - - -@pytest.mark.asyncio -async def test_add(safemath_mock): - safemath = safemath_mock - - a = to_uint(56789) - b = to_uint(1234) - c = add_uint(a, b) - - execution_info = await safemath.uint256_add(a, b).execute() - assert execution_info.result == (c,) - - -@pytest.mark.asyncio -async def test_add_overflow(safemath_mock): - safemath = safemath_mock - - a = MAX_UINT256 - b = to_uint(1) - - await assert_revert( - safemath.uint256_add(a, b).execute(), - reverted_with="SafeUint256: addition overflow" - ) - - -@pytest.mark.asyncio -async def test_sub_lt(safemath_mock): - safemath = safemath_mock - - a = to_uint(56789) - b = to_uint(1234) - c = sub_uint(a, b) - - execution_info = await safemath.uint256_sub_lt(a, b).execute() - assert execution_info.result == (c,) - - -@pytest.mark.asyncio -async def test_sub_lt_equal(safemath_mock): - safemath = safemath_mock - - a = MAX_UINT256 - b = MAX_UINT256 - - await assert_revert( - safemath.uint256_sub_lt(a, b).execute(), - reverted_with="SafeUint256: subtraction overflow or the difference equals zero" - ) - - -@pytest.mark.asyncio -async def test_sub_lt_overflow(safemath_mock): - safemath = safemath_mock - - a = to_uint(1234) - b = to_uint(56789) - - await assert_revert( - safemath.uint256_sub_lt(a, b).execute(), - reverted_with="SafeUint256: subtraction overflow or the difference equals zero" - ) - - -@pytest.mark.asyncio -async def test_sub_le(safemath_mock): - safemath = safemath_mock - - a = to_uint(56789) - b = to_uint(1234) - c = sub_uint(a, b) - - execution_info = await safemath.uint256_sub_le(a, b).execute() - assert execution_info.result == (c,) - - -@pytest.mark.asyncio -async def test_sub_le_equal(safemath_mock): - safemath = safemath_mock - - a = MAX_UINT256 - b = MAX_UINT256 - c = sub_uint(a, b) - - execution_info = await safemath.uint256_sub_le(a, b).execute() - assert execution_info.result == (c,) - - -@pytest.mark.asyncio -async def test_sub_le_overflow(safemath_mock): - safemath = safemath_mock - - a = to_uint(1234) - b = to_uint(56789) - - await assert_revert( - safemath.uint256_sub_le(a, b).execute(), - reverted_with="SafeUint256: subtraction overflow" - ) - await assert_revert(safemath.uint256_sub_le(a, b).execute()) - - -@pytest.mark.asyncio -async def test_mul(safemath_mock): - safemath = safemath_mock - - a = to_uint(1234) - b = to_uint(56789) - c = mul_uint(a, b) - - execution_info = await safemath.uint256_mul(a, b).execute() - assert execution_info.result == (c,) - - -@pytest.mark.asyncio -async def test_mul_zero(safemath_mock): - safemath = safemath_mock - - a = to_uint(0) - b = to_uint(56789) - c = to_uint(0) - - execution_info = await safemath.uint256_mul(a, b).execute() - assert execution_info.result == (c,) - - execution_info = await safemath.uint256_mul(b, a).execute() - assert execution_info.result == (c,) - - -@pytest.mark.asyncio -async def test_mul_overflow(safemath_mock): - safemath = safemath_mock - - a = MAX_UINT256 - b = to_uint(2) - - await assert_revert( - safemath.uint256_mul(a, b).execute(), - reverted_with="SafeUint256: multiplication overflow" - ) - - -@pytest.mark.asyncio -async def test_div(safemath_mock): - safemath = safemath_mock - - a = to_uint(56789) - b = to_uint(56789) - (c, r) = div_rem_uint(a, b) - - execution_info = await safemath.uint256_div(a, b).execute() - assert execution_info.result == (c, r) - - -@pytest.mark.asyncio -async def test_div_zero_dividend(safemath_mock): - safemath = safemath_mock - - a = to_uint(0) - b = to_uint(56789) - (c, r) = div_rem_uint(a, b) - - execution_info = await safemath.uint256_div(a, b).execute() - assert execution_info.result == (c, r) - - -@pytest.mark.asyncio -async def test_div_zero_divisor(safemath_mock): - safemath = safemath_mock - - a = to_uint(56789) - b = to_uint(0) - - await assert_revert( - safemath.uint256_div(a, b).execute(), - reverted_with="SafeUint256: divisor cannot be zero" - ) - - -@pytest.mark.asyncio -async def test_div_uneven_division(safemath_mock): - safemath = safemath_mock - - a = to_uint(7000) - b = to_uint(5678) - (c, r) = div_rem_uint(a, b) - - execution_info = await safemath.uint256_div(a, b).execute() - assert execution_info.result == (c, r) diff --git a/tests/signers.py b/tests/signers.py deleted file mode 100644 index ca614f4e6..000000000 --- a/tests/signers.py +++ /dev/null @@ -1,215 +0,0 @@ -from typing import Tuple -from starkware.starknet.core.os.transaction_hash.transaction_hash import TransactionHashPrefix -from starkware.starknet.core.os.contract_address.contract_address import ( - calculate_contract_address_from_hash, -) -from starkware.starknet.definitions.general_config import StarknetChainId -from starkware.starknet.services.api.gateway.transaction import InvokeFunction, DeployAccount -from starkware.starknet.business_logic.transaction.objects import InternalTransaction, InternalDeclare, TransactionExecutionInfo -from nile.signer import Signer, from_call_to_call_array, get_transaction_hash, TRANSACTION_VERSION -from nile.utils import to_uint -from utils import get_class_hash, get_contract_class -import eth_keys - - -class BaseSigner(): - async def send_transaction(self, account, to, selector_name, calldata, nonce=None, max_fee=0): - return await self.send_transactions(account, [(to, selector_name, calldata)], nonce, max_fee) - - async def send_transactions( - self, - account, - calls, - nonce=None, - max_fee=0 - ) -> TransactionExecutionInfo: - raw_invocation = get_raw_invoke(account, calls) - state = raw_invocation.state - - if nonce is None: - nonce = await state.state.get_nonce_at(account.contract_address) - - transaction_hash = get_transaction_hash( - prefix=TransactionHashPrefix.INVOKE, - account=account.contract_address, - calldata=raw_invocation.calldata, - version=TRANSACTION_VERSION, - chain_id=StarknetChainId.TESTNET.value, - nonce=nonce, - max_fee=max_fee, - ) - - signature = self.sign(transaction_hash) - - external_tx = InvokeFunction( - contract_address=account.contract_address, - calldata=raw_invocation.calldata, - entry_point_selector=None, - signature=signature, - max_fee=max_fee, - version=TRANSACTION_VERSION, - nonce=nonce, - ) - - tx = InternalTransaction.from_external( - external_tx=external_tx, general_config=state.general_config - ) - execution_info = await state.execute_tx(tx=tx) - return execution_info - - async def declare_class( - self, - account, - contract_name, - nonce=None, - max_fee=0, - ) -> Tuple[int, TransactionExecutionInfo]: - state = account.state - - if nonce is None: - nonce = await state.state.get_nonce_at(contract_address=account.contract_address) - - contract_class = get_contract_class(contract_name) - class_hash = get_class_hash(contract_name) - - transaction_hash = get_transaction_hash( - prefix=TransactionHashPrefix.DECLARE, - account=account.contract_address, - calldata=[class_hash], - nonce=nonce, - version=TRANSACTION_VERSION, - max_fee=max_fee, - chain_id=StarknetChainId.TESTNET.value - ) - - signature = self.sign(transaction_hash) - - tx = InternalDeclare.create( - sender_address=account.contract_address, - contract_class=contract_class, - chain_id=StarknetChainId.TESTNET.value, - max_fee=max_fee, - version=TRANSACTION_VERSION, - nonce=nonce, - signature=signature, - ) - - execution_info = await state.execute_tx(tx=tx) - - await state.state.set_contract_class( - class_hash=tx.class_hash, - contract_class=contract_class - ) - return class_hash, execution_info - - async def deploy_account( - self, - state, - calldata, - salt=0, - nonce=0, - max_fee=0, - ) -> TransactionExecutionInfo: - account_address = calculate_contract_address_from_hash( - salt=salt, - class_hash=self.class_hash, - constructor_calldata=calldata, - deployer_address=0 - ) - - transaction_hash = get_transaction_hash( - prefix=TransactionHashPrefix.DEPLOY_ACCOUNT, - account=account_address, - calldata=[self.class_hash, salt, *calldata], - nonce=nonce, - version=TRANSACTION_VERSION, - max_fee=max_fee, - chain_id=StarknetChainId.TESTNET.value - ) - - signature = self.sign(transaction_hash) - - external_tx = DeployAccount( - class_hash=self.class_hash, - contract_address_salt=salt, - constructor_calldata=calldata, - signature=signature, - max_fee=max_fee, - version=TRANSACTION_VERSION, - nonce=nonce, - ) - - tx = InternalTransaction.from_external( - external_tx=external_tx, general_config=state.general_config - ) - - execution_info = await state.execute_tx(tx=tx) - return execution_info - -class MockSigner(BaseSigner): - """ - Utility for sending signed transactions to an Account on Starknet. - - Parameters - ---------- - - private_key : int - - Examples - --------- - Constructing a MockSigner object - - >>> signer = MockSigner(1234) - - Sending a transaction - - >>> await signer.send_transaction( - account, contract_address, 'contract_method', [arg_1] - ) - - Sending multiple transactions - - >>> await signer.send_transactions( - account, [ - (contract_address, 'contract_method', [arg_1]), - (contract_address, 'another_method', [arg_1, arg_2]) - ] - ) - - """ - def __init__(self, private_key): - self.signer = Signer(private_key) - self.public_key = self.signer.public_key - self.class_hash = get_class_hash("Account") - - def sign(self, transaction_hash): - sig_r, sig_s = self.signer.sign(transaction_hash) - return [sig_r, sig_s] - - -class MockEthSigner(BaseSigner): - """ - Utility for sending signed transactions to an Account on Starknet, like MockSigner, but using a secp256k1 signature. - Parameters - ---------- - private_key : int - - """ - def __init__(self, private_key): - self.signer = eth_keys.keys.PrivateKey(private_key) - self.eth_address = int(self.signer.public_key.to_checksum_address(), 0) - self.class_hash = get_class_hash("EthAccount") - - def sign(self, transaction_hash): - signature = self.signer.sign_msg_hash( - (transaction_hash).to_bytes(32, byteorder="big")) - sig_r = to_uint(signature.r) - sig_s = to_uint(signature.s) - return [signature.v, *sig_r, *sig_s] - - -def get_raw_invoke(sender, calls): - """Return raw invoke, remove when test framework supports `invoke`.""" - call_array, calldata = from_call_to_call_array(calls) - raw_invocation = sender.__execute__(call_array, calldata) - return raw_invocation diff --git a/tests/test_signers.py b/tests/test_signers.py deleted file mode 100644 index 82e343436..000000000 --- a/tests/test_signers.py +++ /dev/null @@ -1,72 +0,0 @@ -import pytest - -from signers import MockSigner -from utils import ( - State, - Account, - get_contract_class, - cached_contract, - FALSE, - TRUE, -) - -signer = MockSigner(123456789987654321) - - -@pytest.fixture(scope='module') -def contract_classes(): - account_cls = Account.get_class - deployer_cls = get_contract_class('UniversalDeployer') - - return account_cls, deployer_cls - - -@pytest.fixture(scope='module') -async def deployer_init(contract_classes): - _, deployer_cls = contract_classes - starknet = await State.init() - account = await Account.deploy(signer.public_key) - deployer = await starknet.deploy(contract_class=deployer_cls) - return ( - starknet.state, - account, - deployer - ) - - -@pytest.fixture -def deployer_factory(contract_classes, deployer_init): - account_cls, deployer_cls = contract_classes - state, account, deployer = deployer_init - _state = state.copy() - _account = cached_contract(_state, account_cls, account) - deployer = cached_contract(_state, deployer_cls, deployer) - - return _account, deployer - - -@pytest.mark.asyncio -async def test_signer_declare_class(deployer_factory): - account, deployer = deployer_factory - salt = 1234567875432 # random value - unique = 0 - calldata = [] - - # declare contract class - class_hash, _ = await signer.declare_class(account, "Initializable") - - # deploy contract - params = [class_hash, salt, unique, len(calldata), *calldata] - deploy_exec_info = await signer.send_transaction(account, deployer.contract_address, 'deployContract', params) - deployed_address = deploy_exec_info.call_info.retdata[1] - - # test deployment - tx_exec_info = await signer.send_transaction(account, deployed_address, 'initialized', []) - is_initialized = tx_exec_info.call_info.retdata[1] - assert is_initialized == FALSE - - await signer.send_transaction(account, deployed_address, 'initialize', []) - - tx_exec_info = await signer.send_transaction(account, deployed_address, 'initialized', []) - is_initialized = tx_exec_info.call_info.retdata[1] - assert is_initialized == TRUE diff --git a/tests/token/erc1155/ERC1155BaseSuite.py b/tests/token/erc1155/ERC1155BaseSuite.py deleted file mode 100644 index 76e02f650..000000000 --- a/tests/token/erc1155/ERC1155BaseSuite.py +++ /dev/null @@ -1,811 +0,0 @@ -import pytest -from signers import MockSigner -from utils import assert_event_emitted - -from nile.utils import ( - MAX_UINT256, ZERO_ADDRESS, INVALID_UINT256, TRUE, FALSE, - to_uint, add_uint, sub_uint, assert_revert, str_to_felt -) - -signer = MockSigner(123456789987654321) - -# -# Helpers -# - - -def to_uint_array(arr): - return list(map(to_uint, arr)) - - -def prepare_calldata(arr): - """Flatten an array of tuples or an array of ints.""" - # [5, 5, 5] => [3, 5, 5, 5] - # [(1, 2), (3, 4)] => [2, 1, 2, 3, 4] - res = [len(arr)] - if res[0] == 0: - return res - if type(arr[0]) == int: - return res + arr - if type(arr[0]) == tuple: - for elem in arr: - res += [*elem] - return res - raise Exception - -# -# Constants -# - -NOT_BOOLEAN = 3 -ACCOUNT = 123 -TOKEN_ID = to_uint(111) -MINT_VALUE = to_uint(1000) -TRANSFER_VALUE = to_uint(500) - -ACCOUNTS = [123, 234, 345] -TOKEN_IDS = [TOKEN_ID, to_uint(222), to_uint(333)] -MINT_VALUES = [MINT_VALUE, to_uint(2000), to_uint(3000)] -TRANSFER_VALUES = [TRANSFER_VALUE, to_uint(1000), to_uint(1500)] -TRANSFER_DIFFERENCES = [sub_uint(m, t) - for m, t in zip(MINT_VALUES, TRANSFER_VALUES)] -MAX_UINT_VALUES = [to_uint(1), MAX_UINT256, to_uint(1)] -INVALID_VALUES = [to_uint(1), (MAX_UINT256[0]+1, MAX_UINT256[1]), to_uint(1)] -INVALID_IDS = [to_uint(111),INVALID_UINT256,to_uint(333)] - -DEFAULT_URI = str_to_felt('mock://mytoken.v1') -NEW_URI = str_to_felt('mock://mytoken.v2') - -DATA = 0 -REJECT_DATA = [1, 0] - -IERC165_ID = int('0x01ffc9a7', 16) -IERC1155_ID = int('0xd9b67a26', 16) -IERC1155_MetadataURI = int('0x0e89341c', 16) -ERC165_UNSUPPORTED = int('0xffffffff', 16) -UNSUPPORTED_ID = int('0xaabbccdd', 16) - -SUPPORTED_INTERFACES = [IERC165_ID, IERC1155_ID, IERC1155_MetadataURI] -UNSUPPORTED_INTERFACES = [ERC165_UNSUPPORTED, UNSUPPORTED_ID] - - -class ERC1155Base: - # - # Constructor - # - - - @pytest.mark.asyncio - async def test_constructor(self, contract_factory): - erc1155, _, _, _ = contract_factory - - execution_info = await erc1155.uri(TOKEN_ID).execute() - assert execution_info.result.uri == DEFAULT_URI - - # - # ERC165 - # - - - @pytest.mark.asyncio - @pytest.mark.parametrize("supported_id", SUPPORTED_INTERFACES) - async def test_supports_interface(self, contract_factory, supported_id): - erc1155, _, _, _ = contract_factory - - execution_info = await erc1155.supportsInterface(supported_id).execute() - assert execution_info.result.success == TRUE - - - @pytest.mark.asyncio - @pytest.mark.parametrize("unsupported_id", UNSUPPORTED_INTERFACES) - async def test_supports_interface_unsupported(self, contract_factory, unsupported_id): - erc1155, _, _, _ = contract_factory - - execution_info = await erc1155.supportsInterface(unsupported_id).execute() - assert execution_info.result.success == FALSE - - # - # Set/Get approval - # - - - @pytest.mark.asyncio - async def test_set_approval_for_all(self, contract_factory): - erc1155, account, _, _ = contract_factory - - approver = account.contract_address - - # Set approval - await signer.send_transaction( - account, erc1155.contract_address, 'setApprovalForAll', - [ACCOUNT, TRUE] - ) - - execution_info = await erc1155.isApprovedForAll( - approver, ACCOUNT).execute() - - assert execution_info.result.approved == TRUE - - # Unset approval - await signer.send_transaction( - account, erc1155.contract_address, 'setApprovalForAll', - [ACCOUNT, FALSE] - ) - - execution_info = await erc1155.isApprovedForAll( - approver, ACCOUNT).execute() - - assert execution_info.result.approved == FALSE - - - @pytest.mark.asyncio - @pytest.mark.parametrize("approval", [TRUE, FALSE]) - async def test_set_approval_for_all_emits_event(self, contract_factory, approval): - erc1155, account, _, _ = contract_factory - - execution_info = await signer.send_transaction( - account, erc1155.contract_address, 'setApprovalForAll', - [ACCOUNT, approval] - ) - assert_event_emitted( - execution_info, - from_address=erc1155.contract_address, - name='ApprovalForAll', - data=[ - account.contract_address, - ACCOUNT, - approval - ] - ) - - - @pytest.mark.asyncio - async def test_set_approval_for_all_non_boolean(self, contract_factory): - erc1155, account, _, _ = contract_factory - - await assert_revert(signer.send_transaction( - account, erc1155.contract_address, 'setApprovalForAll', - [ACCOUNT, NOT_BOOLEAN]), - "ERC1155: approval is not boolean") - - - @pytest.mark.asyncio - async def test_set_approval_for_all_self(self, contract_factory): - erc1155, account, _, _ = contract_factory - - approvee = account.contract_address - - # Set approval - await assert_revert(signer.send_transaction( - account, erc1155.contract_address, 'setApprovalForAll', - [approvee, TRUE]), - "ERC1155: setting approval status for self") - - - @pytest.mark.asyncio - async def test_set_approval_for_all_zero_address(self, contract_factory): - erc1155, account, _, _ = contract_factory - - # Set approval - await assert_revert(signer.send_transaction( - account, erc1155.contract_address, 'setApprovalForAll', - [ZERO_ADDRESS, TRUE]), - "ERC1155: setting approval status for zero address") - - # - # Balance getters - # - - - @pytest.mark.asyncio - async def test_balance_of(self, minted_factory): - erc1155, _, account, _ = minted_factory - - user = account.contract_address - - execution_info = await erc1155.balanceOf(user, TOKEN_ID).execute() - assert execution_info.result.balance == MINT_VALUE - - - @pytest.mark.asyncio - async def test_balance_of_zero_address(self, contract_factory): - erc1155, _, _, _ = contract_factory - - await assert_revert( - erc1155.balanceOf(ZERO_ADDRESS, TOKEN_ID).execute(), - "ERC1155: address zero is not a valid owner") - - - @pytest.mark.asyncio - async def test_balance_of_invalid_id(self, contract_factory): - erc1155, _, _, _ = contract_factory - - await assert_revert( - erc1155.balanceOf(ACCOUNT, INVALID_UINT256).execute(), - "ERC1155: token_id is not a valid Uint256") - - - @pytest.mark.asyncio - async def test_balance_of_batch(self, minted_factory): - erc1155, _, account, _ = minted_factory - - accounts = [account.contract_address]*3 - - execution_info = await erc1155.balanceOfBatch(accounts, TOKEN_IDS).execute() - assert execution_info.result.balances == MINT_VALUES - - - @pytest.mark.asyncio - async def test_balance_of_batch_zero_address(self, contract_factory): - erc1155, _, _, _ = contract_factory - accounts = [ACCOUNT, ZERO_ADDRESS, ACCOUNT] - - await assert_revert( - erc1155.balanceOfBatch(accounts, TOKEN_IDS).execute(), - "ERC1155: address zero is not a valid owner") - - - @pytest.mark.asyncio - async def test_balance_of_batch_invalid_id(self, contract_factory): - erc1155, _, _, _ = contract_factory - - accounts = [ACCOUNT]*3 - - await assert_revert( - erc1155.balanceOfBatch(accounts, INVALID_IDS).execute(), - "ERC1155: token_id is not a valid Uint256") - - - @pytest.mark.asyncio - @pytest.mark.parametrize( - "accounts,ids", - [(ACCOUNTS[:2], TOKEN_IDS), (ACCOUNTS, TOKEN_IDS[:2])]) - async def test_balance_of_batch_uneven_arrays(self, contract_factory, accounts, ids): - erc1155, _, _, _ = contract_factory - - await assert_revert( - erc1155.balanceOfBatch(accounts, ids).execute(), - "ERC1155: accounts and ids length mismatch") - - # - # Transfer - # - - - @pytest.mark.asyncio - async def test_safe_transfer_from(self, minted_factory): - erc1155, account1, account2, _ = minted_factory - - sender = account2.contract_address - recipient = account1.contract_address - - await signer.send_transaction( - account2, erc1155.contract_address, 'safeTransferFrom', - [sender, recipient, *TOKEN_ID, *TRANSFER_VALUE, DATA]) - - execution_info = await erc1155.balanceOf(sender, TOKEN_ID).execute() - assert execution_info.result.balance == sub_uint( - MINT_VALUE, TRANSFER_VALUE) - - execution_info = await erc1155.balanceOf(recipient, TOKEN_ID).execute() - assert execution_info.result.balance == TRANSFER_VALUE - - - @pytest.mark.asyncio - async def test_safe_transfer_from_emits_event(self, minted_factory): - erc1155, account1, account2, _ = minted_factory - - sender = account2.contract_address - recipient = account1.contract_address - - execution_info = await signer.send_transaction( - account2, erc1155.contract_address, 'safeTransferFrom', - [sender, recipient, *TOKEN_ID, *TRANSFER_VALUE, DATA]) - - assert_event_emitted( - execution_info, - from_address=erc1155.contract_address, - name='TransferSingle', - data=[ - sender, # operator - sender, # from - recipient, # to - *TOKEN_ID, - *TRANSFER_VALUE - ] - ) - - - @pytest.mark.asyncio - async def test_safe_transfer_from_approved(self, minted_factory): - erc1155, account1, account2, _ = minted_factory - - operator = account1.contract_address - sender = account2.contract_address - recipient = account1.contract_address - - # account2 approves account1 - await signer.send_transaction( - account2, erc1155.contract_address, 'setApprovalForAll', - [operator, TRUE]) - - # account sends transaction - await signer.send_transaction( - account1, erc1155.contract_address, 'safeTransferFrom', - [sender, recipient, *TOKEN_ID, *TRANSFER_VALUE, DATA]) - - execution_info = await erc1155.balanceOf(sender, TOKEN_ID).execute() - assert execution_info.result.balance == sub_uint( - MINT_VALUE, TRANSFER_VALUE) - - execution_info = await erc1155.balanceOf(recipient, TOKEN_ID).execute() - assert execution_info.result.balance == TRANSFER_VALUE - - - @pytest.mark.asyncio - async def test_safe_transfer_from_approved_emits_event(self, minted_factory): - erc1155, account1, account2, _ = minted_factory - - operator = account1.contract_address - sender = account2.contract_address - recipient = account1.contract_address - - await signer.send_transaction( - account2, erc1155.contract_address, 'setApprovalForAll', - [operator, TRUE]) - - # account1/operator sends transaction - execution_info = await signer.send_transaction( - account1, erc1155.contract_address, 'safeTransferFrom', - [sender, recipient, *TOKEN_ID, *TRANSFER_VALUE, DATA]) - - assert_event_emitted( - execution_info, - from_address=erc1155.contract_address, - name='TransferSingle', - data=[ - operator, # operator - sender, # from - recipient, # to - *TOKEN_ID, - *TRANSFER_VALUE - ] - ) - - - @pytest.mark.asyncio - async def test_safe_transfer_from_invalid_value(self, contract_factory): - erc1155, owner, account, _ = contract_factory - - sender = account.contract_address - recipient = owner.contract_address - - # mint max uint to avoid possible insufficient balance error - await signer.send_transaction( - owner, erc1155.contract_address, 'mint', - [sender, *TOKEN_ID, *MAX_UINT256, DATA]) - - await assert_revert( - signer.send_transaction( - account, erc1155.contract_address, 'safeTransferFrom', - [sender, recipient, *TOKEN_ID, *INVALID_UINT256, DATA]), - "ERC1155: value is not a valid Uint256" - ) - - - @pytest.mark.asyncio - async def test_safe_transfer_from_invalid_id(self, contract_factory): - erc1155, owner, account, _ = contract_factory - - sender = account.contract_address - recipient = owner.contract_address - - # transfer 0 value of invalid id to avoid - # insufficient balance error - await assert_revert( - signer.send_transaction( - account, erc1155.contract_address, 'safeTransferFrom', - [sender, recipient, *INVALID_UINT256, *to_uint(0), DATA]), - "ERC1155: token_id is not a valid Uint256" - ) - - - @pytest.mark.asyncio - async def test_safe_transfer_from_insufficient_balance(self, minted_factory): - erc1155, account, account2, _ = minted_factory - - sender = account2.contract_address - recipient = account.contract_address - - transfer_value = add_uint(MINT_VALUES[0], to_uint(1)) - - await assert_revert( - signer.send_transaction( - account2, erc1155.contract_address, 'safeTransferFrom', - [sender, recipient, *TOKEN_ID, *transfer_value, DATA]), - "ERC1155: insufficient balance for transfer" - ) - - - @pytest.mark.asyncio - async def test_safe_transfer_from_unapproved(self, minted_factory): - erc1155, account1, account2, _ = minted_factory - - sender = account2.contract_address - recipient = account1.contract_address - - await assert_revert(signer.send_transaction( - account1, erc1155.contract_address, 'safeTransferFrom', - [sender, recipient, *TOKEN_ID, *TRANSFER_VALUE, DATA]), - "ERC1155: caller is not owner nor approved") - - - @pytest.mark.asyncio - async def test_safe_transfer_from_to_zero_address(self, minted_factory): - erc1155, _, account, _ = minted_factory - - sender = account.contract_address - - await assert_revert(signer.send_transaction( - account, erc1155.contract_address, 'safeTransferFrom', - [sender, ZERO_ADDRESS, *TOKEN_ID, *TRANSFER_VALUE, DATA]), - "ERC1155: transfer to the zero address") - - - @pytest.mark.asyncio - async def test_safe_transfer_from_overflow(self, minted_factory): - erc1155, owner, account, _ = minted_factory - - sender = account.contract_address - recipient = owner.contract_address - - # Bring recipient's balance to max possible - await signer.send_transaction( - owner, erc1155.contract_address, 'mint', - [recipient, *TOKEN_ID, *MAX_UINT256, DATA]) - - # Issuing recipient any more should revert due to overflow - await assert_revert(signer.send_transaction( - account, erc1155.contract_address, 'safeTransferFrom', - [sender, recipient, *TOKEN_ID, *to_uint(1), DATA]), - "ERC1155: balance overflow") - - - @pytest.mark.asyncio - async def test_safe_transfer_from_to_receiver(self, minted_factory): - erc1155, _, account2, receiver = minted_factory - - sender = account2.contract_address - recipient = receiver.contract_address - - await signer.send_transaction( - account2, erc1155.contract_address, 'safeTransferFrom', - [sender, recipient, *TOKEN_ID, *TRANSFER_VALUE, DATA]) - - execution_info = await erc1155.balanceOf(recipient, TOKEN_ID).execute() - assert execution_info.result.balance == TRANSFER_VALUE - - - @pytest.mark.asyncio - async def test_safe_transfer_from_to_receiver_rejection(self, minted_factory): - erc1155, _, account2, receiver = minted_factory - - sender = account2.contract_address - recipient = receiver.contract_address - - await assert_revert(signer.send_transaction( - account2, erc1155.contract_address, 'safeTransferFrom', - [sender, recipient, *TOKEN_ID, *TRANSFER_VALUE, *REJECT_DATA]), - "ERC1155: ERC1155Receiver rejected tokens") - - - @pytest.mark.asyncio - async def test_safe_transfer_from_to_non_receiver(self, minted_factory): - erc1155, _, account, _ = minted_factory - - sender = account.contract_address - recipient = erc1155.contract_address - - await assert_revert(signer.send_transaction( - account, erc1155.contract_address, 'safeTransferFrom', - [sender, recipient, *TOKEN_ID, *TRANSFER_VALUE, DATA]), - "ERC1155: transfer to non-ERC1155Receiver implementer") - - - # - # Batch Transfers - # - - - @pytest.mark.asyncio - async def test_safe_batch_transfer_from(self, minted_factory): - erc1155, account1, account2, _ = minted_factory - - sender = account2.contract_address - recipient = account1.contract_address - - await signer.send_transaction( - account2, erc1155.contract_address, 'safeBatchTransferFrom', - [ - sender, recipient, *prepare_calldata(TOKEN_IDS), - *prepare_calldata(TRANSFER_VALUES), DATA - ]) - - execution_info = await erc1155.balanceOfBatch( - [sender]*3+[recipient]*3, TOKEN_IDS*2).execute() - assert execution_info.result.balances[:3] == TRANSFER_DIFFERENCES - assert execution_info.result.balances[3:] == TRANSFER_VALUES - - - @pytest.mark.asyncio - async def test_safe_batch_transfer_from_emits_event(self, minted_factory): - erc1155, account1, account2, _ = minted_factory - - sender = account2.contract_address - recipient = account1.contract_address - - execution_info = await signer.send_transaction( - account2, erc1155.contract_address, 'safeBatchTransferFrom', - [ - sender, recipient, *prepare_calldata(TOKEN_IDS), - *prepare_calldata(TRANSFER_VALUES), DATA - ]) - - assert_event_emitted( - execution_info, - from_address=erc1155.contract_address, - name='TransferBatch', - data=[ - sender, # operator - sender, # from - recipient, # to - *prepare_calldata(TOKEN_IDS), - *prepare_calldata(TRANSFER_VALUES), - ] - ) - - - @pytest.mark.asyncio - async def test_safe_batch_transfer_from_approved(self, minted_factory): - erc1155, account1, account2, _ = minted_factory - - sender = account2.contract_address - operator = account1.contract_address - recipient = account1.contract_address - - await signer.send_transaction( - account2, erc1155.contract_address, 'setApprovalForAll', - [operator, TRUE]) - - await signer.send_transaction( - account1, erc1155.contract_address, 'safeBatchTransferFrom', - [ - sender, recipient, *prepare_calldata(TOKEN_IDS), - *prepare_calldata(TRANSFER_VALUES), DATA - ]) - - execution_info = await erc1155.balanceOfBatch( - [sender]*3+[recipient]*3, TOKEN_IDS*2).execute() - assert execution_info.result.balances[:3] == TRANSFER_DIFFERENCES - assert execution_info.result.balances[3:] == TRANSFER_VALUES - - - @pytest.mark.asyncio - async def test_safe_batch_transfer_from_approved_emits_event(self, - minted_factory): - erc1155, account1, account2, receiver = minted_factory - - sender = account2.contract_address - operator = account1.contract_address - recipient = receiver.contract_address - - await signer.send_transaction( - account2, erc1155.contract_address, 'setApprovalForAll', - [operator, TRUE]) - - execution_info = await signer.send_transaction( - account1, erc1155.contract_address, 'safeBatchTransferFrom', - [ - sender, recipient, *prepare_calldata(TOKEN_IDS), - *prepare_calldata(TRANSFER_VALUES), DATA - ]) - - assert_event_emitted( - execution_info, - from_address=erc1155.contract_address, - name='TransferBatch', - data=[ - operator, # operator - sender, # from - recipient, # to - *prepare_calldata(TOKEN_IDS), - *prepare_calldata(TRANSFER_VALUES), - ] - ) - - - @pytest.mark.asyncio - async def test_safe_batch_transfer_from_invalid_value(self, contract_factory): - erc1155, owner, account, _ = contract_factory - - sender = account.contract_address - recipient = owner.contract_address - - await signer.send_transaction( - owner, erc1155.contract_address, 'mintBatch', - [sender, *prepare_calldata(TOKEN_IDS), *prepare_calldata(MAX_UINT_VALUES), DATA]) - - await assert_revert(signer.send_transaction( - account, erc1155.contract_address, 'safeBatchTransferFrom', - [ - sender, recipient, *prepare_calldata(TOKEN_IDS), - *prepare_calldata(INVALID_VALUES), DATA - ]), - "ERC1155: value is not a valid Uint256") - - - @pytest.mark.asyncio - async def test_safe_batch_transfer_from_invalid_id(self, minted_factory): - erc1155, owner, account, _ = minted_factory - - sender = account.contract_address - recipient = owner.contract_address - transfer_values = [to_uint(0)]*3 - - await assert_revert( - signer.send_transaction( - account, erc1155.contract_address, 'safeBatchTransferFrom', - [ - sender, recipient, *prepare_calldata(INVALID_IDS), - *prepare_calldata(transfer_values), DATA - ]), - "ERC1155: token_id is not a valid Uint256") - - - @pytest.mark.asyncio - async def test_safe_batch_transfer_from_insufficient_balance(self, - minted_factory): - erc1155, account1, account2, _ = minted_factory - - sender = account2.contract_address - recipient = account1.contract_address - - values = values = MINT_VALUES.copy() - values[1] = add_uint(values[1], to_uint(1)) - - await assert_revert(signer.send_transaction( - account2, erc1155.contract_address, 'safeBatchTransferFrom', - [sender, recipient, *prepare_calldata(TOKEN_IDS), *prepare_calldata(values), DATA]), - "ERC1155: insufficient balance for transfer") - - - @pytest.mark.asyncio - async def test_safe_batch_transfer_from_unapproved(self, minted_factory): - erc1155, account1, account2, _ = minted_factory - - sender = account2.contract_address - recipient = account1.contract_address - - await assert_revert(signer.send_transaction( - account1, erc1155.contract_address, 'safeBatchTransferFrom', - [ - sender, recipient, *prepare_calldata(TOKEN_IDS), - *prepare_calldata(TRANSFER_VALUES), DATA - ]), - "ERC1155: caller is not owner nor approved") - - - @pytest.mark.asyncio - async def test_safe_batch_transfer_from_to_zero_address(self, - minted_factory): - erc1155, _, account, _ = minted_factory - - sender = account.contract_address - - await assert_revert(signer.send_transaction( - account, erc1155.contract_address, 'safeBatchTransferFrom', - [ - sender, ZERO_ADDRESS, *prepare_calldata(TOKEN_IDS), - *prepare_calldata(TRANSFER_VALUES), DATA - ]), - "ERC1155: transfer to the zero address") - - - @pytest.mark.asyncio - @pytest.mark.parametrize( - "values,token_ids", - [ - (TRANSFER_VALUES[:2], TOKEN_IDS), - (TRANSFER_VALUES, TOKEN_IDS[:2]) - ] - ) - async def test_safe_batch_transfer_from_uneven_arrays(self, - minted_factory, values, token_ids): - erc1155, account1, account2, _ = minted_factory - - sender = account2.contract_address - recipient = account1.contract_address - - await assert_revert(signer.send_transaction( - account2, erc1155.contract_address, 'safeBatchTransferFrom', - [sender, recipient, *prepare_calldata(token_ids), *prepare_calldata(values), DATA]), - "ERC1155: ids and values length mismatch") - - - @pytest.mark.asyncio - async def test_safe_batch_transfer_from_overflow(self, minted_factory): - erc1155, account1, account2, _ = minted_factory - - sender = account2.contract_address - recipient = account1.contract_address - transfer_values = to_uint_array([0, 1, 0]) - - # Bring 1 recipient's balance to max possible - await signer.send_transaction( - account1, erc1155.contract_address, 'mintBatch', - [recipient, *prepare_calldata(TOKEN_IDS), *prepare_calldata(MAX_UINT_VALUES), DATA] - ) - - # Issuing recipient any more on just 1 token_id - # should revert due to overflow - await assert_revert(signer.send_transaction( - account2, erc1155.contract_address, 'safeBatchTransferFrom', - [ - sender, recipient, *prepare_calldata(TOKEN_IDS), - *prepare_calldata(transfer_values), DATA - ]), - "ERC1155: balance overflow") - - - @pytest.mark.asyncio - async def test_safe_batch_transfer_from_to_receiver(self, minted_factory): - erc1155, _, account2, receiver = minted_factory - - sender = account2.contract_address - recipient = receiver.contract_address - - await signer.send_transaction( - account2, erc1155.contract_address, 'safeBatchTransferFrom', - [ - sender, recipient, *prepare_calldata(TOKEN_IDS), - *prepare_calldata(TRANSFER_VALUES), DATA - ]) - - execution_info = await erc1155.balanceOfBatch( - [sender]*3+[recipient]*3, TOKEN_IDS*2).execute() - assert execution_info.result.balances[:3] == TRANSFER_DIFFERENCES - assert execution_info.result.balances[3:] == TRANSFER_VALUES - - - @pytest.mark.asyncio - async def test_safe_batch_transfer_from_to_receiver_rejection(self, - minted_factory): - erc1155, _, account2, receiver = minted_factory - - sender = account2.contract_address - recipient = receiver.contract_address - - await assert_revert(signer.send_transaction( - account2, erc1155.contract_address, 'safeBatchTransferFrom', - [ - sender, recipient, *prepare_calldata(TOKEN_IDS), - *prepare_calldata(TRANSFER_VALUES), *REJECT_DATA - ]), - "ERC1155: ERC1155Receiver rejected tokens") - - - @pytest.mark.asyncio - async def test_safe_batch_transfer_from_to_non_receiver(self, - minted_factory): - erc1155, _, account, _ = minted_factory - - sender = account.contract_address - recipient = erc1155.contract_address - - await assert_revert(signer.send_transaction( - account, erc1155.contract_address, 'safeBatchTransferFrom', - [ - sender, recipient, *prepare_calldata(TOKEN_IDS), - *prepare_calldata(TRANSFER_VALUES), DATA - ]), - "ERC1155: transfer to non-ERC1155Receiver implementer") diff --git a/tests/token/erc1155/test_ERC1155Internals.py b/tests/token/erc1155/test_ERC1155Internals.py deleted file mode 100644 index a706598cf..000000000 --- a/tests/token/erc1155/test_ERC1155Internals.py +++ /dev/null @@ -1,67 +0,0 @@ -import pytest -from signers import MockSigner -from utils import State, Account, get_contract_class, cached_contract - -from ERC1155BaseSuite import TOKEN_ID, NEW_URI, DEFAULT_URI - -signer = MockSigner(123456789987654321) - - -# -# Fixtures -# - - -@pytest.fixture(scope='module') -def contract_classes(): - account_cls = Account.get_class - erc1155_cls = get_contract_class('ERC1155Mock') - return account_cls, erc1155_cls - - -@pytest.fixture(scope='module') -async def erc1155_init(contract_classes): - _, erc1155_cls = contract_classes - starknet = await State.init() - account1 = await Account.deploy(signer.public_key) - account2 = await Account.deploy(signer.public_key) - erc1155 = await starknet.deploy( - contract_class=erc1155_cls, - constructor_calldata=[DEFAULT_URI, account1.contract_address] - ) - return ( - starknet.state, - account1, - account2, - erc1155, - ) - - -@pytest.fixture -def contract_factory(contract_classes, erc1155_init): - account_cls, erc1155_cls = contract_classes - state, account1, account2, erc1155 = erc1155_init - _state = state.copy() - account1 = cached_contract(_state, account_cls, account1) - account2 = cached_contract(_state, account_cls, account2) - erc1155 = cached_contract(_state, erc1155_cls, erc1155) - return erc1155, account1, account2 - - -class TestERC1155Internals(): - # - # Set URI - # - - - @pytest.mark.asyncio - async def test_set_uri(self, contract_factory): - erc1155, owner, _ = contract_factory - - await signer.send_transaction( - owner, erc1155.contract_address, 'setURI', - [NEW_URI] - ) - - execution_info = await erc1155.uri(TOKEN_ID).execute() - assert execution_info.result.uri == NEW_URI diff --git a/tests/token/erc1155/test_ERC1155MintableBurnable.py b/tests/token/erc1155/test_ERC1155MintableBurnable.py deleted file mode 100644 index 5f548d394..000000000 --- a/tests/token/erc1155/test_ERC1155MintableBurnable.py +++ /dev/null @@ -1,723 +0,0 @@ -import pytest -from signers import MockSigner -from utils import State, Account, get_contract_class, cached_contract, assert_event_emitted - -from access.OwnableBaseSuite import OwnableBase -from ERC1155BaseSuite import ( - ERC1155Base, to_uint_array, prepare_calldata, - TOKEN_ID, MINT_VALUE, - MINT_VALUE,TOKEN_IDS, - MINT_VALUES, MAX_UINT_VALUES, - INVALID_VALUES, INVALID_IDS, - DATA, REJECT_DATA, DEFAULT_URI -) - -from nile.utils import ( - MAX_UINT256, ZERO_ADDRESS, INVALID_UINT256, TRUE, FALSE, - to_uint, add_uint, sub_uint, assert_revert -) - -signer = MockSigner(123456789987654321) - -# -# Constants -# -BURN_value = to_uint(500) -BURN_VALUES = [BURN_value, to_uint(1000), to_uint(1500)] -BURN_DIFFERENCES = [sub_uint(m, b) for m, b in zip(MINT_VALUES, BURN_VALUES)] - -# -# Fixtures -# - - -@pytest.fixture(scope='module') -def contract_classes(): - account_cls = Account.get_class - erc1155_cls = get_contract_class('ERC1155MintableBurnable') - receiver_cls = get_contract_class('ERC1155Holder') - return account_cls, erc1155_cls, receiver_cls - - -@pytest.fixture(scope='module') -async def erc1155_init(contract_classes): - _, erc1155_cls, receiver_cls = contract_classes - starknet = await State.init() - account1 = await Account.deploy(signer.public_key) - account2 = await Account.deploy(signer.public_key) - erc1155 = await starknet.deploy( - contract_class=erc1155_cls, - constructor_calldata=[DEFAULT_URI, account1.contract_address] - ) - receiver = await starknet.deploy( - contract_class=receiver_cls - ) - return ( - starknet.state, - account1, - account2, - erc1155, - receiver - ) - - -@pytest.fixture -def contract_factory(contract_classes, erc1155_init): - account_cls, erc1155_cls, receiver_cls = contract_classes - state, account1, account2, erc1155, receiver = erc1155_init - _state = state.copy() - account1 = cached_contract(_state, account_cls, account1) - account2 = cached_contract(_state, account_cls, account2) - erc1155 = cached_contract(_state, erc1155_cls, erc1155) - receiver = cached_contract(_state, receiver_cls, receiver) - return erc1155, account1, account2, receiver - - -@pytest.fixture -async def minted_factory(contract_classes, erc1155_init): - account_cls, erc1155_cls, receiver_cls = contract_classes - state, owner, account, erc1155, receiver = erc1155_init - _state = state.copy() - owner = cached_contract(_state, account_cls, owner) - account = cached_contract(_state, account_cls, account) - erc1155 = cached_contract(_state, erc1155_cls, erc1155) - receiver = cached_contract(_state, receiver_cls, receiver) - await signer.send_transaction( - owner, erc1155.contract_address, 'mintBatch', - [ - account.contract_address, # to - *prepare_calldata(TOKEN_IDS), # ids - *prepare_calldata(MINT_VALUES), # values - DATA - ] - ) - return erc1155, owner, account, receiver - - - -class TestERC1155MintableBurnable(ERC1155Base, OwnableBase): - # - # Minting - # - - @pytest.mark.asyncio - async def test_mint(self, contract_factory): - erc1155, owner, account, _ = contract_factory - - recipient = account.contract_address - - await signer.send_transaction( - owner, erc1155.contract_address, 'mint', - [recipient, *TOKEN_ID, *MINT_VALUE, DATA]) - - execution_info = await erc1155.balanceOf(recipient, TOKEN_ID).execute() - assert execution_info.result.balance == MINT_VALUE - - - @pytest.mark.asyncio - async def test_mint_emits_event(self, contract_factory): - erc1155, owner, account, _ = contract_factory - - recipient = account.contract_address - - execution_info = await signer.send_transaction( - owner, erc1155.contract_address, 'mint', - [recipient, *TOKEN_ID, *MINT_VALUE, DATA]) - - assert_event_emitted( - execution_info, - from_address=erc1155.contract_address, - name='TransferSingle', - data=[ - owner.contract_address, # operator - ZERO_ADDRESS, # from - recipient, # to - *TOKEN_ID, - *MINT_VALUE - ] - ) - - - @pytest.mark.asyncio - async def test_mint_to_zero_address(self, contract_factory): - erc1155, owner, _, _ = contract_factory - - await assert_revert( - signer.send_transaction( - owner, erc1155.contract_address, 'mint', - [ZERO_ADDRESS, *TOKEN_ID, *MINT_VALUE, DATA]), - "ERC1155: mint to the zero address") - - - @pytest.mark.asyncio - async def test_mint_overflow(self, contract_factory): - erc1155, owner, account, _ = contract_factory - - recipient = account.contract_address - - # Bring recipient's balance to max possible - await signer.send_transaction( - owner, erc1155.contract_address, 'mint', - [recipient, *TOKEN_ID, *MAX_UINT256, DATA]) - - # Issuing recipient any more should revert due to overflow - await assert_revert( - signer.send_transaction( - owner, erc1155.contract_address, 'mint', - [recipient, *TOKEN_ID, *to_uint(1), DATA]), - "ERC1155: balance overflow") - - - @pytest.mark.asyncio - @pytest.mark.parametrize( - "value,token_id,error", - [ - (MINT_VALUE, INVALID_UINT256, - "ERC1155: token_id is not a valid Uint256"), - (INVALID_UINT256, TOKEN_ID, - "ERC1155: value is not a valid Uint256") - ] - ) - async def test_mint_invalid_uint(self, contract_factory, value, token_id, error): - erc1155, owner, account, _ = contract_factory - - recipient = account.contract_address - - await assert_revert( - signer.send_transaction( - owner, erc1155.contract_address, 'mint', - [recipient, *token_id, *value, DATA]), - error) - - - @pytest.mark.asyncio - async def test_mint_receiver(self, contract_factory): - erc1155, owner, _, receiver = contract_factory - - recipient = receiver.contract_address - - await signer.send_transaction( - owner, erc1155.contract_address, 'mint', - [recipient, *TOKEN_ID, *MINT_VALUE, DATA]) - - execution_info = await erc1155.balanceOf( - recipient, TOKEN_ID).execute() - assert execution_info.result.balance == MINT_VALUE - - - @pytest.mark.asyncio - async def test_mint_receiver_rejection(self, contract_factory): - erc1155, owner, _, receiver = contract_factory - - recipient = receiver.contract_address - - await assert_revert( - signer.send_transaction( - owner, erc1155.contract_address, 'mint', - [recipient, *TOKEN_ID, *MINT_VALUE, *REJECT_DATA]), - "ERC1155: ERC1155Receiver rejected tokens") - - - @pytest.mark.asyncio - async def test_mint_to_unsafe_contract(self, contract_factory): - erc1155, owner, _, _ = contract_factory - - recipient = erc1155.contract_address - - await assert_revert( - signer.send_transaction( - owner, erc1155.contract_address, 'mint', - [recipient, *TOKEN_ID, *MINT_VALUE, DATA]), - "ERC1155: transfer to non-ERC1155Receiver implementer") - - # - # Burning - # - - - @pytest.mark.asyncio - async def test_burn(self, minted_factory): - erc1155, _, account, _ = minted_factory - - subject = account.contract_address - - await signer.send_transaction( - account, erc1155.contract_address, 'burn', - [subject, *TOKEN_ID, *BURN_value]) - - execution_info = await erc1155.balanceOf(subject, TOKEN_ID).execute() - assert execution_info.result.balance == sub_uint(MINT_VALUE, BURN_value) - - - @pytest.mark.asyncio - async def test_burn_emits_event(self, minted_factory): - erc1155, _, account, _ = minted_factory - - subject = account.contract_address - - execution_info = await signer.send_transaction( - account, erc1155.contract_address, 'burn', - [subject, *TOKEN_ID, *BURN_value]) - - assert_event_emitted( - execution_info, - from_address=erc1155.contract_address, - name='TransferSingle', - data=[ - subject, # operator - subject, # from - ZERO_ADDRESS, # to - *TOKEN_ID, - *BURN_value - ] - ) - - - @pytest.mark.asyncio - async def test_burn_approved(self, minted_factory): - erc1155, account1, account2, _ = minted_factory - - operator = account1.contract_address - subject = account2.contract_address - - # account2 approves account - await signer.send_transaction( - account2, erc1155.contract_address, 'setApprovalForAll', - [operator, TRUE]) - - await signer.send_transaction( - account1, erc1155.contract_address, 'burn', - [subject, *TOKEN_ID, *BURN_value]) - - execution_info = await erc1155.balanceOf(subject, TOKEN_ID).execute() - assert execution_info.result.balance == sub_uint(MINT_VALUE, BURN_value) - - - @pytest.mark.asyncio - async def test_burn_approved_emits_event(self, minted_factory): - erc1155, account1, account2, _ = minted_factory - - operator = account1.contract_address - subject = account2.contract_address - - # account2 approves account - await signer.send_transaction( - account2, erc1155.contract_address, 'setApprovalForAll', - [operator, TRUE] - ) - - execution_info = await signer.send_transaction( - account1, erc1155.contract_address, 'burn', - [subject, *TOKEN_ID, *BURN_value] - ) - - assert_event_emitted( - execution_info, - from_address=erc1155.contract_address, - name='TransferSingle', - data=[ - operator, # operator - subject, # from - ZERO_ADDRESS, # to - *TOKEN_ID, - *BURN_value - ] - ) - - - @pytest.mark.asyncio - async def test_burn_insufficient_balance(self, minted_factory): - erc1155, _, account, _ = minted_factory - - subject = account.contract_address - burn_value = add_uint(MINT_VALUE, to_uint(1)) - - await assert_revert( - signer.send_transaction( - account, erc1155.contract_address, 'burn', - [subject, *TOKEN_ID, *burn_value]), - "ERC1155: burn value exceeds balance") - - - @pytest.mark.asyncio - async def test_burn_invalid_value(self, contract_factory): - erc1155, owner, account, _ = contract_factory - - burner = account.contract_address - - # mint max possible to avoid insufficient balance - await signer.send_transaction( - owner, erc1155.contract_address, 'mint', - [burner, *TOKEN_ID, *MAX_UINT256, DATA]) - - await assert_revert( - signer.send_transaction( - account, erc1155.contract_address, 'burn', - [burner, *TOKEN_ID, *INVALID_UINT256]), - "ERC1155: value is not a valid Uint256") - - - @pytest.mark.asyncio - async def test_burn_invalid_id(self, minted_factory): - erc1155, _, account, _ = minted_factory - - burner = account.contract_address - - await assert_revert( - signer.send_transaction( - account, erc1155.contract_address, 'burn', - [burner, *INVALID_UINT256, *to_uint(0)]), - "ERC1155: token_id is not a valid Uint256") - - - # - # Batch Minting - # - - - @pytest.mark.asyncio - async def test_mint_batch(self, contract_factory): - erc1155, owner, account, _ = contract_factory - - recipient = account.contract_address - - await signer.send_transaction( - owner, erc1155.contract_address, 'mintBatch', - [recipient, *prepare_calldata(TOKEN_IDS), *prepare_calldata(MINT_VALUES), DATA]) - - execution_info = await erc1155.balanceOfBatch( - [recipient]*3, TOKEN_IDS).execute() - assert execution_info.result.balances == MINT_VALUES - - - @pytest.mark.asyncio - async def test_mint_batch_emits_event(self, contract_factory): - erc1155, owner, account, _ = contract_factory - - recipient = account.contract_address - - execution_info = await signer.send_transaction( - owner, erc1155.contract_address, 'mintBatch', - [recipient, *prepare_calldata(TOKEN_IDS), *prepare_calldata(MINT_VALUES), DATA]) - - assert_event_emitted( - execution_info, - from_address=erc1155.contract_address, - name='TransferBatch', - data=[ - owner.contract_address, # operator - ZERO_ADDRESS, # from - recipient, # to - *prepare_calldata(TOKEN_IDS), - *prepare_calldata(MINT_VALUES), - ] - ) - - - @pytest.mark.asyncio - async def test_mint_batch_to_zero_address(self, contract_factory): - erc1155, owner, _, _ = contract_factory - - await assert_revert( - signer.send_transaction( - owner, erc1155.contract_address, 'mintBatch', - [ZERO_ADDRESS, *prepare_calldata(TOKEN_IDS), *prepare_calldata(MINT_VALUES), DATA]), - "ERC1155: mint to the zero address") - - - @pytest.mark.asyncio - async def test_mint_batch_overflow(self, contract_factory): - erc1155, owner, account, _ = contract_factory - - recipient = account.contract_address - - # Bring recipient's balance to max possible - await signer.send_transaction( - owner, erc1155.contract_address, 'mintBatch', - [recipient, *prepare_calldata(TOKEN_IDS), *prepare_calldata(MAX_UINT_VALUES), DATA]) - - # Issuing recipient any more on just 1 token_id - # should revert due to overflow - values = to_uint_array([0, 1, 0]) - await assert_revert( - signer.send_transaction( - owner, erc1155.contract_address, 'mintBatch', - [recipient, *prepare_calldata(TOKEN_IDS), *prepare_calldata(values), DATA]), - "ERC1155: balance overflow") - - - @pytest.mark.asyncio - @pytest.mark.parametrize( - "values,token_ids,error", - [ - (INVALID_VALUES, TOKEN_IDS, "ERC1155: value is not a valid Uint256"), - (MINT_VALUES, INVALID_IDS, "ERC1155: token_id is not a valid Uint256") - ]) - async def test_mint_batch_invalid_uint(self, contract_factory, values, token_ids, error): - erc1155, owner, account, _ = contract_factory - - recipient = account.contract_address - - await assert_revert( - signer.send_transaction( - owner, erc1155.contract_address, 'mintBatch', - [recipient, *prepare_calldata(token_ids), *prepare_calldata(values), DATA]), - error) - - - @pytest.mark.asyncio - @pytest.mark.parametrize( - "values,token_ids", - [ - (MINT_VALUES[:2], TOKEN_IDS), - (MINT_VALUES, TOKEN_IDS[:2]) - ]) - async def test_mint_batch_uneven_arrays(self, contract_factory, values, token_ids): - erc1155, owner, account, _ = contract_factory - - recipient = account.contract_address - - await assert_revert( - signer.send_transaction( - owner, erc1155.contract_address, 'mintBatch', - [recipient, *prepare_calldata(token_ids), *prepare_calldata(values), DATA]), - "ERC1155: ids and values length mismatch") - - - @pytest.mark.asyncio - async def test_mint_batch_to_receiver(self, contract_factory): - erc1155, owner, _, receiver = contract_factory - - recipient = receiver.contract_address - - await signer.send_transaction( - owner, erc1155.contract_address, 'mintBatch', - [ - recipient, *prepare_calldata(TOKEN_IDS), - *prepare_calldata(MINT_VALUES), DATA - ]) - - execution_info = await erc1155.balanceOfBatch( - [recipient]*3, TOKEN_IDS).execute() - assert execution_info.result.balances == MINT_VALUES - - - @pytest.mark.asyncio - async def test_mint_batch_to_receiver_rejection(self, contract_factory): - erc1155, owner, _, receiver = contract_factory - - recipient = receiver.contract_address - - await assert_revert(signer.send_transaction( - owner, erc1155.contract_address, 'mintBatch', - [ - recipient, *prepare_calldata(TOKEN_IDS), - *prepare_calldata(MINT_VALUES), *REJECT_DATA - ]), - "ERC1155: ERC1155Receiver rejected tokens") - - - @pytest.mark.asyncio - async def test_mint_batch_to_non_receiver(self, contract_factory): - erc1155, owner, _, _ = contract_factory - - recipient = erc1155.contract_address - - await assert_revert(signer.send_transaction( - owner, erc1155.contract_address, 'mintBatch', - [ - recipient, *prepare_calldata(TOKEN_IDS), - *prepare_calldata(MINT_VALUES), DATA - ]), - "ERC1155: transfer to non-ERC1155Receiver implementer") - - # - # Batch Burning - # - - - @pytest.mark.asyncio - async def test_burn_batch(self, minted_factory): - erc1155, _, account, _ = minted_factory - - burner = account.contract_address - - await signer.send_transaction( - account, erc1155.contract_address, 'burnBatch', - [burner, *prepare_calldata(TOKEN_IDS), *prepare_calldata(BURN_VALUES)]) - - execution_info = await erc1155.balanceOfBatch( - [burner]*3, TOKEN_IDS).execute() - assert execution_info.result.balances == BURN_DIFFERENCES - - - @pytest.mark.asyncio - async def test_burn_batch_emits_event(self, minted_factory): - erc1155, _, account, _ = minted_factory - - burner = account.contract_address - - execution_info = await signer.send_transaction( - account, erc1155.contract_address, 'burnBatch', - [burner, *prepare_calldata(TOKEN_IDS), *prepare_calldata(BURN_VALUES)]) - - assert_event_emitted( - execution_info, - from_address=erc1155.contract_address, - name='TransferBatch', - data=[ - burner, # operator - burner, # from - ZERO_ADDRESS, # to - *prepare_calldata(TOKEN_IDS), - *prepare_calldata(BURN_VALUES), - ] - ) - - - @pytest.mark.asyncio - async def test_burn_batch_from_approved(self, minted_factory): - erc1155, account1, account2, _ = minted_factory - - burner = account2.contract_address - operator = account1.contract_address - - await signer.send_transaction( - account2, erc1155.contract_address, 'setApprovalForAll', - [operator, TRUE]) - - await signer.send_transaction( - account1, erc1155.contract_address, 'burnBatch', - [burner, *prepare_calldata(TOKEN_IDS), *prepare_calldata(BURN_VALUES)]) - - execution_info = await erc1155.balanceOfBatch( - [burner]*3, TOKEN_IDS).execute() - assert execution_info.result.balances == BURN_DIFFERENCES - - - @pytest.mark.asyncio - async def test_burn_batch_from_approved_emits_event(self, minted_factory): - erc1155, account1, account2, _ = minted_factory - - burner = account2.contract_address - operator = account1.contract_address - - await signer.send_transaction( - account2, erc1155.contract_address, 'setApprovalForAll', - [operator, TRUE]) - - execution_info = await signer.send_transaction( - account1, erc1155.contract_address, 'burnBatch', - [burner, *prepare_calldata(TOKEN_IDS), *prepare_calldata(BURN_VALUES)]) - - assert_event_emitted( - execution_info, - from_address=erc1155.contract_address, - name='TransferBatch', - data=[ - operator, # operator - burner, # from - ZERO_ADDRESS, # to - *prepare_calldata(TOKEN_IDS), - *prepare_calldata(BURN_VALUES), - ] - ) - - - @pytest.mark.asyncio - async def test_burn_batch_from_unapproved(self, minted_factory): - erc1155, account1, account2, _ = minted_factory - - burner = account2.contract_address - operator = account1.contract_address - - await signer.send_transaction( - account2, erc1155.contract_address, 'setApprovalForAll', - [operator, FALSE]) - - await assert_revert(signer.send_transaction( - account1, erc1155.contract_address, 'burnBatch', - [burner, *prepare_calldata(TOKEN_IDS), *prepare_calldata(BURN_VALUES)]), - "ERC1155: caller is not owner nor approved") - - - @pytest.mark.asyncio - async def test_burn_batch_from_zero_address(self, minted_factory): - erc1155, _, _, _ = minted_factory - - values = [to_uint(0)]*3 - - # Attempt to burn nothing (since cannot mint non_zero balance to burn) - # note invoking this way (without signer) gives caller address of 0 - await assert_revert( - erc1155.burnBatch(ZERO_ADDRESS, TOKEN_IDS, values).execute(), - "ERC1155: burn from the zero address") - - - @pytest.mark.asyncio - async def test_burn_batch_insufficent_balance(self, minted_factory): - erc1155, _, account, _ = minted_factory - - burner = account.contract_address - values = MINT_VALUES.copy() - values[1] = add_uint(values[1], to_uint(1)) - - await assert_revert( - signer.send_transaction( - account, erc1155.contract_address, 'burnBatch', - [burner, *prepare_calldata(TOKEN_IDS), *prepare_calldata(values)]), - "ERC1155: burn value exceeds balance") - - - @pytest.mark.asyncio - async def test_burn_batch_invalid_value(self, contract_factory): - erc1155, owner, account, _ = contract_factory - - burner = account.contract_address - - # mint max possible to avoid insufficient balance - await signer.send_transaction( - owner, erc1155.contract_address, 'mintBatch', - [burner, *prepare_calldata(TOKEN_IDS), *prepare_calldata(MAX_UINT_VALUES), 0]) - - # attempt passing an invalid uint in batch - await assert_revert( - signer.send_transaction( - account, erc1155.contract_address, 'burnBatch', - [burner, *prepare_calldata(TOKEN_IDS), *prepare_calldata(INVALID_VALUES)]), - "ERC1155: value is not a valid Uint256") - - - @pytest.mark.asyncio - async def test_burn_batch_invalid_id(self, minted_factory): - erc1155, _, account, _ = minted_factory - - burner = account.contract_address - burn_values = [to_uint(0)]*3 - - await assert_revert( - signer.send_transaction( - account, erc1155.contract_address, 'burnBatch', - [burner, *prepare_calldata(INVALID_IDS), *prepare_calldata(burn_values)]), - "ERC1155: token_id is not a valid Uint256") - - - @pytest.mark.asyncio - @pytest.mark.parametrize( - "values,token_ids", - [ - (BURN_VALUES[:2], TOKEN_IDS), - (BURN_VALUES, TOKEN_IDS[:2]) - ] - ) - async def test_burn_batch_uneven_arrays(self, - minted_factory, values, token_ids): - erc1155, _, account, _ = minted_factory - - burner = account.contract_address - - await assert_revert( - signer.send_transaction( - account, erc1155.contract_address, 'burnBatch', - [burner, *prepare_calldata(token_ids), *prepare_calldata(values)]), - "ERC1155: ids and values length mismatch") diff --git a/tests/token/erc20/ERC20BaseSuite.py b/tests/token/erc20/ERC20BaseSuite.py deleted file mode 100644 index 69d4c6386..000000000 --- a/tests/token/erc20/ERC20BaseSuite.py +++ /dev/null @@ -1,714 +0,0 @@ -import pytest - -from signers import MockSigner -from nile.utils import ( - to_uint, add_uint, sub_uint, str_to_felt, MAX_UINT256, ZERO_ADDRESS, - INVALID_UINT256, TRUE, assert_revert -) -from utils import ( - assert_event_emitted, assert_events_emitted, contract_path, State, get_cairo_path -) - - -signer = MockSigner(123456789987654321) - -# testing vars -NAME = str_to_felt("Token") -SYMBOL = str_to_felt("TKN") -DECIMALS = 18 -RECIPIENT = 123 -INIT_SUPPLY = to_uint(1000) -AMOUNT = to_uint(200) -UINT_ONE = to_uint(1) -UINT_ZERO = to_uint(0) - - - -class ERC20Base: - # - # Constructor - # - - @pytest.mark.asyncio - async def test_constructor(self, contract_factory): - erc20, account, _ = contract_factory - - # balanceOf recipient - execution_info = await erc20.balanceOf(account.contract_address).execute() - assert execution_info.result.balance == INIT_SUPPLY - - # totalSupply - execution_info = await erc20.totalSupply().execute() - assert execution_info.result.totalSupply == INIT_SUPPLY - - - @pytest.mark.asyncio - async def test_constructor_exceed_max_decimals(self, contract_factory): - _, account, _ = contract_factory - - bad_decimals = 2**8 + 1 - - starknet = await State.init() - await assert_revert( - starknet.deploy( - contract_path("openzeppelin/token/erc20/presets/ERC20.cairo"), - constructor_calldata=[ - NAME, - SYMBOL, - bad_decimals, - *INIT_SUPPLY, - account.contract_address - ], - cairo_path=get_cairo_path() - ), - reverted_with="ERC20: decimals exceed 2^8" - ) - - - @pytest.mark.asyncio - async def test_name(self, contract_factory): - erc20, _, _ = contract_factory - execution_info = await erc20.name().execute() - assert execution_info.result.name == NAME - - - @pytest.mark.asyncio - async def test_symbol(self, contract_factory): - erc20, _, _ = contract_factory - execution_info = await erc20.symbol().execute() - assert execution_info.result.symbol == SYMBOL - - - @pytest.mark.asyncio - async def test_decimals(self, contract_factory): - erc20, _, _ = contract_factory - execution_info = await erc20.decimals().execute() - assert execution_info.result.decimals == DECIMALS - - - # - # approve - # - - - @pytest.mark.asyncio - async def test_approve(self, contract_factory): - erc20, account, spender = contract_factory - - # check spender's allowance starts at zero - - execution_info = await erc20.allowance(account.contract_address, spender.contract_address).execute() - assert execution_info.result.remaining == UINT_ZERO - - # set approval - return_bool = await signer.send_transaction( - account, erc20.contract_address, 'approve', [ - spender.contract_address, - *AMOUNT - ] - ) - assert return_bool.call_info.retdata[1] == TRUE - - # check spender's allowance - execution_info = await erc20.allowance(account.contract_address, spender.contract_address).execute() - assert execution_info.result.remaining == AMOUNT - - - - @pytest.mark.asyncio - async def test_approve_emits_event(self, contract_factory): - erc20, account, spender = contract_factory - - tx_exec_info = await signer.send_transaction( - account, erc20.contract_address, 'approve', [ - spender.contract_address, - *AMOUNT - ]) - - assert_event_emitted( - tx_exec_info, - from_address=erc20.contract_address, - name='Approval', - data=[ - account.contract_address, - spender.contract_address, - *AMOUNT - ] - ) - - - @pytest.mark.asyncio - async def test_approve_from_zero_address(self, contract_factory): - erc20, _, spender = contract_factory - - # Without using an account abstraction, the caller address - # (get_caller_address) is zero - await assert_revert( - erc20.approve(spender.contract_address, AMOUNT).execute(), - reverted_with="ERC20: cannot approve from the zero address" - ) - - - @pytest.mark.asyncio - async def test_approve_to_zero_address(self, contract_factory): - erc20, account, _ = contract_factory - - await assert_revert(signer.send_transaction( - account, erc20.contract_address, 'approve', [ - ZERO_ADDRESS, - *UINT_ONE - ]), - reverted_with="ERC20: cannot approve to the zero address" - ) - - - @pytest.mark.asyncio - async def test_approve_invalid_uint256(self, contract_factory): - erc20, account, spender = contract_factory - - await assert_revert( - signer.send_transaction( - account, erc20.contract_address, 'approve', [ - spender.contract_address, - *INVALID_UINT256 - ]), - reverted_with="ERC20: amount is not a valid Uint256" - ) - - - # - # transfer - # - - - @pytest.mark.asyncio - async def test_transfer(self, contract_factory): - erc20, account, _ = contract_factory - - # check original totalSupply - execution_info = await erc20.balanceOf(account.contract_address).execute() - assert execution_info.result.balance == INIT_SUPPLY - - # check recipient original balance - execution_info = await erc20.balanceOf(RECIPIENT).execute() - assert execution_info.result.balance == UINT_ZERO - - # transfer - return_bool = await signer.send_transaction( - account, erc20.contract_address, 'transfer', [ - RECIPIENT, - *AMOUNT - ] - ) - assert return_bool.call_info.retdata[1] == TRUE - - # check account balance - execution_info = await erc20.balanceOf(account.contract_address).execute() - assert execution_info.result.balance == sub_uint(INIT_SUPPLY, AMOUNT) - - # check recipient balance - execution_info = await erc20.balanceOf(RECIPIENT).execute() - assert execution_info.result.balance == AMOUNT - - # check totalSupply - execution_info = await erc20.totalSupply().execute() - assert execution_info.result.totalSupply == INIT_SUPPLY - - - @pytest.mark.asyncio - async def test_transfer_emits_event(self, contract_factory): - erc20, account, _ = contract_factory - - tx_exec_info = await signer.send_transaction( - account, erc20.contract_address, 'transfer', [ - RECIPIENT, - *AMOUNT - ]) - - assert_event_emitted( - tx_exec_info, - from_address=erc20.contract_address, - name='Transfer', - data=[ - account.contract_address, - RECIPIENT, - *AMOUNT - ] - ) - - - @pytest.mark.asyncio - async def test_transfer_not_enough_balance(self, contract_factory): - erc20, account, _ = contract_factory - - await assert_revert(signer.send_transaction( - account, erc20.contract_address, 'transfer', [ - RECIPIENT, - *add_uint(INIT_SUPPLY, UINT_ONE) - ]), - reverted_with="ERC20: transfer amount exceeds balance" - ) - - - @pytest.mark.asyncio - async def test_transfer_to_zero_address(self, contract_factory): - erc20, account, _ = contract_factory - - await assert_revert(signer.send_transaction( - account, erc20.contract_address, 'transfer', [ - ZERO_ADDRESS, - *UINT_ONE - ]), - reverted_with="ERC20: cannot transfer to the zero address" - ) - - - @pytest.mark.asyncio - async def test_transfer_from_zero_address(self, contract_factory): - erc20, _, _ = contract_factory - - # Without using an account abstraction, the caller address - # (get_caller_address) is zero - await assert_revert( - erc20.transfer(RECIPIENT, UINT_ONE).execute(), - reverted_with="ERC20: cannot transfer from the zero address" - ) - - - @pytest.mark.asyncio - async def test_transfer_invalid_uint256(self, contract_factory): - erc20, account, _ = contract_factory - - await assert_revert(signer.send_transaction( - account, erc20.contract_address, 'transfer', [ - RECIPIENT, - *INVALID_UINT256 - ]), - reverted_with="ERC20: amount is not a valid Uint256" - ) - - - # - # transferFrom - # - - - @pytest.mark.asyncio - async def test_transferFrom(self, contract_factory): - erc20, account, spender = contract_factory - - # approve - await signer.send_transaction( - account, erc20.contract_address, 'approve', [ - spender.contract_address, - *AMOUNT - ] - ) - # transferFrom - return_bool = await signer.send_transaction( - spender, erc20.contract_address, 'transferFrom', [ - account.contract_address, - RECIPIENT, - *AMOUNT - ] - ) - assert return_bool.call_info.retdata[1] == TRUE - - # check account balance - execution_info = await erc20.balanceOf(account.contract_address).execute() - assert execution_info.result.balance == sub_uint(INIT_SUPPLY, AMOUNT) - - # check recipient balance - execution_info = await erc20.balanceOf(RECIPIENT).execute() - assert execution_info.result.balance == AMOUNT - - # check spender allowance after tx - execution_info = await erc20.allowance(account.contract_address, spender.contract_address).execute() - assert execution_info.result.remaining == UINT_ZERO - - - @pytest.mark.asyncio - async def test_transferFrom_emits_event(self, contract_factory): - erc20, account, spender = contract_factory - - # approve - await signer.send_transaction( - account, erc20.contract_address, 'approve', [ - spender.contract_address, - *AMOUNT - ]) - - # transferFrom - tx_exec_info = await signer.send_transaction( - spender, erc20.contract_address, 'transferFrom', [ - account.contract_address, - RECIPIENT, - *AMOUNT - ]) - - # check events - assert_events_emitted( - tx_exec_info, - [ - [0, erc20.contract_address, 'Approval', [ - account.contract_address, spender.contract_address, *UINT_ZERO]], - [1, erc20.contract_address, 'Transfer', [ - account.contract_address, RECIPIENT, *AMOUNT]] - ] - ) - - - async def test_transferFrom_doesnt_consume_infinite_allowance(self, contract_factory): - erc20, account, spender = contract_factory - - # approve - await signer.send_transaction(account, erc20.contract_address, 'approve', [spender.contract_address, *MAX_UINT256]) - - # check approval - execution_info_1 = await erc20.allowance(account.contract_address, spender.contract_address).call() - assert execution_info_1.result.remaining == MAX_UINT256 - - # transferFrom - await signer.send_transaction( - spender, erc20.contract_address, 'transferFrom', [ - account.contract_address, - RECIPIENT, - *AMOUNT - ]) - - # re-check approval - execution_info_2 = await erc20.allowance(account.contract_address, spender.contract_address).call() - assert execution_info_2.result.remaining == MAX_UINT256 - - - @pytest.mark.asyncio - async def test_transferFrom_greater_than_allowance(self, contract_factory): - erc20, account, spender = contract_factory - # we use the same signer to control the main and the spender accounts - # this is ok since they're still two different accounts - - await signer.send_transaction( - account, erc20.contract_address, 'approve', [ - spender.contract_address, - *AMOUNT - ] - ) - - fail_amount = add_uint(AMOUNT, UINT_ONE) - - # increasing the transfer amount above allowance - await assert_revert(signer.send_transaction( - spender, erc20.contract_address, 'transferFrom', [ - account.contract_address, - RECIPIENT, - *fail_amount - ]), - reverted_with="ERC20: insufficient allowance" - ) - - - @pytest.mark.asyncio - async def test_transferFrom_from_zero_address(self, contract_factory): - erc20, _, spender = contract_factory - - await assert_revert(signer.send_transaction( - spender, erc20.contract_address, 'transferFrom', [ - ZERO_ADDRESS, - RECIPIENT, - *AMOUNT - ]), - reverted_with="ERC20: insufficient allowance" - ) - - - @pytest.mark.asyncio - async def test_transferFrom_to_zero_address(self, contract_factory): - erc20, account, spender = contract_factory - - await signer.send_transaction( - account, erc20.contract_address, 'approve', [ - spender.contract_address, - *UINT_ONE - ] - ) - - await assert_revert(signer.send_transaction( - spender, erc20.contract_address, 'transferFrom', [ - account.contract_address, - ZERO_ADDRESS, - *UINT_ONE - ]), - reverted_with="ERC20: cannot transfer to the zero address" - ) - - - # - # increaseAllowance - # - - - @pytest.mark.asyncio - async def test_increaseAllowance(self, contract_factory): - erc20, account, spender = contract_factory - - execution_info = await erc20.allowance(account.contract_address, spender.contract_address).execute() - assert execution_info.result.remaining == UINT_ZERO - - # set approve - await signer.send_transaction( - account, erc20.contract_address, 'approve', [ - spender.contract_address, - *AMOUNT - ] - ) - - # check allowance - execution_info = await erc20.allowance(account.contract_address, spender.contract_address).execute() - assert execution_info.result.remaining == AMOUNT - - # increase allowance - return_bool = await signer.send_transaction( - account, erc20.contract_address, 'increaseAllowance', [ - spender.contract_address, - *AMOUNT - ] - ) - assert return_bool.call_info.retdata[1] == TRUE - - # check spender's allowance increased - execution_info = await erc20.allowance(account.contract_address, spender.contract_address).execute() - assert execution_info.result.remaining == add_uint(AMOUNT, AMOUNT) - - - @pytest.mark.asyncio - async def test_increaseAllowance_emits_event(self, contract_factory): - erc20, account, spender = contract_factory - - # set approve - await signer.send_transaction( - account, erc20.contract_address, 'approve', [ - spender.contract_address, - *AMOUNT - ]) - - # increase allowance - tx_exec_info = await signer.send_transaction( - account, erc20.contract_address, 'increaseAllowance', [ - spender.contract_address, - *AMOUNT - ]) - - new_allowance = add_uint(AMOUNT, AMOUNT) - - assert_event_emitted( - tx_exec_info, - from_address=erc20.contract_address, - name='Approval', - data=[ - account.contract_address, - spender.contract_address, - *new_allowance - ] - ) - - - @pytest.mark.asyncio - async def test_increaseAllowance_overflow(self, contract_factory): - erc20, account, spender = contract_factory - - # approve max - await signer.send_transaction( - account, erc20.contract_address, 'approve', [ - spender.contract_address, - *MAX_UINT256 - ] - ) - - # overflow_amount adds (1, 0) to (2**128 - 1, 2**128 - 1) - await assert_revert(signer.send_transaction( - account, erc20.contract_address, 'increaseAllowance', [ - spender.contract_address, - *UINT_ONE - ]), - reverted_with="ERC20: allowance overflow" - ) - - - @pytest.mark.asyncio - async def test_increaseAllowance_to_zero_address(self, contract_factory): - erc20, account, spender = contract_factory - - await signer.send_transaction( - account, erc20.contract_address, 'approve', [ - spender.contract_address, - *AMOUNT - ] - ) - - await assert_revert(signer.send_transaction( - account, erc20.contract_address, 'increaseAllowance', [ - ZERO_ADDRESS, - *AMOUNT - ]) - ) - - - @pytest.mark.asyncio - async def test_increaseAllowance_from_zero_address(self, contract_factory): - erc20, account, spender = contract_factory - - await signer.send_transaction( - account, erc20.contract_address, 'approve', [ - spender.contract_address, - *AMOUNT - ] - ) - - await assert_revert( - erc20.increaseAllowance(RECIPIENT, AMOUNT).execute() - ) - - - # - # decreaseAllowance - # - - - @pytest.mark.asyncio - async def test_decreaseAllowance(self, contract_factory): - erc20, account, spender = contract_factory - - execution_info = await erc20.allowance(account.contract_address, spender.contract_address).execute() - assert execution_info.result.remaining == UINT_ZERO - - # set approve - await signer.send_transaction( - account, erc20.contract_address, 'approve', [ - spender.contract_address, - *AMOUNT - ] - ) - - execution_info = await erc20.allowance(account.contract_address, spender.contract_address).execute() - assert execution_info.result.remaining == AMOUNT - - # decrease allowance - return_bool = await signer.send_transaction( - account, erc20.contract_address, 'decreaseAllowance', [ - spender.contract_address, - *UINT_ONE - ] - ) - assert return_bool.call_info.retdata[1] == TRUE - - new_allowance = sub_uint(AMOUNT, UINT_ONE) - - execution_info = await erc20.allowance(account.contract_address, spender.contract_address).execute() - assert execution_info.result.remaining == new_allowance - - - @pytest.mark.asyncio - async def test_decreaseAllowance_emits_event(self, contract_factory): - erc20, account, spender = contract_factory - - # set approve - await signer.send_transaction( - account, erc20.contract_address, 'approve', [ - spender.contract_address, - *INIT_SUPPLY - ]) - - # decrease allowance - tx_exec_info = await signer.send_transaction( - account, erc20.contract_address, 'decreaseAllowance', [ - spender.contract_address, - *AMOUNT - ]) - - new_allowance = sub_uint(INIT_SUPPLY, AMOUNT) - - assert_event_emitted( - tx_exec_info, - from_address=erc20.contract_address, - name='Approval', - data=[ - account.contract_address, - spender.contract_address, - *new_allowance - ] - ) - - - @pytest.mark.asyncio - async def test_decreaseAllowance_overflow(self, contract_factory): - erc20, account, spender = contract_factory - - await signer.send_transaction( - account, erc20.contract_address, 'approve', [ - spender.contract_address, - *AMOUNT - ] - ) - - execution_info = await erc20.allowance(account.contract_address, spender.contract_address).execute() - assert execution_info.result.remaining == AMOUNT - - allowance_plus_one = add_uint(AMOUNT, UINT_ONE) - - # increasing the decreased allowance amount by more than the spender's allowance - await assert_revert(signer.send_transaction( - account, erc20.contract_address, 'decreaseAllowance', [ - spender.contract_address, - *allowance_plus_one - ]), - reverted_with="ERC20: allowance below zero" - ) - - - @pytest.mark.asyncio - async def test_decreaseAllowance_to_zero_address(self, contract_factory): - erc20, account, spender = contract_factory - - await signer.send_transaction( - account, erc20.contract_address, 'approve', [ - spender.contract_address, - *AMOUNT - ] - ) - - await assert_revert(signer.send_transaction( - account, erc20.contract_address, 'decreaseAllowance', [ - ZERO_ADDRESS, - *AMOUNT - ]) - ) - - - @pytest.mark.asyncio - async def test_decreaseAllowance_from_zero_address(self, contract_factory): - erc20, account, spender = contract_factory - - await signer.send_transaction( - account, erc20.contract_address, 'approve', [ - spender.contract_address, - *AMOUNT - ] - ) - - await assert_revert( - erc20.decreaseAllowance(RECIPIENT, AMOUNT).execute() - ) - - - @pytest.mark.asyncio - async def test_decreaseAllowance_invalid_uint256(self, contract_factory): - erc20, account, spender = contract_factory - - await assert_revert( - signer.send_transaction( - account, erc20.contract_address, 'decreaseAllowance', [ - spender.contract_address, - *INVALID_UINT256 - ]), - reverted_with="ERC20: subtracted_value is not a valid Uint256" - ) diff --git a/tests/token/erc20/test_ERC20.py b/tests/token/erc20/test_ERC20.py deleted file mode 100644 index a196a7f31..000000000 --- a/tests/token/erc20/test_ERC20.py +++ /dev/null @@ -1,54 +0,0 @@ -import pytest -from signers import MockSigner -from utils import get_contract_class, cached_contract, State, Account -from ERC20BaseSuite import ERC20Base, NAME, SYMBOL, DECIMALS, INIT_SUPPLY - - -signer = MockSigner(123456789987654321) - - -@pytest.fixture(scope='module') -def contract_classes(): - account_cls = Account.get_class - erc20_cls = get_contract_class('ERC20') - - return account_cls, erc20_cls - - -@pytest.fixture(scope='module') -async def erc20_init(contract_classes): - account_cls, erc20_cls = contract_classes - starknet = await State.init() - account1 = await Account.deploy(signer.public_key) - account2 = await Account.deploy(signer.public_key) - erc20 = await starknet.deploy( - contract_class=erc20_cls, - constructor_calldata=[ - NAME, - SYMBOL, - DECIMALS, - *INIT_SUPPLY, - account1.contract_address, # recipient - ] - ) - return ( - starknet.state, - account1, - account2, - erc20 - ) - - -@pytest.fixture -def contract_factory(contract_classes, erc20_init): - account_cls, erc20_cls = contract_classes - state, account1, account2, erc20 = erc20_init - _state = state.copy() - account1 = cached_contract(_state, account_cls, account1) - account2 = cached_contract(_state, account_cls, account2) - erc20 = cached_contract(_state, erc20_cls, erc20) - return erc20, account1, account2 - - -class TestERC20(ERC20Base): - pass diff --git a/tests/token/erc20/test_ERC20Burnable.py b/tests/token/erc20/test_ERC20Burnable.py deleted file mode 100644 index a15fba3a6..000000000 --- a/tests/token/erc20/test_ERC20Burnable.py +++ /dev/null @@ -1,216 +0,0 @@ -import pytest -from signers import MockSigner -from nile.utils import ( - add_uint, sub_uint, ZERO_ADDRESS, INVALID_UINT256, assert_revert, -) -from utils import ( - get_contract_class, cached_contract, assert_events_emitted, - assert_event_emitted, State, Account -) -from ERC20BaseSuite import ( - ERC20Base, NAME, SYMBOL, DECIMALS, INIT_SUPPLY, AMOUNT, UINT_ONE, UINT_ZERO -) - - -signer = MockSigner(123456789987654321) - - -@pytest.fixture(scope='module') -def contract_classes(): - account_cls = Account.get_class - erc20_cls = get_contract_class('ERC20Burnable') - - return account_cls, erc20_cls - - -@pytest.fixture(scope='module') -async def erc20_init(contract_classes): - _, erc20_cls = contract_classes - starknet = await State.init() - account1 = await Account.deploy(signer.public_key) - account2 = await Account.deploy(signer.public_key) - erc20 = await starknet.deploy( - contract_class=erc20_cls, - constructor_calldata=[ - NAME, - SYMBOL, - DECIMALS, - *INIT_SUPPLY, - account1.contract_address, # recipient - ] - ) - return ( - starknet.state, - account1, - account2, - erc20 - ) - - -@pytest.fixture -def contract_factory(contract_classes, erc20_init): - account_cls, erc20_cls = contract_classes - state, account1, account2, erc20 = erc20_init - _state = state.copy() - account1 = cached_contract(_state, account_cls, account1) - account2 = cached_contract(_state, account_cls, account2) - erc20 = cached_contract(_state, erc20_cls, erc20) - - return erc20, account1, account2 - - -class TestERC20Burnable(ERC20Base): - # - # burn - # - - @pytest.mark.asyncio - async def test_burn(self, contract_factory): - erc20, account, _ = contract_factory - - await signer.send_transaction( - account, erc20.contract_address, 'burn', [ - *AMOUNT - ]) - - new_balance = sub_uint(INIT_SUPPLY, AMOUNT) - - execution_info = await erc20.balanceOf(account.contract_address).execute() - assert execution_info.result.balance == new_balance - - - @pytest.mark.asyncio - async def test_burn_emits_event(self, contract_factory): - erc20, account, _ = contract_factory - - tx_exec_info = await signer.send_transaction( - account, erc20.contract_address, 'burn', [ - *AMOUNT - ]) - - assert_event_emitted( - tx_exec_info, - from_address=erc20.contract_address, - name='Transfer', - data=[ - account.contract_address, - ZERO_ADDRESS, - *AMOUNT - ] - ) - - - @pytest.mark.asyncio - async def test_burn_not_enough_balance(self, contract_factory): - erc20, account, _ = contract_factory - - balance_plus_one = add_uint(INIT_SUPPLY, UINT_ONE) - - await assert_revert(signer.send_transaction( - account, erc20.contract_address, 'burn', [ - *balance_plus_one - ]), - reverted_with="ERC20: burn amount exceeds balance" - ) - - - @pytest.mark.asyncio - async def test_burn_from_zero_address(self, contract_factory): - erc20, _, _ = contract_factory - - await assert_revert( - erc20.burn(UINT_ONE).execute(), - reverted_with="ERC20: cannot burn from the zero address" - ) - - - @pytest.mark.asyncio - async def test_burn_invalid_uint256(self, contract_factory): - erc20, _, _ = contract_factory - - await assert_revert( - erc20.burn(INVALID_UINT256).execute(), - reverted_with="ERC20: amount is not a valid Uint256" - ) - - - @pytest.mark.asyncio - async def test_burn_from(self, contract_factory): - erc20, account1, account2 = contract_factory - - await signer.send_transaction( - account1, erc20.contract_address, 'increaseAllowance', [ - account2.contract_address, - *AMOUNT - ]) - - await signer.send_transaction( - account2, erc20.contract_address, 'burnFrom', [ - account1.contract_address, - *AMOUNT - ]) - - new_balance = sub_uint(INIT_SUPPLY, AMOUNT) - - execution_info = await erc20.balanceOf(account1.contract_address).execute() - assert execution_info.result.balance == new_balance - - - @pytest.mark.asyncio - async def test_burn_from_emits_event(self, contract_factory): - erc20, account1, account2 = contract_factory - - await signer.send_transaction( - account1, erc20.contract_address, 'increaseAllowance', [ - account2.contract_address, - *AMOUNT - ]) - - tx_exec_info = await signer.send_transaction( - account2, erc20.contract_address, 'burnFrom', [ - account1.contract_address, - *AMOUNT - ]) - - # events - assert_events_emitted( - tx_exec_info, - [ - [0, erc20.contract_address, 'Approval', [ - account1.contract_address, account2.contract_address, *UINT_ZERO]], - [1, erc20.contract_address, 'Transfer', [ - account1.contract_address, ZERO_ADDRESS, *AMOUNT]] - ] - ) - - - @pytest.mark.asyncio - async def test_burn_from_over_allowance(self, contract_factory): - erc20, account1, account2 = contract_factory - - await signer.send_transaction( - account1, erc20.contract_address, 'increaseAllowance', [ - account2.contract_address, - *AMOUNT - ]) - - await assert_revert(signer.send_transaction( - account2, erc20.contract_address, 'burnFrom', [ - account1.contract_address, - *INIT_SUPPLY - ]), - reverted_with="ERC20: insufficient allowance" - ) - - - @pytest.mark.asyncio - async def test_burn_from_no_allowance(self, contract_factory): - erc20, account1, account2 = contract_factory - - await assert_revert(signer.send_transaction( - account2, erc20.contract_address, 'burnFrom', [ - account1.contract_address, - *AMOUNT - ]), - reverted_with="ERC20: insufficient allowance" - ) diff --git a/tests/token/erc20/test_ERC20Mintable.py b/tests/token/erc20/test_ERC20Mintable.py deleted file mode 100644 index efdd5c49d..000000000 --- a/tests/token/erc20/test_ERC20Mintable.py +++ /dev/null @@ -1,152 +0,0 @@ -import pytest -from signers import MockSigner -from nile.utils import ( - add_uint, sub_uint, MAX_UINT256, ZERO_ADDRESS, INVALID_UINT256, assert_revert -) -from utils import ( - get_contract_class, cached_contract, assert_event_emitted, State, Account -) -from ERC20BaseSuite import ERC20Base, NAME, SYMBOL, DECIMALS, INIT_SUPPLY, UINT_ONE -from access.OwnableBaseSuite import OwnableBase - - -signer = MockSigner(123456789987654321) - - -@pytest.fixture(scope='module') -def contract_classes(): - account_cls = Account.get_class - erc20_cls = get_contract_class('ERC20Mintable') - - return account_cls, erc20_cls - - -@pytest.fixture(scope='module') -async def erc20_init(contract_classes): - account_cls, erc20_cls = contract_classes - starknet = await State.init() - account1 = await Account.deploy(signer.public_key) - account2 = await Account.deploy(signer.public_key) - erc20 = await starknet.deploy( - contract_class=erc20_cls, - constructor_calldata=[ - NAME, - SYMBOL, - DECIMALS, - *INIT_SUPPLY, - account1.contract_address, # recipient - account1.contract_address # owner - ] - ) - return ( - starknet.state, - account1, - account2, - erc20, - ) - -@pytest.fixture -def contract_factory(contract_classes, erc20_init): - account_cls, erc20_cls = contract_classes - state, account1, account2, erc20 = erc20_init - _state = state.copy() - account1 = cached_contract(_state, account_cls, account1) - account2 = cached_contract(_state, account_cls, account2) - erc20 = cached_contract(_state, erc20_cls, erc20) - - return erc20, account1, account2 - - -class TestERC20Mintable(ERC20Base, OwnableBase): - # - # mint - # - - @pytest.mark.asyncio - async def test_mint(self, contract_factory): - erc20, account, _ = contract_factory - - await signer.send_transaction( - account, erc20.contract_address, 'mint', [ - account.contract_address, - *UINT_ONE - ]) - - # check new supply - execution_info = await erc20.totalSupply().execute() - new_supply = execution_info.result.totalSupply - assert new_supply == add_uint(INIT_SUPPLY, UINT_ONE) - - - @pytest.mark.asyncio - async def test_mint_emits_event(self, contract_factory): - erc20, account, _ = contract_factory - - tx_exec_info = await signer.send_transaction( - account, erc20.contract_address, 'mint', [ - account.contract_address, - *UINT_ONE - ]) - - assert_event_emitted( - tx_exec_info, - from_address=erc20.contract_address, - name='Transfer', - data=[ - ZERO_ADDRESS, - account.contract_address, - *UINT_ONE - ] - ) - - - @pytest.mark.asyncio - async def test_mint_to_zero_address(self, contract_factory): - erc20, account, _ = contract_factory - - await assert_revert(signer.send_transaction( - account, - erc20.contract_address, - 'mint', - [ZERO_ADDRESS, *UINT_ONE] - ), - reverted_with="ERC20: cannot mint to the zero address" - ) - - - @pytest.mark.asyncio - async def test_mint_overflow(self, contract_factory): - erc20, account, recipient = contract_factory - # pass_amount subtracts the already minted supply from MAX_UINT256 in order for - # the minted supply to equal MAX_UINT256 - # (2**128 - 1, 2**128 - 1) - pass_amount = sub_uint(MAX_UINT256, INIT_SUPPLY) - - await signer.send_transaction( - account, erc20.contract_address, 'mint', [ - recipient.contract_address, - *pass_amount - ]) - - # totalSupply is MAX_UINT256 therefore adding (1, 0) should fail - await assert_revert( - signer.send_transaction( - account, erc20.contract_address, 'mint', [ - recipient.contract_address, - *UINT_ONE - ]), - reverted_with="ERC20: mint overflow" - ) - - - @pytest.mark.asyncio - async def test_mint_invalid_uint256(self, contract_factory): - erc20, account, recipient = contract_factory - - await assert_revert(signer.send_transaction( - account, - erc20.contract_address, - 'mint', - [recipient.contract_address, *INVALID_UINT256]), - reverted_with="ERC20: amount is not a valid Uint256" - ) diff --git a/tests/token/erc20/test_ERC20Pausable.py b/tests/token/erc20/test_ERC20Pausable.py deleted file mode 100644 index 3cb3c71e6..000000000 --- a/tests/token/erc20/test_ERC20Pausable.py +++ /dev/null @@ -1,194 +0,0 @@ -import pytest -from signers import MockSigner -from nile.utils import TRUE, FALSE, assert_revert -from utils import ( - get_contract_class, cached_contract, State, Account -) -from ERC20BaseSuite import ERC20Base, NAME, SYMBOL, DECIMALS, INIT_SUPPLY, AMOUNT -from access.OwnableBaseSuite import OwnableBase - - -signer = MockSigner(123456789987654321) - - -@pytest.fixture(scope='module') -def contract_classes(): - account_cls = Account.get_class - erc20_cls = get_contract_class('ERC20Pausable') - - return account_cls, erc20_cls - - -@pytest.fixture(scope='module') -async def erc20_init(contract_classes): - _, erc20_cls = contract_classes - starknet = await State.init() - account1 = await Account.deploy(signer.public_key) - account2 = await Account.deploy(signer.public_key) - erc20 = await starknet.deploy( - contract_class=erc20_cls, - constructor_calldata=[ - NAME, - SYMBOL, - DECIMALS, - *INIT_SUPPLY, - account1.contract_address, # recipient - account1.contract_address # owner - ] - ) - return ( - starknet.state, - account1, - account2, - erc20 - ) - - -@pytest.fixture -def contract_factory(contract_classes, erc20_init): - account_cls, erc20_cls = contract_classes - state, account1, account2, erc20 = erc20_init - _state = state.copy() - account1 = cached_contract(_state, account_cls, account1) - account2 = cached_contract(_state, account_cls, account2) - erc20 = cached_contract(_state, erc20_cls, erc20) - - return erc20, account1, account2 - - -class TestPausable(ERC20Base, OwnableBase): - # - # pause - # - - @pytest.mark.asyncio - async def test_constructor(self, contract_factory): - erc20, _, _ = contract_factory - - execution_info = await erc20.paused().execute() - assert execution_info.result.paused == FALSE - - - @pytest.mark.asyncio - async def test_pause(self, contract_factory): - erc20, owner, other = contract_factory - - await signer.send_transaction(owner, erc20.contract_address, 'pause', []) - - execution_info = await erc20.paused().execute() - assert execution_info.result.paused == TRUE - - await assert_revert(signer.send_transaction( - owner, - erc20.contract_address, - 'transfer', - [other.contract_address, *AMOUNT] - ), - reverted_with="Pausable: paused" - ) - - await assert_revert(signer.send_transaction( - owner, - erc20.contract_address, - 'transferFrom', - [other.contract_address, other.contract_address, *AMOUNT] - ), - reverted_with="Pausable: paused" - ) - - await assert_revert(signer.send_transaction( - owner, - erc20.contract_address, - 'approve', - [other.contract_address, *AMOUNT] - ), - reverted_with="Pausable: paused" - ) - - await assert_revert(signer.send_transaction( - owner, - erc20.contract_address, - 'increaseAllowance', - [other.contract_address, *AMOUNT] - ), - reverted_with="Pausable: paused" - ) - - await assert_revert(signer.send_transaction( - owner, - erc20.contract_address, - 'decreaseAllowance', - [other.contract_address, *AMOUNT] - ), - reverted_with="Pausable: paused" - ) - - - @pytest.mark.asyncio - async def test_unpause(self, contract_factory): - erc20, owner, other = contract_factory - - await signer.send_transaction(owner, erc20.contract_address, 'pause', []) - await signer.send_transaction(owner, erc20.contract_address, 'unpause', []) - - execution_info = await erc20.paused().execute() - assert execution_info.result.paused == FALSE - - success = await signer.send_transaction( - owner, - erc20.contract_address, - 'transfer', - [other.contract_address, *AMOUNT] - ) - assert success.call_info.retdata[1] == TRUE - - success = await signer.send_transaction( - owner, - erc20.contract_address, - 'approve', - [other.contract_address, *AMOUNT] - ) - assert success.call_info.retdata[1] == TRUE - - success = await signer.send_transaction( - other, - erc20.contract_address, - 'transferFrom', - [owner.contract_address, other.contract_address, *AMOUNT] - ) - assert success.call_info.retdata[1] == TRUE - - success = await signer.send_transaction( - owner, - erc20.contract_address, - 'increaseAllowance', - [other.contract_address, *AMOUNT] - ) - assert success.call_info.retdata[1] == TRUE - - success = await signer.send_transaction( - owner, - erc20.contract_address, - 'decreaseAllowance', - [other.contract_address, *AMOUNT] - ) - assert success.call_info.retdata[1] == TRUE - - - @pytest.mark.asyncio - async def test_only_owner(self, contract_factory): - erc20, _, other = contract_factory - - await assert_revert( - signer.send_transaction( - other, erc20.contract_address, 'pause', [] - ), - reverted_with="Ownable: caller is not the owner" - ) - - await assert_revert( - signer.send_transaction( - other, erc20.contract_address, 'unpause', [] - ), - reverted_with="Ownable: caller is not the owner" - ) diff --git a/tests/token/erc20/test_ERC20Upgradeable.py b/tests/token/erc20/test_ERC20Upgradeable.py deleted file mode 100644 index cf3713640..000000000 --- a/tests/token/erc20/test_ERC20Upgradeable.py +++ /dev/null @@ -1,179 +0,0 @@ -import pytest -from starkware.starknet.public.abi import get_selector_from_name -from signers import MockSigner -from nile.utils import to_uint, sub_uint, str_to_felt, assert_revert, TRUE -from utils import get_contract_class, cached_contract, State, Account - - -signer = MockSigner(123456789987654321) - -USER = 999 -INIT_SUPPLY = to_uint(1000) -AMOUNT = to_uint(250) -NAME = str_to_felt('Upgradeable Token') -SYMBOL = str_to_felt('UTKN') -DECIMALS = 18 - - -class TestERC20Upgradeable: - @pytest.fixture(scope='module') - def contract_classes(self): - account_cls = Account.get_class - token_cls = get_contract_class('ERC20Upgradeable') - proxy_cls = get_contract_class('Proxy') - - return account_cls, token_cls, proxy_cls - - - @pytest.fixture(scope='module') - async def token_init(self, contract_classes): - account_cls, token_cls, proxy_cls = contract_classes - starknet = await State.init() - account1 = await Account.deploy(signer.public_key) - account2 = await Account.deploy(signer.public_key) - token_v1 = await starknet.declare( - contract_class=token_cls, - ) - token_v2 = await starknet.declare( - contract_class=token_cls, - ) - selector = get_selector_from_name('initializer') - params = [ - NAME, # name - SYMBOL, # symbol - DECIMALS, # decimals - *INIT_SUPPLY, # initial supply - account1.contract_address, # recipient - account1.contract_address # admin - ] - proxy = await starknet.deploy( - contract_class=proxy_cls, - constructor_calldata=[ - token_v1.class_hash, - selector, - len(params), - *params - ] - ) - return ( - starknet.state, - account1, - account2, - token_v1, - token_v2, - proxy - ) - - - @pytest.fixture - def token_factory(self, contract_classes, token_init): - account_cls, _, proxy_cls = contract_classes - state, account1, account2, token_v1, token_v2, proxy = token_init - _state = state.copy() - account1 = cached_contract(_state, account_cls, account1) - account2 = cached_contract(_state, account_cls, account2) - proxy = cached_contract(_state, proxy_cls, proxy) - - return account1, account2, proxy, token_v1, token_v2 - - - @pytest.mark.asyncio - async def test_constructor(self, token_factory): - admin, _, proxy, *_ = token_factory - - execution_info = await signer.send_transactions( - admin, - [ - (proxy.contract_address, 'name', []), - (proxy.contract_address, 'symbol', []), - (proxy.contract_address, 'decimals', []), - (proxy.contract_address, 'totalSupply', []) - ] - ) - - # check values - expected = [5, NAME, SYMBOL, DECIMALS, *INIT_SUPPLY] - assert execution_info.call_info.retdata == expected - - - @pytest.mark.asyncio - async def test_upgrade(self, token_factory): - admin, _, proxy, _, token_v2 = token_factory - - # transfer - await signer.send_transaction( - admin, proxy.contract_address, 'transfer', [USER, *AMOUNT] - ) - - # upgrade - await signer.send_transaction( - admin, proxy.contract_address, 'upgrade', [token_v2.class_hash] - ) - - # fetch values - execution_info = await signer.send_transactions( - admin, - [ - (proxy.contract_address, 'balanceOf', [admin.contract_address]), - (proxy.contract_address, 'balanceOf', [USER]), - (proxy.contract_address, 'totalSupply', []) - ] - ) - - expected = [ - 6, # number of return values - *sub_uint(INIT_SUPPLY, AMOUNT), # balanceOf admin - *AMOUNT, # balanceOf USER - *INIT_SUPPLY # totalSupply - ] - - assert execution_info.call_info.retdata == expected - - - @pytest.mark.asyncio - async def test_upgrade_from_nonadmin(self, token_factory): - admin, non_admin, proxy, _, token_v2 = token_factory - - # should revert - await assert_revert(signer.send_transaction( - non_admin, proxy.contract_address, 'upgrade', [token_v2.class_hash]), - reverted_with="Proxy: caller is not admin" - ) - - # should upgrade from admin - await signer.send_transaction( - admin, proxy.contract_address, 'upgrade', [token_v2.class_hash] - ) - - - @pytest.mark.asyncio - async def test_upgrade_transferFrom(self, token_factory): - admin, non_admin, proxy, _, _ = token_factory - - # approve - await signer.send_transaction( - admin, proxy.contract_address, 'approve', [ - non_admin.contract_address, - *AMOUNT - ] - ) - - # transferFrom - return_bool = await signer.send_transaction( - non_admin, proxy.contract_address, 'transferFrom', [ - admin.contract_address, - non_admin.contract_address, - *AMOUNT - ] - ) - assert return_bool.call_info.retdata[1] == TRUE - - # should fail - await assert_revert(signer.send_transaction( - non_admin, proxy.contract_address, 'transferFrom', [ - admin.contract_address, - non_admin.contract_address, - *AMOUNT - ] - ) - ) diff --git a/tests/token/erc721/ERC721BaseSuite.py b/tests/token/erc721/ERC721BaseSuite.py deleted file mode 100644 index 551c00743..000000000 --- a/tests/token/erc721/ERC721BaseSuite.py +++ /dev/null @@ -1,924 +0,0 @@ -import pytest -from signers import MockSigner -from nile.utils import ( - str_to_felt, ZERO_ADDRESS, TRUE, FALSE, assert_revert, INVALID_UINT256, - to_uint, sub_uint, add_uint -) -from utils import ( - assert_event_emitted, assert_events_emitted, -) - - -signer = MockSigner(123456789987654321) - -NAME = str_to_felt("Non-fungible Token") -SYMBOL = str_to_felt("NFT") -NONEXISTENT_TOKEN = to_uint(999) -# random token IDs -TOKENS = [to_uint(5042), to_uint(793)] -# test token -TOKEN = TOKENS[0] -# random user address -RECIPIENT = 555 -# random data (mimicking bytes in Solidity) -DATA = [0x42, 0x89, 0x55] -# random URIs -SAMPLE_URI_1 = str_to_felt('mock://mytoken.v1') -SAMPLE_URI_2 = str_to_felt('mock://mytoken.v2') - -# selector ids -IERC165_ID = 0x01ffc9a7 -IERC721_ID = 0x80ac58cd -IERC721_METADATA_ID = 0x5b5e139f -INVALID_ID = 0xffffffff -UNSUPPORTED_ID = 0xabcd1234 - - -class ERC721Base: - # - # constructor - # - - @pytest.mark.asyncio - async def test_constructor(self, contract_factory): - erc721, _, _, _, _ = contract_factory - execution_info = await erc721.name().execute() - assert execution_info.result == (NAME,) - - execution_info = await erc721.symbol().execute() - assert execution_info.result == (SYMBOL,) - # - # supportsInterface - # - - @pytest.mark.asyncio - @pytest.mark.parametrize('interface_id, result', [ - [IERC165_ID, TRUE], - [IERC721_ID, TRUE], - [IERC721_METADATA_ID, TRUE], - [INVALID_ID, FALSE], - [UNSUPPORTED_ID, FALSE], - ]) - async def test_supportsInterface(self, contract_factory, interface_id, result): - erc721, _, _, _, _ = contract_factory - - execution_info = await erc721.supportsInterface(interface_id).execute() - assert execution_info.result == (result,) - - - # - # balanceOf - # - - - @pytest.mark.asyncio - async def test_balanceOf(self, contract_factory): - erc721, account, _, _, _ = contract_factory - - # mint tokens to account - for token in TOKENS: - await signer.send_transaction( - account, erc721.contract_address, 'mint', [ - account.contract_address, *token] - ) - - execution_info = await erc721.balanceOf(account.contract_address).execute() - n_tokens = len(TOKENS) - assert execution_info.result == (to_uint(n_tokens),) - - # user should have zero tokens - execution_info = await erc721.balanceOf(RECIPIENT).execute() - assert execution_info.result == (to_uint(0),) - - - @pytest.mark.asyncio - async def test_balanceOf_zero_address(self, contract_factory): - erc721, account, _, _, _ = contract_factory - - # mint tokens to account - await signer.send_transaction( - account, erc721.contract_address, 'mint', [ - account.contract_address, *TOKEN] - ) - - # should revert when querying zero address - await assert_revert( - erc721.balanceOf(ZERO_ADDRESS).execute(), - reverted_with="ERC721: balance query for the zero address" - ) - - - # - # ownerOf - # - - - @pytest.mark.asyncio - async def test_ownerOf(self, contract_factory): - erc721, account, _, _, _ = contract_factory - - # mint tokens to account - for token in TOKENS: - await signer.send_transaction( - account, erc721.contract_address, 'mint', [ - account.contract_address, *token] - ) - - # should return account's address - execution_info = await erc721.ownerOf(token).execute() - assert execution_info.result == (account.contract_address,) - - - @pytest.mark.asyncio - async def test_ownerOf_nonexistent_token(self, contract_factory): - erc721, account, _, _, _ = contract_factory - - # mint token to account - await signer.send_transaction( - account, erc721.contract_address, 'mint', [ - account.contract_address, *TOKEN] - ) - - # should revert when querying nonexistent token - await assert_revert( - erc721.ownerOf(NONEXISTENT_TOKEN).execute(), - reverted_with="ERC721: owner query for nonexistent token" - ) - - - @pytest.mark.asyncio - async def test_ownerOf_invalid_uint256(self, contract_factory): - erc721, _, _, _, _ = contract_factory - - # should revert when querying nonexistent token - await assert_revert( - erc721.ownerOf(INVALID_UINT256).execute(), - reverted_with="ERC721: token_id is not a valid Uint256" - ) - - - # - # approve - # - - - @pytest.mark.asyncio - async def test_approve(self, erc721_minted): - erc721, account, spender, *_ = erc721_minted - - await signer.send_transaction( - account, erc721.contract_address, 'approve', [ - spender.contract_address, *TOKEN] - ) - - execution_info = await erc721.getApproved(TOKEN).execute() - assert execution_info.result == (spender.contract_address,) - - - @pytest.mark.asyncio - async def test_approve_emits_event(self, erc721_minted): - erc721, account, spender, *_ = erc721_minted - - # mint token to account - tx_exec_info = await signer.send_transaction( - account, erc721.contract_address, 'approve', [ - spender.contract_address, - *TOKEN - ] - ) - - assert_event_emitted( - tx_exec_info, - from_address=erc721.contract_address, - name='Approval', - data=[ - account.contract_address, - spender.contract_address, - *TOKEN - ] - ) - - - @pytest.mark.asyncio - async def test_approve_on_setApprovalForAll(self, erc721_minted): - erc721, account, spender, *_ = erc721_minted - - # set approval_for_all from account to spender - await signer.send_transaction( - account, erc721.contract_address, 'setApprovalForAll', [ - spender.contract_address, TRUE] - ) - - # approve spender to spend account's token to recipient - await signer.send_transaction( - spender, erc721.contract_address, 'approve', [ - RECIPIENT, *TOKEN] - ) - - execution_info = await erc721.getApproved(TOKEN).execute() - assert execution_info.result == (RECIPIENT,) - - - @pytest.mark.asyncio - async def test_approve_from_zero_address(self, erc721_minted): - erc721, _, spender, *_ = erc721_minted - - # Without using an account abstraction, the caller address - # (get_caller_address) is zero - await assert_revert( - erc721.approve( - spender.contract_address, TOKEN).execute(), - reverted_with="ERC721: cannot approve from the zero address" - ) - - - @pytest.mark.asyncio - async def test_approve_owner_is_recipient(self, erc721_minted): - erc721, account, *_ = erc721_minted - - # should fail when owner is the same as address-to-be-approved - await assert_revert( - signer.send_transaction( - account, erc721.contract_address, 'approve', [ - account.contract_address, - *TOKEN - ]), - reverted_with="ERC721: approval to current owner" - ) - - - @pytest.mark.asyncio - async def test_approve_not_owner_or_operator(self, contract_factory): - erc721, account, spender, _, _ = contract_factory - - # mint to recipient — NOT account - await signer.send_transaction( - account, erc721.contract_address, 'mint', [ - RECIPIENT, *TOKEN] - ) - - # 'approve' should fail since recipient owns token - await assert_revert(signer.send_transaction( - account, erc721.contract_address, 'approve', [ - spender.contract_address, - *TOKEN - ]), - reverted_with="ERC721: approve caller is not owner nor approved for all" - ) - - - @pytest.mark.asyncio - async def test_approve_on_already_approved(self, erc721_minted): - erc721, account, spender, *_ = erc721_minted - - # first approval - await signer.send_transaction( - account, erc721.contract_address, 'approve', [ - spender.contract_address, *TOKEN] - ) - - # repeat approval - await signer.send_transaction( - account, erc721.contract_address, 'approve', [ - spender.contract_address, *TOKEN] - ) - - # check that approval does not change - execution_info = await erc721.getApproved(TOKEN).execute() - assert execution_info.result == (spender.contract_address,) - - - @pytest.mark.asyncio - async def test_getApproved_nonexistent_token(self, erc721_minted): - erc721, *_ = erc721_minted - - await assert_revert( - erc721.getApproved(NONEXISTENT_TOKEN).execute(), - reverted_with="ERC721: approved query for nonexistent token" - ) - - - @pytest.mark.asyncio - async def test_getApproved_invalid_uint256(self, erc721_minted): - erc721, *_ = erc721_minted - - await assert_revert( - erc721.getApproved(INVALID_UINT256).execute(), - reverted_with="ERC721: token_id is not a valid Uint256" - ) - - - # - # setApprovalForAll - # - - - @pytest.mark.asyncio - async def test_setApprovalForAll(self, erc721_minted): - erc721, account, spender, *_ = erc721_minted - - await signer.send_transaction( - account, erc721.contract_address, 'setApprovalForAll', [ - spender.contract_address, TRUE] - ) - - execution_info = await erc721.isApprovedForAll( - account.contract_address, spender.contract_address).execute() - assert execution_info.result == (TRUE,) - - - @pytest.mark.asyncio - async def test_setApprovalForAll_emits_event(self, erc721_minted): - erc721, account, spender, *_ = erc721_minted - - tx_exec_info = await signer.send_transaction( - account, erc721.contract_address, 'setApprovalForAll', [ - spender.contract_address, TRUE] - ) - - assert_event_emitted( - tx_exec_info, - from_address=erc721.contract_address, - name='ApprovalForAll', - data=[ - account.contract_address, - spender.contract_address, - TRUE - ] - ) - - - @pytest.mark.asyncio - async def test_setApprovalForAll_when_operator_was_set_as_not_approved(self, erc721_minted): - erc721, account, spender, *_ = erc721_minted - - await signer.send_transaction( - account, erc721.contract_address, 'setApprovalForAll', [ - spender.contract_address, FALSE] - ) - - await signer.send_transaction( - account, erc721.contract_address, 'setApprovalForAll', [ - spender.contract_address, TRUE] - ) - - execution_info = await erc721.isApprovedForAll( - account.contract_address, spender.contract_address).execute() - assert execution_info.result == (TRUE,) - - - @pytest.mark.asyncio - async def test_setApprovalForAll_with_invalid_bool_arg(self, erc721_minted): - erc721, account, spender, *_ = erc721_minted - - not_bool = 2 - - await assert_revert( - signer.send_transaction( - account, erc721.contract_address, 'setApprovalForAll', [ - spender.contract_address, - not_bool - ]), - reverted_with="ERC721: approved is not a Cairo boolean") - - - @pytest.mark.asyncio - async def test_setApprovalForAll_owner_is_operator(self, erc721_minted): - erc721, account, *_ = erc721_minted - - await assert_revert( - signer.send_transaction( - account, erc721.contract_address, 'setApprovalForAll', [ - account.contract_address, - TRUE - ]), - reverted_with="ERC721: approve to caller" - ) - - - @pytest.mark.asyncio - async def test_setApprovalForAll_from_zero_address(self, erc721_minted): - erc721, account, *_ = erc721_minted - - await assert_revert( - erc721.setApprovalForAll(account.contract_address, TRUE).execute(), - reverted_with="ERC721: either the caller or operator is the zero address" - ) - - - @pytest.mark.asyncio - async def test_setApprovalForAll_operator_is_zero_address(self, erc721_minted): - erc721, account, *_ = erc721_minted - - await assert_revert( - signer.send_transaction( - account, erc721.contract_address, 'setApprovalForAll', [ - ZERO_ADDRESS, - TRUE - ]), - reverted_with="ERC721: either the caller or operator is the zero address" - ) - - - # - # transferFrom - # - - - @pytest.mark.asyncio - async def test_transferFrom_owner(self, erc721_minted): - erc721, account, *_ = erc721_minted - - # get account's previous balance - execution_info = await erc721.balanceOf(account.contract_address).execute() - previous_balance = execution_info.result.balance - - # transfers token from account to recipient - await signer.send_transaction( - account, erc721.contract_address, 'transferFrom', [ - account.contract_address, RECIPIENT, *TOKEN] - ) - - # checks recipient balance - execution_info = await erc721.balanceOf(RECIPIENT).execute() - assert execution_info.result == (to_uint(1),) - - # checks account balance - execution_info = await erc721.balanceOf(account.contract_address).execute() - assert execution_info.result.balance == sub_uint( - previous_balance, to_uint(1)) - - # checks token has new owner - execution_info = await erc721.ownerOf(TOKEN).execute() - assert execution_info.result == (RECIPIENT,) - - # checks approval is cleared for token_id - execution_info = await erc721.getApproved(TOKEN).execute() - assert execution_info.result == (0,) - - - @pytest.mark.asyncio - async def test_transferFrom_emits_events(self, erc721_minted): - erc721, account, spender, *_ = erc721_minted - - # setApprovalForAll - await signer.send_transaction( - account, erc721.contract_address, 'setApprovalForAll', [ - spender.contract_address, TRUE] - ) - - # spender transfers token from account to recipient - tx_exec_info = await signer.send_transaction( - spender, erc721.contract_address, 'transferFrom', [ - account.contract_address, - RECIPIENT, - *TOKEN - ] - ) - - # events - assert_events_emitted( - tx_exec_info, - [ - [0, erc721.contract_address, 'Approval', [ - account.contract_address, ZERO_ADDRESS, *TOKEN]], - [1, erc721.contract_address, 'Transfer', [ - account.contract_address, RECIPIENT, *TOKEN]] - ] - ) - - - @pytest.mark.asyncio - async def test_transferFrom_approved_user(self, erc721_minted): - erc721, account, spender, *_ = erc721_minted - - # approve spender - await signer.send_transaction( - account, erc721.contract_address, 'approve', [ - spender.contract_address, *TOKEN] - ) - - # spender transfers token from account to recipient - await signer.send_transaction( - spender, erc721.contract_address, 'transferFrom', [ - account.contract_address, RECIPIENT, *TOKEN] - ) - - # checks user balance - execution_info = await erc721.balanceOf(RECIPIENT).execute() - assert execution_info.result == (to_uint(1),) - - - @pytest.mark.asyncio - async def test_transferFrom_operator(self, erc721_minted): - erc721, account, spender, *_ = erc721_minted - - # setApprovalForAll - await signer.send_transaction( - account, erc721.contract_address, 'setApprovalForAll', [ - spender.contract_address, TRUE] - ) - - # spender transfers token from account to recipient - await signer.send_transaction( - spender, erc721.contract_address, 'transferFrom', [ - account.contract_address, RECIPIENT, *TOKEN] - ) - - # checks user balance - execution_info = await erc721.balanceOf(RECIPIENT).execute() - assert execution_info.result == (to_uint(1),) - - - @pytest.mark.asyncio - async def test_transferFrom_when_not_approved_or_owner(self, erc721_minted): - erc721, account, spender, *_ = erc721_minted - - # setApprovalForAll to false - await signer.send_transaction( - account, erc721.contract_address, 'setApprovalForAll', [ - spender.contract_address, FALSE] - ) - - # should be rejected when not approved - await assert_revert(signer.send_transaction( - spender, erc721.contract_address, 'transferFrom', [ - account.contract_address, - RECIPIENT, - *TOKEN - ]), - reverted_with="ERC721: either is not approved or the caller is the zero address" - ) - - - @pytest.mark.asyncio - async def test_transferFrom_to_zero_address(self, erc721_minted): - erc721, account, spender, *_ = erc721_minted - - # setApprovalForAll - await signer.send_transaction( - account, erc721.contract_address, 'setApprovalForAll', [ - spender.contract_address, TRUE] - ) - - try: - # erc721 - await assert_revert(signer.send_transaction( - spender, erc721.contract_address, 'transferFrom', [ - account.contract_address, - ZERO_ADDRESS, - *TOKEN - ]), - reverted_with="ERC721: cannot transfer to the zero address" - ) - except AssertionError: - # erc721 enumerable - await assert_revert(signer.send_transaction( - spender, erc721.contract_address, 'transferFrom', [ - account.contract_address, - ZERO_ADDRESS, - *TOKEN - ]), - reverted_with="ERC721: balance query for the zero address" - ) - - - @pytest.mark.asyncio - async def test_transferFrom_invalid_uint256(self, erc721_minted): - erc721, account, *_ = erc721_minted - - await assert_revert( - signer.send_transaction( - account, erc721.contract_address, 'transferFrom', [ - account.contract_address, - RECIPIENT, - *INVALID_UINT256 - ]), - reverted_with="ERC721: token_id is not a valid Uint256" - ) - - - @pytest.mark.asyncio - async def test_transferFrom_from_zero_address(self, erc721_minted): - erc721, account, *_ = erc721_minted - - # caller address is `0` when not using an account contract - await assert_revert( - erc721.transferFrom( - account.contract_address, - RECIPIENT, - TOKEN - ).execute(), - reverted_with="ERC721: either is not approved or the caller is the zero address" - ) - - - # - # safeTransferFrom - # - - - @pytest.mark.asyncio - async def test_safeTransferFrom(self, erc721_minted): - erc721, account, _, erc721_holder, _ = erc721_minted - - await signer.send_transaction( - account, erc721.contract_address, 'safeTransferFrom', [ - account.contract_address, - erc721_holder.contract_address, - *TOKEN, - len(DATA), - *DATA - ] - ) - - # check balance - execution_info = await erc721.balanceOf(erc721_holder.contract_address).execute() - assert execution_info.result == (to_uint(1),) - - # check owner - execution_info = await erc721.ownerOf(TOKEN).execute() - assert execution_info.result == (erc721_holder.contract_address,) - - - @pytest.mark.asyncio - async def test_safeTransferFrom_emits_events(self, erc721_minted): - erc721, account, _, erc721_holder, _ = erc721_minted - - tx_exec_info = await signer.send_transaction( - account, erc721.contract_address, 'safeTransferFrom', [ - account.contract_address, - erc721_holder.contract_address, - *TOKEN, - len(DATA), - *DATA - ] - ) - - - # events - assert_events_emitted( - tx_exec_info, - [ - [0, erc721.contract_address, 'Approval', [ - account.contract_address, ZERO_ADDRESS, *TOKEN]], - [1, erc721.contract_address, 'Transfer', [ - account.contract_address, erc721_holder.contract_address, *TOKEN]] - ] - ) - - - @pytest.mark.asyncio - async def test_safeTransferFrom_from_approved(self, erc721_minted): - erc721, account, spender, erc721_holder, _ = erc721_minted - - execution_info = await erc721.balanceOf(erc721_holder.contract_address).execute() - previous_balance = execution_info.result.balance - - # approve spender - await signer.send_transaction( - account, erc721.contract_address, 'approve', [ - spender.contract_address, *TOKEN] - ) - - # spender transfers token from account to erc721_holder - await signer.send_transaction( - spender, erc721.contract_address, 'safeTransferFrom', [ - account.contract_address, - erc721_holder.contract_address, - *TOKEN, - len(DATA), - *DATA - ] - ) - - # erc721_holder balance check - execution_info = await erc721.balanceOf(erc721_holder.contract_address).execute() - assert execution_info.result.balance == add_uint( - previous_balance, to_uint(1) - ) - - - @pytest.mark.asyncio - async def test_safeTransferFrom_from_operator(self, erc721_minted): - erc721, account, spender, erc721_holder, _ = erc721_minted - - execution_info = await erc721.balanceOf(erc721_holder.contract_address).execute() - previous_balance = execution_info.result.balance - - # setApprovalForAll - await signer.send_transaction( - account, erc721.contract_address, 'setApprovalForAll', [ - spender.contract_address, TRUE] - ) - - # spender transfers token from account to erc721_holder - await signer.send_transaction( - spender, erc721.contract_address, 'safeTransferFrom', [ - account.contract_address, - erc721_holder.contract_address, - *TOKEN, - len(DATA), - *DATA - ] - ) - - # erc721_holder balance check - execution_info = await erc721.balanceOf(erc721_holder.contract_address).execute() - assert execution_info.result.balance == add_uint( - previous_balance, to_uint(1) - ) - - - @pytest.mark.asyncio - async def test_safeTransferFrom_when_not_approved_or_owner(self, erc721_minted): - erc721, account, spender, erc721_holder, _ = erc721_minted - - # should fail when not approved or owner - await assert_revert(signer.send_transaction( - spender, erc721.contract_address, 'safeTransferFrom', [ - account.contract_address, - erc721_holder.contract_address, - *TOKEN, - len(DATA), - *DATA - ]), - reverted_with="ERC721: either is not approved or the caller is the zero address" - ) - - - @pytest.mark.asyncio - async def test_safeTransferFrom_to_zero_address(self, erc721_minted): - erc721, account, *_ = erc721_minted - - # to zero address should be rejected - try: - await assert_revert(signer.send_transaction( - account, erc721.contract_address, 'safeTransferFrom', [ - account.contract_address, - ZERO_ADDRESS, - *TOKEN, - len(DATA), - *DATA - ]), - reverted_with="ERC721: cannot transfer to the zero address" - ) - except AssertionError: - await assert_revert(signer.send_transaction( - account, erc721.contract_address, 'safeTransferFrom', [ - account.contract_address, - ZERO_ADDRESS, - *TOKEN, - len(DATA), - *DATA - ]), - reverted_with="ERC721: balance query for the zero address" - ) - - - @pytest.mark.asyncio - async def test_safeTransferFrom_from_zero_address(self, erc721_minted): - erc721, account, _, erc721_holder, _ = erc721_minted - - # caller address is `0` when not using an account contract - await assert_revert( - erc721.safeTransferFrom( - account.contract_address, - erc721_holder.contract_address, - TOKEN, - DATA - ).execute(), - reverted_with="ERC721: either is not approved or the caller is the zero address" - ) - - - @pytest.mark.asyncio - async def test_safeTransferFrom_to_unsupported_contract(self, erc721_minted): - erc721, account, _, _, unsupported = erc721_minted - - await assert_revert( - signer.send_transaction( - account, erc721.contract_address, 'safeTransferFrom', [ - account.contract_address, - unsupported.contract_address, - *TOKEN, - len(DATA), - *DATA, - ]) - ) - - - @pytest.mark.asyncio - async def test_safeTransferFrom_to_account(self, erc721_minted): - erc721, account, account2, *_ = erc721_minted - - await signer.send_transaction( - account, erc721.contract_address, 'safeTransferFrom', [ - account.contract_address, - account2.contract_address, - *TOKEN, - len(DATA), - *DATA - ] - ) - - # check balance - execution_info = await erc721.balanceOf(account2.contract_address).execute() - assert execution_info.result == (to_uint(1),) - - # check owner - execution_info = await erc721.ownerOf(TOKEN).execute() - assert execution_info.result == (account2.contract_address,) - - - @pytest.mark.asyncio - async def test_safeTransferFrom_invalid_uint256(self, erc721_minted): - erc721, account, _, erc721_holder, _ = erc721_minted - - await assert_revert( - signer.send_transaction( - account, erc721.contract_address, 'safeTransferFrom', [ - account.contract_address, - erc721_holder.contract_address, - *INVALID_UINT256, - len(DATA), - *DATA - ]), - reverted_with="ERC721: token_id is not a valid Uint256" - ) - - - # - # tokenURI - # - - - @pytest.mark.asyncio - async def test_tokenURI(self, erc721_minted): - erc721, account, *_ = erc721_minted - - token_1 = TOKENS[0] - token_2 = TOKENS[1] - - # should be zero when tokenURI is not set - execution_info = await erc721.tokenURI(token_1).execute() - assert execution_info.result == (0,) - - # setTokenURI for token_1 - await signer.send_transaction( - account, erc721.contract_address, 'setTokenURI', [ - *token_1, - SAMPLE_URI_1 - ] - ) - - execution_info = await erc721.tokenURI(token_1).execute() - assert execution_info.result == (SAMPLE_URI_1,) - - # setTokenURI for token_2 - await signer.send_transaction( - account, erc721.contract_address, 'setTokenURI', [ - *token_2, - SAMPLE_URI_2 - ] - ) - - execution_info = await erc721.tokenURI(token_2).execute() - assert execution_info.result == (SAMPLE_URI_2,) - - - @pytest.mark.asyncio - async def test_tokenURI_should_revert_for_nonexistent_token(self, erc721_minted): - erc721, *_ = erc721_minted - - # should revert for nonexistent token - await assert_revert( - erc721.tokenURI(NONEXISTENT_TOKEN).execute(), - reverted_with="ERC721_Metadata: URI query for nonexistent token" - ) - - - @pytest.mark.asyncio - async def test_setTokenURI_from_not_owner(self, erc721_minted): - erc721, _, not_owner, *_ = erc721_minted - - await assert_revert(signer.send_transaction( - not_owner, erc721.contract_address, 'setTokenURI', [ - *TOKEN, - SAMPLE_URI_1 - ]), - reverted_with="Ownable: caller is not the owner" - ) - - - @pytest.mark.asyncio - async def test_setTokenURI_for_nonexistent_token(self, erc721_minted): - erc721, account, *_ = erc721_minted - - await assert_revert(signer.send_transaction( - account, erc721.contract_address, 'setTokenURI', [ - *NONEXISTENT_TOKEN, - SAMPLE_URI_1 - ]), - reverted_with="ERC721_Metadata: set token URI for nonexistent token" - ) diff --git a/tests/token/erc721/test_ERC721EnumerableMintableBurnable.py b/tests/token/erc721/test_ERC721EnumerableMintableBurnable.py deleted file mode 100644 index 5ee789508..000000000 --- a/tests/token/erc721/test_ERC721EnumerableMintableBurnable.py +++ /dev/null @@ -1,514 +0,0 @@ -import pytest -from signers import MockSigner -from nile.utils import ( - MAX_UINT256, ZERO_ADDRESS, TRUE, assert_revert, to_uint, sub_uint, add_uint -) -from utils import ( - get_contract_class, cached_contract, assert_event_emitted, - assert_events_emitted, State, Account -) -from ERC721BaseSuite import ( - ERC721Base, NAME, SYMBOL, NONEXISTENT_TOKEN, DATA, RECIPIENT -) -from access.OwnableBaseSuite import OwnableBase - - -signer = MockSigner(123456789987654321) - - -# random token IDs -TOKENS = [ - to_uint(5042), to_uint(793), to_uint(321), MAX_UINT256, to_uint(8) -] -TOKEN = TOKENS[0] -# total tokens as uint -TOTAL_TOKENS = to_uint(len(TOKENS)) -# selector id -ENUMERABLE_INTERFACE_ID = 0x780e9d63 - - -@pytest.fixture(scope='module') -def contract_classes(): - account_cls = Account.get_class - erc721_cls = get_contract_class('ERC721EnumerableMintableBurnable') - erc721_holder_cls = get_contract_class('ERC721Holder') - unsupported_cls = get_contract_class('Initializable') - - return account_cls, erc721_cls, erc721_holder_cls, unsupported_cls - - -@pytest.fixture(scope='module') -async def erc721_init(contract_classes): - account_cls, erc721_cls, erc721_holder_cls, unsupported_cls = contract_classes - starknet = await State.init() - account1 = await Account.deploy(signer.public_key) - account2 = await Account.deploy(signer.public_key) - erc721 = await starknet.deploy( - contract_class=erc721_cls, - constructor_calldata=[ - NAME, # name - SYMBOL, # symbol - account1.contract_address # owner - ] - ) - erc721_holder = await starknet.deploy( - contract_class=erc721_holder_cls, - constructor_calldata=[] - ) - unsupported = await starknet.deploy( - contract_class=unsupported_cls, - constructor_calldata=[] - ) - return ( - starknet.state, - account1, - account2, - erc721, - erc721_holder, - unsupported, - ) - - -@pytest.fixture -def contract_factory(contract_classes, erc721_init): - account_cls, erc721_cls, erc721_holder_cls, unsupported_cls = contract_classes - state, account1, account2, erc721, erc721_holder, unsupported = erc721_init - _state = state.copy() - account1 = cached_contract(_state, account_cls, account1) - account2 = cached_contract(_state, account_cls, account2) - erc721 = cached_contract(_state, erc721_cls, erc721) - erc721_holder = cached_contract(_state, erc721_holder_cls, erc721_holder) - unsupported = cached_contract(_state, unsupported_cls, unsupported) - - return erc721, account1, account2, erc721_holder, unsupported - - -@pytest.fixture -async def erc721_minted(contract_factory): - erc721, account, account2, erc721_holder, unsupported = contract_factory - # mint tokens to account - for token in TOKENS: - await signer.send_transaction( - account, erc721.contract_address, 'mint', [ - account.contract_address, *token] - ) - - return erc721, account, account2, erc721_holder, unsupported - - -class TestERC721EnumerableMintableBurnable(ERC721Base, OwnableBase): - # - # supportsInterface - # - - @pytest.mark.asyncio - async def test_supportsInterface(self, contract_factory): - erc721, *_ = contract_factory - - execution_info = await erc721.supportsInterface(ENUMERABLE_INTERFACE_ID).execute() - assert execution_info.result == (TRUE,) - - # - # totalSupply - # - - @pytest.mark.asyncio - async def test_totalSupply(self, erc721_minted): - erc721, *_ = erc721_minted - - execution_info = await erc721.totalSupply().execute() - assert execution_info.result == (TOTAL_TOKENS,) - - - # - # tokenOfOwnerByIndex - # - - - @pytest.mark.asyncio - async def test_tokenOfOwnerByIndex(self, erc721_minted): - erc721, account, *_ = erc721_minted - - # check index - for i in range(0, len(TOKENS)): - execution_info = await erc721.tokenOfOwnerByIndex( - account.contract_address, to_uint(i)).execute() - assert execution_info.result == (TOKENS[i],) - - - @pytest.mark.asyncio - async def test_tokenOfOwnerByIndex_greater_than_supply(self, erc721_minted): - erc721, account, *_ = erc721_minted - - tokens_plus_one = add_uint(TOTAL_TOKENS, to_uint(1)) - - await assert_revert( - erc721.tokenOfOwnerByIndex( - account.contract_address, tokens_plus_one).execute(), - reverted_with="ERC721Enumerable: owner index out of bounds" - ) - - - @pytest.mark.asyncio - async def test_tokenOfOwnerByIndex_owner_with_no_tokens(self, erc721_minted): - erc721, *_ = erc721_minted - - await assert_revert( - erc721.tokenOfOwnerByIndex(RECIPIENT, to_uint(1)).execute(), - reverted_with="ERC721Enumerable: owner index out of bounds" - ) - - - @pytest.mark.asyncio - async def test_tokenOfOwnerByIndex_transfer_all_tokens(self, erc721_minted): - erc721, account, other, *_ = erc721_minted - - # transfer all tokens - for token in TOKENS: - await signer.send_transaction( - account, erc721.contract_address, 'transferFrom', [ - account.contract_address, - other.contract_address, - *token - ] - ) - - execution_info = await erc721.balanceOf(other.contract_address).execute() - assert execution_info.result == (TOTAL_TOKENS,) - - for i in range(0, len(TOKENS)): - execution_info = await erc721.tokenOfOwnerByIndex(other.contract_address, to_uint(i)).execute() - assert execution_info.result == (TOKENS[i],) - - execution_info = await erc721.balanceOf(account.contract_address).execute() - assert execution_info.result == (to_uint(0),) - - # check that queries to old owner's token ownership reverts since index is less - # than the target's balance - await assert_revert(erc721.tokenOfOwnerByIndex( - account.contract_address, to_uint(0)).execute(), - reverted_with="ERC721Enumerable: owner index out of bounds" - ) - - - @pytest.mark.asyncio - async def test_tokenOfOwnerByIndex_safe_transfer_all_tokens(self, erc721_minted): - erc721, account, other, *_ = erc721_minted - - # safe transfer all tokens - for token in TOKENS: - await signer.send_transaction( - account, erc721.contract_address, 'safeTransferFrom', [ - account.contract_address, - other.contract_address, - *token, - len(DATA), - *DATA - ] - ) - - execution_info = await erc721.balanceOf(other.contract_address).execute() - assert execution_info.result == (TOTAL_TOKENS,) - - for i in range(0, len(TOKENS)): - execution_info = await erc721.tokenOfOwnerByIndex(other.contract_address, to_uint(i)).execute() - assert execution_info.result == (TOKENS[i],) - - execution_info = await erc721.balanceOf(account.contract_address).execute() - assert execution_info.result == (to_uint(0),) - - # check that queries to old owner's token ownership reverts since index is less - # than the target's balance - await assert_revert(erc721.tokenOfOwnerByIndex( - account.contract_address, to_uint(0)).execute(), - reverted_with="ERC721Enumerable: owner index out of bounds" - ) - - - # - # tokenByIndex - # - - - @pytest.mark.asyncio - async def test_tokenByIndex(self, erc721_minted): - erc721, *_ = erc721_minted - - for i in range(0, len(TOKENS)): - execution_info = await erc721.tokenByIndex(to_uint(i)).execute() - assert execution_info.result == (TOKENS[i],) - - - @pytest.mark.asyncio - async def test_tokenByIndex_greater_than_supply(self, erc721_minted): - erc721, *_ = erc721_minted - - await assert_revert( - erc721.tokenByIndex(to_uint(5)).execute(), - reverted_with="ERC721Enumerable: global index out of bounds" - ) - - - @pytest.mark.asyncio - async def test_tokenByIndex_burn_last_token(self, erc721_minted): - erc721, account, *_ = erc721_minted - - tokens_minus_one = sub_uint(TOTAL_TOKENS, to_uint(1)) - - # burn last token - await signer.send_transaction( - account, erc721.contract_address, 'burn', [ - *TOKENS[4]] - ) - - execution_info = await erc721.totalSupply().execute() - assert execution_info.result == (tokens_minus_one,) - - for i in range(0, 4): - execution_info = await erc721.tokenByIndex(to_uint(i)).execute() - assert execution_info.result == (TOKENS[i],) - - await assert_revert( - erc721.tokenByIndex(tokens_minus_one).execute(), - reverted_with="ERC721Enumerable: global index out of bounds" - ) - - - @pytest.mark.asyncio - async def test_tokenByIndex_burn_first_token(self, erc721_minted): - erc721, account, *_ = erc721_minted - - # burn first token - await signer.send_transaction( - account, erc721.contract_address, 'burn', [ - *TOKENS[0]] - ) - - # TOKEN[0] should be burnt and TOKEN[4] should be swapped - # to TOKEN[0]'s index - new_token_order = [TOKENS[4], TOKENS[1], TOKENS[2], TOKENS[3]] - for i in range(0, 3): - execution_info = await erc721.tokenByIndex(to_uint(i)).execute() - assert execution_info.result == (new_token_order[i],) - - - @pytest.mark.asyncio - async def test_tokenByIndex_burn_and_mint(self, erc721_minted): - erc721, account, *_ = erc721_minted - - for token in TOKENS: - await signer.send_transaction( - account, erc721.contract_address, 'burn', [ - *token] - ) - - execution_info = await erc721.totalSupply().execute() - assert execution_info.result == (to_uint(0),) - - await assert_revert( - erc721.tokenByIndex(to_uint(0)).execute(), - reverted_with="ERC721Enumerable: global index out of bounds" - ) - - # mint new tokens - for token in TOKENS: - await signer.send_transaction( - account, erc721.contract_address, 'mint', [ - account.contract_address, *token] - ) - - for i in range(0, len(TOKENS)): - execution_info = await erc721.tokenByIndex(to_uint(i)).execute() - assert execution_info.result == (TOKENS[i],) - - # - # mint - # - - @pytest.mark.asyncio - async def test_mint_emits_event(self, contract_factory): - erc721, account, _, _, _ = contract_factory - - # mint token to account - tx_exec_info = await signer.send_transaction( - account, erc721.contract_address, 'mint', [ - account.contract_address, *TOKEN] - ) - - assert_event_emitted( - tx_exec_info, - from_address=erc721.contract_address, - name='Transfer', - data=[ - ZERO_ADDRESS, - account.contract_address, - *TOKEN - ] - ) - - - @pytest.mark.asyncio - async def test_mint(self, erc721_minted): - erc721, account, *_ = erc721_minted - - # checks balance - execution_info = await erc721.balanceOf(account.contract_address).execute() - assert execution_info.result == (to_uint(5),) - - # checks that account owns correct tokens - for token in TOKENS: - execution_info = await erc721.ownerOf(token).execute() - assert execution_info.result == (account.contract_address,) - - - @pytest.mark.asyncio - async def test_mint_duplicate_token_id(self, erc721_minted): - erc721, account, *_ = erc721_minted - - # minting duplicate token_id should fail - await assert_revert(signer.send_transaction( - account, erc721.contract_address, 'mint', [ - account.contract_address, - *TOKEN - ]), - reverted_with="ERC721: token already minted" - ) - - - @pytest.mark.asyncio - async def test_mint_to_zero_address(self, erc721_minted): - erc721, account, *_ = erc721_minted - - # minting to zero address should fail - await assert_revert(signer.send_transaction( - account, erc721.contract_address, 'mint', [ - ZERO_ADDRESS, - *NONEXISTENT_TOKEN - ]), - reverted_with="ERC721: balance query for the zero address" - ) - - - @pytest.mark.asyncio - async def test_mint_approve_should_be_zero_address(self, erc721_minted): - erc721, *_ = erc721_minted - - # approved address should be zero for newly minted tokens - for token in TOKENS: - execution_info = await erc721.getApproved(token).execute() - assert execution_info.result == (0,) - - - @pytest.mark.asyncio - async def test_mint_by_not_owner(self, contract_factory): - erc721, _, not_owner, _, _ = contract_factory - - # minting from not_owner should fail - await assert_revert(signer.send_transaction( - not_owner, erc721.contract_address, 'mint', [ - not_owner.contract_address, - *TOKENS[0] - ]), - reverted_with="Ownable: caller is not the owner" - ) - - - # - # burn - # - - - @pytest.mark.asyncio - async def test_burn(self, erc721_minted): - erc721, account, *_ = erc721_minted - - execution_info = await erc721.balanceOf(account.contract_address).execute() - previous_balance = execution_info.result.balance - - # burn token - await signer.send_transaction( - account, erc721.contract_address, 'burn', [*TOKEN] - ) - - # account balance should subtract one - execution_info = await erc721.balanceOf(account.contract_address).execute() - assert execution_info.result.balance == sub_uint( - previous_balance, to_uint(1) - ) - - # approve should be cleared to zero, therefore, - # 'getApproved()' call should fail - await assert_revert( - erc721.getApproved(TOKEN).execute(), - reverted_with="ERC721: approved query for nonexistent token" - ) - - # 'token_to_burn' owner should be zero; therefore, - # 'ownerOf()' call should fail - await assert_revert( - erc721.ownerOf(TOKEN).execute(), - reverted_with="ERC721: owner query for nonexistent token" - ) - - - @pytest.mark.asyncio - async def test_burn_emits_event(self, erc721_minted): - erc721, account, *_ = erc721_minted - - # mint token to account - tx_exec_info = await signer.send_transaction( - account, erc721.contract_address, 'burn', [ - *TOKEN - ] - ) - - # events - assert_events_emitted( - tx_exec_info, - [ - [0, erc721.contract_address, 'Approval', [ - account.contract_address, ZERO_ADDRESS, *TOKEN]], - [1, erc721.contract_address, 'Transfer', [ - account.contract_address, ZERO_ADDRESS, *TOKEN]] - ] - ) - - - @pytest.mark.asyncio - async def test_burn_nonexistent_token(self, erc721_minted): - erc721, account, *_ = erc721_minted - - await assert_revert(signer.send_transaction( - account, erc721.contract_address, 'burn', [ - *NONEXISTENT_TOKEN - ]), - reverted_with="ERC721: owner query for nonexistent token" - ) - - - @pytest.mark.asyncio - async def test_burn_unowned_token(self, erc721_minted): - erc721, account, other, *_ = erc721_minted - - # other should not be able to burn account's token - await assert_revert( - signer.send_transaction( - other, erc721.contract_address, 'burn', [*TOKEN] - ), - reverted_with="ERC721: caller is not the token owner" - ) - - # account can burn their own token - await signer.send_transaction( - account, erc721.contract_address, 'burn', [*TOKEN] - ) - - - @pytest.mark.asyncio - async def test_burn_from_zero_address(self, erc721_minted): - erc721, *_ = erc721_minted - - await assert_revert( - erc721.burn(TOKEN).execute(), - reverted_with="ERC721: caller is not the token owner" - ) diff --git a/tests/token/erc721/test_ERC721MintableBurnable.py b/tests/token/erc721/test_ERC721MintableBurnable.py deleted file mode 100644 index d14251be4..000000000 --- a/tests/token/erc721/test_ERC721MintableBurnable.py +++ /dev/null @@ -1,279 +0,0 @@ -import pytest -from signers import MockSigner -from nile.utils import ZERO_ADDRESS, assert_revert, to_uint, sub_uint -from utils import ( - get_contract_class, cached_contract, assert_event_emitted, - assert_events_emitted, State, Account -) -from ERC721BaseSuite import ( - ERC721Base, NAME, SYMBOL, NONEXISTENT_TOKEN, TOKENS, TOKEN -) -from access.OwnableBaseSuite import OwnableBase - - -signer = MockSigner(123456789987654321) - - -@pytest.fixture(scope='module') -def contract_classes(): - account_cls = Account.get_class - erc721_cls = get_contract_class('ERC721MintableBurnable') - erc721_holder_cls = get_contract_class('ERC721Holder') - unsupported_cls = get_contract_class('Initializable') - - return account_cls, erc721_cls, erc721_holder_cls, unsupported_cls - - -@pytest.fixture(scope='module') -async def erc721_init(contract_classes): - _, erc721_cls, erc721_holder_cls, unsupported_cls = contract_classes - starknet = await State.init() - account1 = await Account.deploy(signer.public_key) - account2 = await Account.deploy(signer.public_key) - erc721 = await starknet.deploy( - contract_class=erc721_cls, - constructor_calldata=[ - NAME, # name - SYMBOL, # symbol - account1.contract_address # owner - ] - ) - erc721_holder = await starknet.deploy( - contract_class=erc721_holder_cls, - constructor_calldata=[] - ) - unsupported = await starknet.deploy( - contract_class=unsupported_cls, - constructor_calldata=[] - ) - return ( - starknet.state, - account1, - account2, - erc721, - erc721_holder, - unsupported - ) - - -@pytest.fixture -def contract_factory(contract_classes, erc721_init): - account_cls, erc721_cls, erc721_holder_cls, unsupported_cls = contract_classes - state, account1, account2, erc721, erc721_holder, unsupported = erc721_init - _state = state.copy() - account1 = cached_contract(_state, account_cls, account1) - account2 = cached_contract(_state, account_cls, account2) - erc721 = cached_contract(_state, erc721_cls, erc721) - erc721_holder = cached_contract(_state, erc721_holder_cls, erc721_holder) - unsupported = cached_contract(_state, unsupported_cls, unsupported) - - return erc721, account1, account2, erc721_holder, unsupported - - -# Note that depending on what's being tested, test cases alternate between -# accepting `erc721_minted` and `contract_factory` fixtures -@pytest.fixture -async def erc721_minted(contract_factory): - erc721, account, account2, erc721_holder, unsupported = contract_factory - # mint tokens to account - for token in TOKENS: - await signer.send_transaction( - account, erc721.contract_address, 'mint', [ - account.contract_address, *token] - ) - - return erc721, account, account2, erc721_holder, unsupported - - -class TestERC721MintableBurnable(ERC721Base, OwnableBase): - # - # mint - # - - @pytest.mark.asyncio - async def test_mint_emits_event(self, contract_factory): - erc721, account, _, _, _ = contract_factory - - # mint token to account - tx_exec_info = await signer.send_transaction( - account, erc721.contract_address, 'mint', [ - account.contract_address, *TOKEN] - ) - - assert_event_emitted( - tx_exec_info, - from_address=erc721.contract_address, - name='Transfer', - data=[ - ZERO_ADDRESS, - account.contract_address, - *TOKEN - ] - ) - - - @pytest.mark.asyncio - async def test_mint(self, erc721_minted): - erc721, account, *_ = erc721_minted - - # checks balance - execution_info = await erc721.balanceOf(account.contract_address).execute() - assert execution_info.result == (to_uint(2),) - - # checks that account owns correct tokens - for token in TOKENS: - execution_info = await erc721.ownerOf(token).execute() - assert execution_info.result == (account.contract_address,) - - - @pytest.mark.asyncio - async def test_mint_duplicate_token_id(self, erc721_minted): - erc721, account, *_ = erc721_minted - - # minting duplicate token_id should fail - await assert_revert(signer.send_transaction( - account, erc721.contract_address, 'mint', [ - account.contract_address, - *TOKEN - ]), - reverted_with="ERC721: token already minted" - ) - - - @pytest.mark.asyncio - async def test_mint_to_zero_address(self, erc721_minted): - erc721, account, *_ = erc721_minted - - # minting to zero address should fail - await assert_revert(signer.send_transaction( - account, erc721.contract_address, 'mint', [ - ZERO_ADDRESS, - *NONEXISTENT_TOKEN - ]), - reverted_with="ERC721: cannot mint to the zero address" - ) - - - @pytest.mark.asyncio - async def test_mint_approve_should_be_zero_address(self, erc721_minted): - erc721, *_ = erc721_minted - - # approved address should be zero for newly minted tokens - for token in TOKENS: - execution_info = await erc721.getApproved(token).execute() - assert execution_info.result == (0,) - - - @pytest.mark.asyncio - async def test_mint_by_not_owner(self, contract_factory): - erc721, _, not_owner, _, _ = contract_factory - - # minting from not_owner should fail - await assert_revert(signer.send_transaction( - not_owner, erc721.contract_address, 'mint', [ - not_owner.contract_address, - *TOKENS[0] - ]), - reverted_with="Ownable: caller is not the owner" - ) - - - # - # burn - # - - - @pytest.mark.asyncio - async def test_burn(self, erc721_minted): - erc721, account, *_ = erc721_minted - - execution_info = await erc721.balanceOf(account.contract_address).execute() - previous_balance = execution_info.result.balance - - # burn token - await signer.send_transaction( - account, erc721.contract_address, 'burn', [*TOKEN] - ) - - # account balance should subtract one - execution_info = await erc721.balanceOf(account.contract_address).execute() - assert execution_info.result.balance == sub_uint( - previous_balance, to_uint(1) - ) - - # approve should be cleared to zero, therefore, - # 'getApproved()' call should fail - await assert_revert( - erc721.getApproved(TOKEN).execute(), - reverted_with="ERC721: approved query for nonexistent token" - ) - - # 'token_to_burn' owner should be zero; therefore, - # 'ownerOf()' call should fail - await assert_revert( - erc721.ownerOf(TOKEN).execute(), - reverted_with="ERC721: owner query for nonexistent token" - ) - - - @pytest.mark.asyncio - async def test_burn_emits_event(self, erc721_minted): - erc721, account, *_ = erc721_minted - - # mint token to account - tx_exec_info = await signer.send_transaction( - account, erc721.contract_address, 'burn', [ - *TOKEN - ] - ) - - # events - assert_events_emitted( - tx_exec_info, - [ - [0, erc721.contract_address, 'Approval', [ - account.contract_address, ZERO_ADDRESS, *TOKEN]], - [1, erc721.contract_address, 'Transfer', [ - account.contract_address, ZERO_ADDRESS, *TOKEN]] - ] - ) - - - @pytest.mark.asyncio - async def test_burn_nonexistent_token(self, erc721_minted): - erc721, account, *_ = erc721_minted - - await assert_revert(signer.send_transaction( - account, erc721.contract_address, 'burn', [ - *NONEXISTENT_TOKEN - ]), - reverted_with="ERC721: owner query for nonexistent token" - ) - - - @pytest.mark.asyncio - async def test_burn_unowned_token(self, erc721_minted): - erc721, account, other, *_ = erc721_minted - - # other should not be able to burn account's token - await assert_revert( - signer.send_transaction( - other, erc721.contract_address, 'burn', [*TOKEN] - ), - reverted_with="ERC721: caller is not the token owner" - ) - - # account can burn their own token - await signer.send_transaction( - account, erc721.contract_address, 'burn', [*TOKEN] - ) - - - @pytest.mark.asyncio - async def test_burn_from_zero_address(self, erc721_minted): - erc721, *_ = erc721_minted - - await assert_revert( - erc721.burn(TOKEN).execute(), - reverted_with="ERC721: caller is not the token owner" - ) diff --git a/tests/token/erc721/test_ERC721MintablePausable.py b/tests/token/erc721/test_ERC721MintablePausable.py deleted file mode 100644 index 74f06cca8..000000000 --- a/tests/token/erc721/test_ERC721MintablePausable.py +++ /dev/null @@ -1,314 +0,0 @@ -import pytest -from signers import MockSigner -from nile.utils import TRUE, FALSE, ZERO_ADDRESS, assert_revert, to_uint -from utils import ( - get_contract_class, cached_contract, assert_event_emitted, State, Account -) -from ERC721BaseSuite import ( - ERC721Base, NAME, SYMBOL, TOKENS, TOKEN, NONEXISTENT_TOKEN, DATA -) -from access.OwnableBaseSuite import OwnableBase - - -signer = MockSigner(123456789987654321) - -# testing vars -TOKEN_TO_MINT = to_uint(33) - - -@pytest.fixture(scope='module') -def contract_classes(): - account_cls = Account.get_class - erc721_cls = get_contract_class('ERC721MintablePausable') - erc721_holder_cls = get_contract_class('ERC721Holder') - unsupported_cls = get_contract_class('Initializable') - - return account_cls, erc721_cls, erc721_holder_cls, unsupported_cls - - -@pytest.fixture(scope='module') -async def erc721_init(contract_classes): - _, erc721_cls, erc721_holder_cls, unsupported_cls = contract_classes - starknet = await State.init() - account1 = await Account.deploy(signer.public_key) - account2 = await Account.deploy(signer.public_key) - erc721 = await starknet.deploy( - contract_class=erc721_cls, - constructor_calldata=[ - NAME, # name - SYMBOL, # ticker - account1.contract_address # owner - ] - ) - erc721_holder = await starknet.deploy( - contract_class=erc721_holder_cls, - constructor_calldata=[] - ) - unsupported = await starknet.deploy( - contract_class=unsupported_cls, - constructor_calldata=[] - ) - return ( - starknet.state, - account1, - account2, - erc721, - erc721_holder, - unsupported, - ) - - -@pytest.fixture -def contract_factory(contract_classes, erc721_init): - account_cls, erc721_cls, erc721_holder_cls, unsupported_cls = contract_classes - state, account1, account2, erc721, erc721_holder, unsupported = erc721_init - _state = state.copy() - account1 = cached_contract(_state, account_cls, account1) - account2 = cached_contract(_state, account_cls, account2) - erc721 = cached_contract(_state, erc721_cls, erc721) - erc721_holder = cached_contract(_state, erc721_holder_cls, erc721_holder) - unsupported = cached_contract(_state, unsupported_cls, unsupported) - - return erc721, account1, account2, erc721_holder, unsupported - - -@pytest.fixture -async def erc721_minted(contract_factory): - erc721, account, account2, erc721_holder, unsupported = contract_factory - # mint tokens to account - for token in TOKENS: - await signer.send_transaction( - account, erc721.contract_address, 'mint', [ - account.contract_address, *token] - ) - - return erc721, account, account2, erc721_holder, unsupported - - -class TestERC721MintablePausable(ERC721Base, OwnableBase): - # - # mint - # - - @pytest.mark.asyncio - async def test_mint_emits_event(self, contract_factory): - erc721, account, _, _, _ = contract_factory - - # mint token to account - tx_exec_info = await signer.send_transaction( - account, erc721.contract_address, 'mint', [ - account.contract_address, *TOKEN] - ) - - assert_event_emitted( - tx_exec_info, - from_address=erc721.contract_address, - name='Transfer', - data=[ - ZERO_ADDRESS, - account.contract_address, - *TOKEN - ] - ) - - - @pytest.mark.asyncio - async def test_mint(self, erc721_minted): - erc721, account, *_ = erc721_minted - - # checks balance - execution_info = await erc721.balanceOf(account.contract_address).execute() - assert execution_info.result == (to_uint(2),) - - # checks that account owns correct tokens - for token in TOKENS: - execution_info = await erc721.ownerOf(token).execute() - assert execution_info.result == (account.contract_address,) - - - @pytest.mark.asyncio - async def test_mint_duplicate_token_id(self, erc721_minted): - erc721, account, *_ = erc721_minted - - # minting duplicate token_id should fail - await assert_revert(signer.send_transaction( - account, erc721.contract_address, 'mint', [ - account.contract_address, - *TOKEN - ]), - reverted_with="ERC721: token already minted" - ) - - - @pytest.mark.asyncio - async def test_mint_to_zero_address(self, erc721_minted): - erc721, account, *_ = erc721_minted - - # minting to zero address should fail - await assert_revert(signer.send_transaction( - account, erc721.contract_address, 'mint', [ - ZERO_ADDRESS, - *NONEXISTENT_TOKEN - ]), - reverted_with="ERC721: cannot mint to the zero address" - ) - - - @pytest.mark.asyncio - async def test_mint_approve_should_be_zero_address(self, erc721_minted): - erc721, *_ = erc721_minted - - # approved address should be zero for newly minted tokens - for token in TOKENS: - execution_info = await erc721.getApproved(token).execute() - assert execution_info.result == (0,) - - - @pytest.mark.asyncio - async def test_mint_by_not_owner(self, contract_factory): - erc721, _, not_owner, _, _ = contract_factory - - # minting from not_owner should fail - await assert_revert(signer.send_transaction( - not_owner, erc721.contract_address, 'mint', [ - not_owner.contract_address, - *TOKENS[0] - ]), - reverted_with="Ownable: caller is not the owner" - ) - - # - # pause - # - - @pytest.mark.asyncio - async def test_pause(self, erc721_minted): - erc721, owner, other, erc721_holder, _ = erc721_minted - - # pause - await signer.send_transaction(owner, erc721.contract_address, 'pause', []) - - execution_info = await erc721.paused().execute() - assert execution_info.result.paused == TRUE - - await assert_revert(signer.send_transaction( - owner, erc721.contract_address, 'approve', [ - other.contract_address, - *TOKENS[0] - ]), - reverted_with="Pausable: paused" - ) - - await assert_revert(signer.send_transaction( - owner, erc721.contract_address, 'setApprovalForAll', [ - other.contract_address, - TRUE - ]), - reverted_with="Pausable: paused" - ) - - await assert_revert(signer.send_transaction( - owner, erc721.contract_address, 'transferFrom', [ - owner.contract_address, - other.contract_address, - *TOKENS[0] - ]), - reverted_with="Pausable: paused" - ) - - await assert_revert(signer.send_transaction( - owner, erc721.contract_address, 'safeTransferFrom', [ - owner.contract_address, - erc721_holder.contract_address, - *TOKENS[1], - len(DATA), - *DATA - ]), - reverted_with="Pausable: paused" - ) - - await assert_revert(signer.send_transaction( - owner, erc721.contract_address, 'mint', [ - other.contract_address, - *TOKEN_TO_MINT - ]), - reverted_with="Pausable: paused" - ) - - - @pytest.mark.asyncio - async def test_unpause(self, erc721_minted): - erc721, owner, other, erc721_holder, _ = erc721_minted - - # pause - await signer.send_transaction(owner, erc721.contract_address, 'pause', []) - - # unpause - await signer.send_transaction(owner, erc721.contract_address, 'unpause', []) - - execution_info = await erc721.paused().execute() - assert execution_info.result.paused == FALSE - - await signer.send_transaction( - owner, erc721.contract_address, 'approve', [ - other.contract_address, - *TOKENS[0] - ] - ) - - await signer.send_transaction( - owner, erc721.contract_address, 'setApprovalForAll', [ - other.contract_address, - TRUE - ] - ) - - await signer.send_transaction( - owner, erc721.contract_address, 'transferFrom', [ - owner.contract_address, - other.contract_address, - *TOKENS[0] - ] - ) - - await signer.send_transaction( - other, erc721.contract_address, 'safeTransferFrom', [ - owner.contract_address, - erc721_holder.contract_address, - *TOKENS[1], - len(DATA), - *DATA - ] - ) - - await signer.send_transaction( - owner, erc721.contract_address, 'mint', [ - other.contract_address, - *TOKEN_TO_MINT - ] - ) - - - @pytest.mark.asyncio - async def test_only_owner(self, erc721_minted): - erc721, owner, other, *_ = erc721_minted - - # not-owner pause should revert - await assert_revert( - signer.send_transaction( - other, erc721.contract_address, 'pause', []), - reverted_with="Ownable: caller is not the owner" - ) - - # owner pause - await signer.send_transaction(owner, erc721.contract_address, 'pause', []) - - # not-owner unpause should revert - await assert_revert( - signer.send_transaction( - other, erc721.contract_address, 'unpause', []), - reverted_with="Ownable: caller is not the owner" - ) - - # owner unpause - await signer.send_transaction(owner, erc721.contract_address, 'unpause', []) diff --git a/tests/token/erc721/test_ERC721SafeMintableMock.py b/tests/token/erc721/test_ERC721SafeMintableMock.py deleted file mode 100644 index 6b9881d1b..000000000 --- a/tests/token/erc721/test_ERC721SafeMintableMock.py +++ /dev/null @@ -1,231 +0,0 @@ -import pytest -from signers import MockSigner -from nile.utils import ( - ZERO_ADDRESS, INVALID_UINT256, assert_revert, to_uint -) -from utils import ( - get_contract_class, cached_contract, assert_event_emitted, State, Account -) -from ERC721BaseSuite import ERC721Base, NAME, SYMBOL, DATA, TOKEN, TOKENS -from access.OwnableBaseSuite import OwnableBase - - -signer = MockSigner(123456789987654321) - - -@pytest.fixture(scope='module') -def contract_classes(): - account_cls = Account.get_class - erc721_cls = get_contract_class('ERC721SafeMintableMock') - erc721_holder_cls = get_contract_class('ERC721Holder') - unsupported_cls = get_contract_class('Initializable') - - return account_cls, erc721_cls, erc721_holder_cls, unsupported_cls - - -@pytest.fixture(scope='module') -async def erc721_init(contract_classes): - account_cls, erc721_cls, erc721_holder_cls, unsupported_cls = contract_classes - starknet = await State.init() - account1 = await Account.deploy(signer.public_key) - account2 = await Account.deploy(signer.public_key) - erc721 = await starknet.deploy( - contract_class=erc721_cls, - constructor_calldata=[ - NAME, # name - SYMBOL, # ticker - account1.contract_address # owner - ] - ) - erc721_holder = await starknet.deploy( - contract_class=erc721_holder_cls, - constructor_calldata=[] - ) - unsupported = await starknet.deploy( - contract_class=unsupported_cls, - constructor_calldata=[] - ) - return ( - starknet.state, - account1, - account2, - erc721, - erc721_holder, - unsupported - ) - - -@pytest.fixture -def contract_factory(contract_classes, erc721_init): - account_cls, erc721_cls, erc721_holder_cls, unsupported_cls = contract_classes - state, account1, account2, erc721, erc721_holder, unsupported = erc721_init - _state = state.copy() - account1 = cached_contract(_state, account_cls, account1) - account2 = cached_contract(_state, account_cls, account2) - erc721 = cached_contract(_state, erc721_cls, erc721) - erc721_holder = cached_contract(_state, erc721_holder_cls, erc721_holder) - unsupported = cached_contract(_state, unsupported_cls, unsupported) - - return erc721, account1, account2, erc721_holder, unsupported - - -@pytest.fixture -async def erc721_minted(contract_factory): - erc721, account, account2, erc721_holder, unsupported = contract_factory - # mint tokens to account - for token in TOKENS: - await signer.send_transaction( - account, erc721.contract_address, 'mint', [ - account.contract_address, *token] - ) - - return erc721, account, account2, erc721_holder, unsupported - - -class TestERC721SafeMintableMock(ERC721Base, OwnableBase): - # - # safeMint - # - - @pytest.mark.asyncio - async def test_safeMint_to_erc721_supported_contract(self, contract_factory): - erc721, account, _, erc721_holder, _ = contract_factory - - await signer.send_transaction( - account, erc721.contract_address, 'safeMint', [ - erc721_holder.contract_address, - *TOKEN, - len(DATA), - *DATA - ] - ) - - # check balance - execution_info = await erc721.balanceOf(erc721_holder.contract_address).call() - assert execution_info.result == (to_uint(1),) - - # check owner - execution_info = await erc721.ownerOf(TOKEN).call() - assert execution_info.result == (erc721_holder.contract_address,) - - - @pytest.mark.asyncio - async def test_safeMint_emits_event(self, contract_factory): - erc721, account, _, erc721_holder, _ = contract_factory - - tx_exec_info = await signer.send_transaction( - account, erc721.contract_address, 'safeMint', [ - erc721_holder.contract_address, - *TOKEN, - len(DATA), - *DATA - ] - ) - - assert_event_emitted( - tx_exec_info, - from_address=erc721.contract_address, - name='Transfer', - data=[ - ZERO_ADDRESS, - erc721_holder.contract_address, - *TOKEN - ] - ) - - - @pytest.mark.asyncio - async def test_safeMint_to_account(self, contract_factory): - erc721, account, recipient, _, _ = contract_factory - - await signer.send_transaction( - account, erc721.contract_address, 'safeMint', [ - recipient.contract_address, - *TOKEN, - len(DATA), - *DATA - ] - ) - - # check balance - execution_info = await erc721.balanceOf(recipient.contract_address).call() - assert execution_info.result == (to_uint(1),) - - # check owner - execution_info = await erc721.ownerOf(TOKEN).call() - assert execution_info.result == (recipient.contract_address,) - - - @pytest.mark.asyncio - async def test_safeMint_to_zero_address(self, contract_factory): - erc721, account, _, _, _ = contract_factory - - # to zero address should be rejected - await assert_revert(signer.send_transaction( - account, erc721.contract_address, 'safeMint', [ - ZERO_ADDRESS, - *TOKEN, - len(DATA), - *DATA - ]), - reverted_with="ERC721: cannot mint to the zero address" - ) - - - @pytest.mark.asyncio - async def test_safeMint_from_zero_address(self, contract_factory): - erc721, _, _, erc721_holder, _ = contract_factory - - # Caller address is `0` when not using an account contract - await assert_revert( - erc721.safeMint( - erc721_holder.contract_address, - TOKEN, - DATA - ).execute(), - reverted_with="Ownable: caller is the zero address" - ) - - - @pytest.mark.asyncio - async def test_safeMint_from_not_owner(self, contract_factory): - erc721, _, other, erc721_holder, _ = contract_factory - - await assert_revert(signer.send_transaction( - other, erc721.contract_address, 'safeMint', [ - erc721_holder.contract_address, - *TOKEN, - len(DATA), - *DATA - ]), - reverted_with="Ownable: caller is not the owner" - ) - - - @pytest.mark.asyncio - async def test_safeMint_to_unsupported_contract(self, contract_factory): - erc721, account, _, _, unsupported = contract_factory - - await assert_revert(signer.send_transaction( - account, erc721.contract_address, 'safeMint', [ - unsupported.contract_address, - *TOKEN, - len(DATA), - *DATA - ]) - ) - - - @pytest.mark.asyncio - async def test_safeMint_invalid_uint256(self, contract_factory): - erc721, account, recipient, _, _ = contract_factory - - await assert_revert(signer.send_transaction( - account, erc721.contract_address, 'safeMint', [ - recipient.contract_address, - *INVALID_UINT256, - len(DATA), - *DATA - ]), - reverted_with="ERC721: token_id is not a valid Uint256" - ) diff --git a/tests/upgrades/test_Proxy.py b/tests/upgrades/test_Proxy.py deleted file mode 100644 index 911bd818b..000000000 --- a/tests/upgrades/test_Proxy.py +++ /dev/null @@ -1,167 +0,0 @@ -import pytest -from starkware.starknet.public.abi import get_selector_from_name -from signers import MockSigner -from nile.utils import ( - assert_revert, assert_revert_entry_point -) -from utils import ( - get_contract_class, - cached_contract, - assert_event_emitted, - State, - Account -) - -# random value -VALUE = 123 - -signer = MockSigner(123456789987654321) - -class TestProxy: - @pytest.fixture(scope='module') - def contract_classes(self): - account_cls = Account.get_class - implementation_cls = get_contract_class('ProxiableImplementation') - proxy_cls = get_contract_class('Proxy') - - return account_cls, implementation_cls, proxy_cls - - - @pytest.fixture(scope='module') - async def proxy_init(self, contract_classes): - account_cls, implementation_cls, proxy_cls = contract_classes - starknet = await State.init() - account1 = await Account.deploy(signer.public_key) - account2 = await Account.deploy(signer.public_key) - implementation_decl = await starknet.declare( - contract_class=implementation_cls - ) - selector = get_selector_from_name('initializer') - params = [ - account1.contract_address # admin account - ] - proxy = await starknet.deploy( - contract_class=proxy_cls, - constructor_calldata=[ - implementation_decl.class_hash, - selector, - len(params), - *params - ] - ) - return ( - starknet.state, - account1, - account2, - proxy - ) - - - @pytest.fixture - def proxy_factory(self, contract_classes, proxy_init): - account_cls, _, proxy_cls = contract_classes - state, account1, account2, proxy = proxy_init - _state = state.copy() - admin = cached_contract(_state, account_cls, account1) - other = cached_contract(_state, account_cls, account2) - proxy = cached_contract(_state, proxy_cls, proxy) - - return admin, other, proxy - - - # - # initializer - # - - @pytest.mark.asyncio - async def test_initializer(self, proxy_factory): - admin, _, proxy = proxy_factory - - # check admin is set - execution_info = await signer.send_transaction( - admin, proxy.contract_address, 'getAdmin', [] - ) - assert execution_info.call_info.retdata[1] == admin.contract_address - - - @pytest.mark.asyncio - async def test_initializer_after_initialized(self, proxy_factory): - admin, _, proxy = proxy_factory - - await assert_revert(signer.send_transaction( - admin, proxy.contract_address, 'initializer', [admin.contract_address]), - reverted_with="Proxy: contract already initialized" - ) - - # - # set_admin - # - - @pytest.mark.asyncio - async def test_set_admin(self, proxy_factory): - admin, _, proxy = proxy_factory - - # set admin - tx_exec_info = await signer.send_transaction( - admin, proxy.contract_address, 'setAdmin', [VALUE] - ) - - # check event - assert_event_emitted( - tx_exec_info, - from_address=proxy.contract_address, - name='AdminChanged', - data=[ - admin.contract_address, # old admin - VALUE # new admin - ] - ) - - # check new admin - execution_info = await signer.send_transaction( - admin, proxy.contract_address, 'getAdmin', [] - ) - assert execution_info.call_info.retdata[1] == VALUE - - - @pytest.mark.asyncio - async def test_set_admin_from_unauthorized(self, proxy_factory): - _, non_admin, proxy = proxy_factory - - # set admin - await assert_revert(signer.send_transaction( - non_admin, proxy.contract_address, 'setAdmin', [VALUE]), - reverted_with="Proxy: caller is not admin" - ) - - # - # fallback function - # - - @pytest.mark.asyncio - async def test_default_fallback(self, proxy_factory): - admin, _, proxy = proxy_factory - - # set value through proxy - await signer.send_transaction( - admin, proxy.contract_address, 'setValue', [VALUE] - ) - - # get value through proxy - execution_info = await signer.send_transaction( - admin, proxy.contract_address, 'getValue', [] - ) - assert execution_info.call_info.retdata[1] == VALUE - - - @pytest.mark.asyncio - async def test_fallback_when_selector_does_not_exist(self, proxy_factory): - admin, _, proxy = proxy_factory - - # should fail with entry point error - await assert_revert_entry_point( - signer.send_transaction( - admin, proxy.contract_address, 'invalid_selector', [] - ), - invalid_selector='invalid_selector' - ) diff --git a/tests/upgrades/test_upgrades.py b/tests/upgrades/test_upgrades.py deleted file mode 100644 index 75e9c7b20..000000000 --- a/tests/upgrades/test_upgrades.py +++ /dev/null @@ -1,368 +0,0 @@ -import pytest -from signers import MockSigner -from starkware.starknet.public.abi import get_selector_from_name -from nile.utils import assert_revert, assert_revert_entry_point, FALSE, TRUE -from utils import ( - State, - Account, - assert_event_emitted, - get_contract_class, - cached_contract, -) - - -# random value -VALUE_1 = 123 -VALUE_2 = 987 - -signer = MockSigner(123456789987654321) - - -class TestUpgrades: - @pytest.fixture(scope='module') - def contract_classes(self): - account_cls = Account.get_class - v1_cls = get_contract_class('UpgradesMockV1') - v2_cls = get_contract_class('UpgradesMockV2') - proxy_cls = get_contract_class('Proxy') - - return account_cls, v1_cls, v2_cls, proxy_cls - - - @pytest.fixture(scope='module') - async def implementations_declare(self, contract_classes): - _, v1_cls, v2_cls, proxy_cls = contract_classes - starknet = await State.init() - account1 = await Account.deploy(signer.public_key) - account2 = await Account.deploy(signer.public_key) - - v1_decl = await starknet.declare( - contract_class=v1_cls, - ) - v2_decl = await starknet.declare( - contract_class=v2_cls, - ) - - return ( - starknet, - account1, - account2, - v1_decl, - v2_decl, - proxy_cls - ) - - - @pytest.fixture(scope='module') - async def proxy_deploy(self, implementations_declare): - starknet, _, _, v1_decl, _, proxy_cls = implementations_declare - - # with selector set to 0, internal initialization call must be ignored - proxy = await starknet.deploy( - contract_class=proxy_cls, - constructor_calldata=[ - v1_decl.class_hash, - 0, - 0, - *[] - ] - ) - return proxy - - - @pytest.fixture(scope='module') - async def proxy_init(self, implementations_declare): - starknet, account1, account2, v1_decl, v2_decl, proxy_cls = implementations_declare - - selector = get_selector_from_name('initializer') - params = [ - account1.contract_address # admin account - ] - proxy = await starknet.deploy( - contract_class=proxy_cls, - constructor_calldata=[ - v1_decl.class_hash, - selector, - len(params), - *params - ] - ) - return ( - starknet.state, - account1, - account2, - v1_decl, - v2_decl, - proxy - ) - - - @pytest.fixture - def proxy_factory(self, contract_classes, proxy_init, proxy_deploy): - account_cls, _, _, proxy_cls = contract_classes - state, account1, account2, v1_decl, v2_decl, proxy = proxy_init - non_initialized_proxy = proxy_deploy - - _state = state.copy() - account1 = cached_contract(_state, account_cls, account1) - account2 = cached_contract(_state, account_cls, account2) - proxy = cached_contract(_state, proxy_cls, proxy) - - return account1, account2, proxy, non_initialized_proxy, v1_decl, v2_decl - - - @pytest.fixture - async def after_upgrade(self, proxy_factory): - admin, other, proxy, _, v1_decl, v2_decl = proxy_factory - - # set value, and upgrade to v2 - await signer.send_transactions( - admin, - [ - (proxy.contract_address, 'setValue1', [VALUE_1]), - (proxy.contract_address, 'upgrade', [v2_decl.class_hash]) - ] - ) - - return admin, other, proxy, v1_decl, v2_decl - - - @pytest.mark.asyncio - async def test_deployment_without_initialization(self, proxy_factory): - admin, _, _, proxy, *_ = proxy_factory - - # assert not initialized yet - execution_info = await signer.send_transaction( - admin, proxy.contract_address, 'initialized', [] - ) - assert execution_info.call_info.retdata[1] == FALSE - - # initialize - await signer.send_transaction( - admin, proxy.contract_address, 'initializer', [admin.contract_address], - ) - - # assert initialized - execution_info = await signer.send_transaction( - admin, proxy.contract_address, 'initialized', [] - ) - assert execution_info.call_info.retdata[1] == TRUE - - - @pytest.mark.asyncio - async def test_initializer_already_initialized(self, proxy_factory): - admin, _, proxy, *_ = proxy_factory - - await assert_revert( - signer.send_transaction( - admin, proxy.contract_address, 'initializer', [ - admin.contract_address - ] - ), - reverted_with='Proxy: contract already initialized' - ) - - - @pytest.mark.asyncio - async def test_upgrade(self, proxy_factory): - admin, _, proxy, _, _, v2_decl = proxy_factory - - # set value - await signer.send_transactions( - admin, - [ - (proxy.contract_address, 'setValue1', [VALUE_1]), - ] - ) - - # check value - execution_info = await signer.send_transaction( - admin, proxy.contract_address, 'getValue1', [] - ) - assert execution_info.call_info.retdata[1] == VALUE_1 - - # upgrade - await signer.send_transaction( - admin, proxy.contract_address, 'upgrade', [ - v2_decl.class_hash - ] - ) - - # check value - execution_info = await signer.send_transaction( - admin, proxy.contract_address, 'getValue1', [] - ) - assert execution_info.call_info.retdata[1] == VALUE_1 - - - @pytest.mark.asyncio - async def test_upgrade_event(self, proxy_factory): - admin, _, proxy, _, _, v2_decl = proxy_factory - - # upgrade - tx_exec_info = await signer.send_transaction( - admin, proxy.contract_address, 'upgrade', [ - v2_decl.class_hash - ] - ) - - # check event - assert_event_emitted( - tx_exec_info, - from_address=proxy.contract_address, - name='Upgraded', - data=[ - v2_decl.class_hash # new class hash - ] - ) - - - @pytest.mark.asyncio - async def test_upgrade_from_non_admin(self, proxy_factory): - _, non_admin, proxy, _, _, v2_decl = proxy_factory - - # upgrade should revert - await assert_revert( - signer.send_transaction( - non_admin, proxy.contract_address, 'upgrade', [ - v2_decl.class_hash - ] - ), - reverted_with="Proxy: caller is not admin" - ) - - - @pytest.mark.asyncio - async def test_set_implementation_as_zero(self, proxy_factory): - admin, _, proxy, *_ = proxy_factory - - # upgrade should revert - await assert_revert( - signer.send_transaction( - admin, proxy.contract_address, 'upgrade', [0] - ), - reverted_with="Proxy: implementation hash cannot be zero" - ) - - @pytest.mark.asyncio - async def test_implementation_v2(self, after_upgrade): - admin, _, proxy, _, v2_decl = after_upgrade - - execution_info = await signer.send_transactions( - admin, - [ - (proxy.contract_address, 'getImplementationHash', []), - (proxy.contract_address, 'getAdmin', []), - (proxy.contract_address, 'getValue1', []) - ] - ) - - expected = [ - 3, # number of return values - v2_decl.class_hash, # getImplementationHash - admin.contract_address, # getAdmin - VALUE_1 # getValue1 - ] - - assert execution_info.call_info.retdata == expected - - # - # v2 functions - # - - @pytest.mark.asyncio - async def test_set_admin(self, after_upgrade): - admin, new_admin, proxy, *_ = after_upgrade - - # change admin - await signer.send_transaction( - admin, proxy.contract_address, 'setAdmin', [ - new_admin.contract_address - ] - ) - - # check admin - execution_info = await signer.send_transaction( - admin, proxy.contract_address, 'getAdmin', [] - ) - assert execution_info.call_info.retdata[1] == new_admin.contract_address - - - @pytest.mark.asyncio - async def test_set_admin_from_non_admin(self, after_upgrade): - _, non_admin, proxy, *_ = after_upgrade - - # should revert - await assert_revert(signer.send_transaction( - non_admin, proxy.contract_address, 'setAdmin', [non_admin.contract_address]), - reverted_with="Proxy: caller is not admin" - ) - - - @pytest.mark.asyncio - async def test_v2_functions_pre_and_post_upgrade(self, proxy_factory): - admin, new_admin, proxy, _, _, v2_decl = proxy_factory - - # check getValue2 doesn't exist - await assert_revert_entry_point( - signer.send_transaction( - admin, proxy.contract_address, 'getValue2', [] - ), - invalid_selector='getValue2' - ) - - # check setValue2 doesn't exist in v1 - await assert_revert_entry_point( - signer.send_transaction( - admin, proxy.contract_address, 'setValue2', [VALUE_2] - ), - invalid_selector='setValue2' - ) - - # check getAdmin doesn't exist in v1 - await assert_revert_entry_point( - signer.send_transaction( - admin, proxy.contract_address, 'getAdmin', [] - ), - invalid_selector='getAdmin' - ) - - # check setAdmin doesn't exist in v1 - await assert_revert_entry_point( - signer.send_transaction( - admin, proxy.contract_address, 'setAdmin', [new_admin.contract_address] - ), - invalid_selector='setAdmin' - ) - - # upgrade - await signer.send_transaction( - admin, proxy.contract_address, 'upgrade', [ - v2_decl.class_hash - ] - ) - - # set value 2 and admin - await signer.send_transactions( - admin, - [ - (proxy.contract_address, 'setValue2', [VALUE_2]), - (proxy.contract_address, 'setAdmin', [new_admin.contract_address]) - ] - ) - - # check value 2 and admin - execution_info = await signer.send_transactions( - admin, - [ - (proxy.contract_address, 'getValue2', []), - (proxy.contract_address, 'getAdmin', []) - ] - ) - - expected = [ - 2, # number of return values - VALUE_2, # getValue2 - new_admin.contract_address # getAdmin - ] - assert execution_info.call_info.retdata == expected diff --git a/tests/utils.py b/tests/utils.py deleted file mode 100644 index e2da740d6..000000000 --- a/tests/utils.py +++ /dev/null @@ -1,154 +0,0 @@ -"""Utilities for testing Cairo contracts.""" - -import os -from pathlib import Path -from starkware.crypto.signature.fast_pedersen_hash import pedersen_hash -from starkware.starknet.core.os.class_hash import compute_class_hash -from starkware.starknet.public.abi import get_selector_from_name -from starkware.starknet.business_logic.execution.objects import OrderedEvent -from starkware.starknet.compiler.compile import compile_starknet_files -from starkware.starknet.testing.starknet import StarknetContract -from starkware.starknet.testing.starknet import Starknet - - - -MAX_UINT256 = (2**128 - 1, 2**128 - 1) -INVALID_UINT256 = (MAX_UINT256[0] + 1, MAX_UINT256[1]) -ZERO_ADDRESS = 0 -TRUE = 1 -FALSE = 0 -IACCOUNT_ID = 0xa66bd575 - -_root = Path(__file__).parent.parent - - -def get_cairo_path(): - CAIRO_PATH = os.getenv('CAIRO_PATH') - cairo_path = [] - - if CAIRO_PATH is not None: - cairo_path = [p for p in CAIRO_PATH.split(":")] - - return cairo_path - -def contract_path(name): - if name.startswith("tests/"): - return str(_root / name) - else: - return str(_root / "src" / name) - - -def assert_event_emitted(tx_exec_info, from_address, name, data, order=0): - """Assert one single event is fired with correct data.""" - assert_events_emitted(tx_exec_info, [(order, from_address, name, data)]) - - -def assert_events_emitted(tx_exec_info, events): - """Assert events are fired with correct data.""" - for event in events: - order, from_address, name, data = event - event_obj = OrderedEvent( - order=order, - keys=[get_selector_from_name(name)], - data=data, - ) - - base = tx_exec_info.call_info.internal_calls[0] - if event_obj in base.events and from_address == base.contract_address: - return - - try: - base2 = base.internal_calls[0] - if event_obj in base2.events and from_address == base2.contract_address: - return - except IndexError: - pass - - raise BaseException("Event not fired or not fired correctly") - - -def _get_path_from_name(name): - """Return the contract path by contract name.""" - dirs = ["src", "tests/mocks"] - for dir in dirs: - for (dirpath, _, filenames) in os.walk(dir): - for file in filenames: - if file == f"{name}.cairo": - return os.path.join(dirpath, file) - - raise FileNotFoundError(f"Cannot find '{name}'.") - - -def get_contract_class(contract, is_path=False): - """Return the contract class from the contract name or path""" - if is_path: - path = contract_path(contract) - else: - path = _get_path_from_name(contract) - - contract_class = compile_starknet_files( - files=[path], - debug_info=True, - cairo_path=get_cairo_path() - ) - return contract_class - - -def get_class_hash(contract_name, is_path=False): - """Return the class_hash for a given contract.""" - contract_class = get_contract_class(contract_name, is_path) - return compute_class_hash(contract_class=contract_class, hash_func=pedersen_hash) - - -def cached_contract(state, _class, deployed): - """Return the cached contract""" - contract = StarknetContract( - state=state, - abi=_class.abi, - contract_address=deployed.contract_address, - deploy_call_info=deployed.deploy_call_info - ) - return contract - - -class State: - """ - Utility helper for Account class to initialize and return StarkNet state. - - Example - --------- - Initalize StarkNet state - - >>> starknet = await State.init() - - """ - async def init(): - global starknet - starknet = await Starknet.empty() - return starknet - - -class Account: - """ - Utility for deploying Account contract. - - Parameters - ---------- - - public_key : int - - Examples - ---------- - - >>> starknet = await State.init() - >>> account = await Account.deploy(public_key) - - """ - get_class = get_contract_class("Account") - - async def deploy(public_key): - account = await starknet.deploy( - contract_class=Account.get_class, - constructor_calldata=[public_key] - ) - return account diff --git a/tests/utils/test_UniversalDeployer.py b/tests/utils/test_UniversalDeployer.py deleted file mode 100644 index 3fbfef2d9..000000000 --- a/tests/utils/test_UniversalDeployer.py +++ /dev/null @@ -1,102 +0,0 @@ -import pytest -from starkware.starknet.core.os.contract_address.contract_address import calculate_contract_address_from_hash -from starkware.crypto.signature.fast_pedersen_hash import pedersen_hash -from starkware.starknet.core.os.class_hash import compute_class_hash - -from signers import MockSigner -from utils import ( - State, - Account, - get_contract_class, - assert_event_emitted, - cached_contract, - IACCOUNT_ID, - FALSE, - TRUE, -) - -signer = MockSigner(123456789987654321) - - -@pytest.fixture(scope='module') -def contract_classes(): - account_cls = Account.get_class - deployer_cls = get_contract_class('UniversalDeployer') - - return account_cls, deployer_cls - - -@pytest.fixture(scope='module') -async def deployer_init(contract_classes): - _, deployer_cls = contract_classes - starknet = await State.init() - account = await Account.deploy(signer.public_key) - deployer = await starknet.deploy(contract_class=deployer_cls) - return ( - starknet.state, - account, - deployer - ) - - -@pytest.fixture -def deployer_factory(contract_classes, deployer_init): - account_cls, deployer_cls = contract_classes - state, account, deployer = deployer_init - _state = state.copy() - _account = cached_contract(_state, account_cls, account) - deployer = cached_contract(_state, deployer_cls, deployer) - - return _account, deployer - - -@pytest.mark.asyncio -@pytest.mark.parametrize('unique', [TRUE, FALSE]) -async def test_deployment(deployer_factory, unique): - account, deployer = deployer_factory - salt = 1234567875432 # random value - calldata = [signer.public_key] - class_hash = compute_class_hash( - contract_class=Account.get_class, hash_func=pedersen_hash) - - # deploy contract - params = [class_hash, salt, unique, len(calldata), *calldata] - deploy_exec_info = await signer.send_transaction(account, deployer.contract_address, 'deployContract', params) - deployed_address = deploy_exec_info.call_info.retdata[1] - - # check address - if unique: - actual_salt = pedersen_hash(account.contract_address, salt) - deployer_address = deployer.contract_address - else: - actual_salt = salt - deployer_address = 0 - - expected_address = calculate_contract_address_from_hash( - salt=actual_salt, - class_hash=class_hash, - constructor_calldata=calldata, - deployer_address=deployer_address - ) - - assert deployed_address == expected_address - - # check deployment - tx_exec_info = await signer.send_transaction(account, deployed_address, 'supportsInterface', [IACCOUNT_ID]) - is_account = tx_exec_info.call_info.retdata[1] - assert is_account == TRUE - - assert_event_emitted( - deploy_exec_info, - from_address=deployer.contract_address, - name='ContractDeployed', - data=[ - deployed_address, # contractAddress - account.contract_address, # deployer - unique, # unique - class_hash, # classHash - len(calldata), # calldata_len - *calldata, # calldata - salt, # salt - ] - ) diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 053a3e028..000000000 --- a/tox.ini +++ /dev/null @@ -1,63 +0,0 @@ -[tox] -minversion = 3.15 -envlist = default -isolated_build = True - -[pytest] -filterwarnings = - ignore::DeprecationWarning -addopts= -n auto -asyncio_mode = auto -markers = - only: marks a subset of tests to run (development purpose) - -[testenv] -description = Invoke pytest to run automated tests -setenv = - TOXINIDIR = {toxinidir} -passenv = - HOME - PYTHONPATH -deps = - cairo-lang==0.10.1 - cairo-nile==0.11.0 - pytest-xdist - # See https://github.com/starkware-libs/cairo-lang/issues/52 - marshmallow-dataclass==8.5.3 -extras = - testing -commands = - nile compile --directory src/ - pytest {posargs} - -[testenv:coverage] -description = Invoke nile-coverage to generate coverage report -skip_install = True -setenv = - {[testenv]setenv} - CAIRO_PATH = src -deps = - {[testenv]deps} - nile-coverage==0.2.4 -commands = - nile compile --directory src/ - nile coverage -c src {posargs} - -[testenv:build] -description = Build the package in isolation according to PEP517, see https://github.com/pypa/build -skip_install = True -changedir = {toxinidir} -deps = - build[virtualenv] - twine -commands = - python -m build . -o dist - python -m twine check --strict dist/* - -[testenv:lint] -description = Lint Markdown documents following the rules set forth here: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md -changedir = {toxinidir} -allowlist_externals = - npx -commands = - npx markdownlint-cli README.md CONTRIBUTING.md docs -i docs/node_modules From bce2506ae8f70ef810d8d299f58c665f4ec6d208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Tue, 7 Mar 2023 20:55:49 -0300 Subject: [PATCH 030/246] re-structure project --- .../account_mock.cairo => account.cairo} | 15 +++---- src/openzeppelin/account/cairo_project.toml | 2 - src/openzeppelin/account/lib.cairo | 6 --- .../account/tests/test_account.cairo | 43 ------------------- src/openzeppelin/cairo_project.toml | 2 + src/openzeppelin/introspection.cairo | 1 + .../tests/erc165mock.cairo => erc165.cairo} | 20 ++++++--- .../introspection/erc165/cairo_project.toml | 2 - .../introspection/erc165/lib.cairo | 6 --- .../introspection/erc165/tests.cairo | 2 - src/openzeppelin/lib.cairo | 3 ++ src/openzeppelin/{account => }/tests.cairo | 2 +- src/openzeppelin/tests/test_account.cairo | 18 ++++++++ .../erc165 => }/tests/test_erc165.cairo | 19 ++++---- 14 files changed, 54 insertions(+), 87 deletions(-) rename src/openzeppelin/{account/tests/account_mock.cairo => account.cairo} (78%) delete mode 100644 src/openzeppelin/account/cairo_project.toml delete mode 100644 src/openzeppelin/account/lib.cairo delete mode 100644 src/openzeppelin/account/tests/test_account.cairo create mode 100644 src/openzeppelin/cairo_project.toml create mode 100644 src/openzeppelin/introspection.cairo rename src/openzeppelin/introspection/{erc165/tests/erc165mock.cairo => erc165.cairo} (60%) delete mode 100644 src/openzeppelin/introspection/erc165/cairo_project.toml delete mode 100644 src/openzeppelin/introspection/erc165/lib.cairo delete mode 100644 src/openzeppelin/introspection/erc165/tests.cairo create mode 100644 src/openzeppelin/lib.cairo rename src/openzeppelin/{account => }/tests.cairo (50%) create mode 100644 src/openzeppelin/tests/test_account.cairo rename src/openzeppelin/{introspection/erc165 => }/tests/test_erc165.cairo (53%) diff --git a/src/openzeppelin/account/tests/account_mock.cairo b/src/openzeppelin/account.cairo similarity index 78% rename from src/openzeppelin/account/tests/account_mock.cairo rename to src/openzeppelin/account.cairo index cee549e12..e41be2080 100644 --- a/src/openzeppelin/account/tests/account_mock.cairo +++ b/src/openzeppelin/account.cairo @@ -1,6 +1,9 @@ +const ACCOUNT_ID: felt = 0x4; + #[account_contract] mod Account { - use erc165::ERC165Library; + use openzeppelin::account::ACCOUNT_ID; + use openzeppelin::introspection::erc165::ERC165Contract; use starknet::get_caller_address; use starknet::get_contract_address; @@ -10,6 +13,7 @@ mod Account { #[constructor] fn constructor(_public_key: felt) { + ERC165Contract::register_interface(ACCOUNT_ID); public_key::write(_public_key); } @@ -41,14 +45,9 @@ mod Account { assert(1 == 2, 'Account: unauthorized.'); } - // ERC165Library + // ERC165Contract #[view] fn supports_interface(interface_id: felt) -> bool { - ERC165Library::supports_interface(interface_id) - } - - #[external] - fn register_interface(interface_id: felt) { - ERC165Library::register_interface(interface_id); + ERC165Contract::supports_interface(interface_id) } } diff --git a/src/openzeppelin/account/cairo_project.toml b/src/openzeppelin/account/cairo_project.toml deleted file mode 100644 index 9a0b3f035..000000000 --- a/src/openzeppelin/account/cairo_project.toml +++ /dev/null @@ -1,2 +0,0 @@ -[crate_roots] -account = "." diff --git a/src/openzeppelin/account/lib.cairo b/src/openzeppelin/account/lib.cairo deleted file mode 100644 index f6bc9aeb8..000000000 --- a/src/openzeppelin/account/lib.cairo +++ /dev/null @@ -1,6 +0,0 @@ -mod tests; - -trait IAccount { - // fn supports_interface(interface_id: felt) -> bool; - // fn register_interface(interface_id: felt); -} diff --git a/src/openzeppelin/account/tests/test_account.cairo b/src/openzeppelin/account/tests/test_account.cairo deleted file mode 100644 index 21ca324d5..000000000 --- a/src/openzeppelin/account/tests/test_account.cairo +++ /dev/null @@ -1,43 +0,0 @@ -use erc165::tests::erc165mock::ERC165Mock; - -const ERC165_ID: felt = 0x01ffc9a7; -const INVALID_ID: felt = 0xffffffff; -const OTHER_ID: felt = 0x12345678; - - -#[test] -#[available_gas(2000000)] -fn test_default_behavior() { - let supports_default_interface: bool = ERC165Mock::supports_interface(ERC165_ID); - assert(supports_default_interface, 'Should support base interface'); -} - -#[test] -#[available_gas(2000000)] -fn test_not_registered_interface() { - let supports_unregistered_interface: bool = ERC165Mock::supports_interface(OTHER_ID); - assert(! supports_unregistered_interface, 'Should not support unregistered'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_supports_invalid_interface() { - let supports_invalid_interface: bool = ERC165Mock::supports_interface(INVALID_ID); - assert(! supports_invalid_interface, 'Should not support invalid id'); -} - -#[test] -#[available_gas(2000000)] -fn test_register_interface() { - ERC165Mock::register_interface(OTHER_ID); - let supports_new_interface: bool = ERC165Mock::supports_interface(OTHER_ID); - assert(supports_new_interface, 'Should support new interface'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_register_invalid_interface() { - ERC165Mock::register_interface(INVALID_ID); -} diff --git a/src/openzeppelin/cairo_project.toml b/src/openzeppelin/cairo_project.toml new file mode 100644 index 000000000..badca57cd --- /dev/null +++ b/src/openzeppelin/cairo_project.toml @@ -0,0 +1,2 @@ +[crate_roots] +openzeppelin = "." diff --git a/src/openzeppelin/introspection.cairo b/src/openzeppelin/introspection.cairo new file mode 100644 index 000000000..4391460f4 --- /dev/null +++ b/src/openzeppelin/introspection.cairo @@ -0,0 +1 @@ +mod erc165; diff --git a/src/openzeppelin/introspection/erc165/tests/erc165mock.cairo b/src/openzeppelin/introspection/erc165.cairo similarity index 60% rename from src/openzeppelin/introspection/erc165/tests/erc165mock.cairo rename to src/openzeppelin/introspection/erc165.cairo index 0526945e7..533593512 100644 --- a/src/openzeppelin/introspection/erc165/tests/erc165mock.cairo +++ b/src/openzeppelin/introspection/erc165.cairo @@ -1,23 +1,29 @@ +const IERC165_ID: felt = 0x01ffc9a7; +const INVALID_ID: felt = 0xffffffff; + +trait IERC165 { + fn supports_interface(interface_id: felt) -> bool; + fn register_interface(interface_id: felt); +} + #[contract] -mod ERC165Mock { - use erc165::IERC165; - const IERC165_ID: felt = 0x01ffc9a7; - const INVALID_ID: felt = 0xffffffff; +mod ERC165Contract { + use openzeppelin::introspection::erc165; struct Storage { supported_interfaces: LegacyMap::, } - impl ERC165 of IERC165 { + impl ERC165 of erc165::IERC165 { fn supports_interface(interface_id: felt) -> bool { - if interface_id == IERC165_ID { + if interface_id == erc165::IERC165_ID { return true; } supported_interfaces::read(interface_id) } fn register_interface(interface_id: felt) { - assert(interface_id != INVALID_ID, 'Invalid id'); + assert(interface_id != erc165::INVALID_ID, 'Invalid id'); supported_interfaces::write(interface_id, true); } } diff --git a/src/openzeppelin/introspection/erc165/cairo_project.toml b/src/openzeppelin/introspection/erc165/cairo_project.toml deleted file mode 100644 index ba52962d0..000000000 --- a/src/openzeppelin/introspection/erc165/cairo_project.toml +++ /dev/null @@ -1,2 +0,0 @@ -[crate_roots] -erc165 = "." diff --git a/src/openzeppelin/introspection/erc165/lib.cairo b/src/openzeppelin/introspection/erc165/lib.cairo deleted file mode 100644 index 32e5e1525..000000000 --- a/src/openzeppelin/introspection/erc165/lib.cairo +++ /dev/null @@ -1,6 +0,0 @@ -mod tests; - -trait IERC165 { - fn supports_interface(interface_id: felt) -> bool; - fn register_interface(interface_id: felt); -} diff --git a/src/openzeppelin/introspection/erc165/tests.cairo b/src/openzeppelin/introspection/erc165/tests.cairo deleted file mode 100644 index 517c54280..000000000 --- a/src/openzeppelin/introspection/erc165/tests.cairo +++ /dev/null @@ -1,2 +0,0 @@ -mod test_erc165; -mod erc165mock; diff --git a/src/openzeppelin/lib.cairo b/src/openzeppelin/lib.cairo new file mode 100644 index 000000000..9c80c87ab --- /dev/null +++ b/src/openzeppelin/lib.cairo @@ -0,0 +1,3 @@ +mod introspection; +mod account; +mod tests; diff --git a/src/openzeppelin/account/tests.cairo b/src/openzeppelin/tests.cairo similarity index 50% rename from src/openzeppelin/account/tests.cairo rename to src/openzeppelin/tests.cairo index b837159e4..4bc4f188e 100644 --- a/src/openzeppelin/account/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -1,2 +1,2 @@ +mod test_erc165; mod test_account; -mod account_mock; diff --git a/src/openzeppelin/tests/test_account.cairo b/src/openzeppelin/tests/test_account.cairo new file mode 100644 index 000000000..7308ec172 --- /dev/null +++ b/src/openzeppelin/tests/test_account.cairo @@ -0,0 +1,18 @@ +use openzeppelin::account::Account; +use openzeppelin::account::ACCOUNT_ID; +use openzeppelin::introspection::erc165::IERC165_ID; + +const PUB_KEY: felt = 0x123; + + +#[test] +#[available_gas(2000000)] +fn test_erc165() { + Account::constructor(PUB_KEY); + + let supports_default_interface: bool = Account::supports_interface(IERC165_ID); + assert(supports_default_interface, 'Should support base interface'); + + let supports_account_interface: bool = Account::supports_interface(ACCOUNT_ID); + assert(supports_account_interface, 'Should support account id'); +} diff --git a/src/openzeppelin/introspection/erc165/tests/test_erc165.cairo b/src/openzeppelin/tests/test_erc165.cairo similarity index 53% rename from src/openzeppelin/introspection/erc165/tests/test_erc165.cairo rename to src/openzeppelin/tests/test_erc165.cairo index 21ca324d5..a387dbfc3 100644 --- a/src/openzeppelin/introspection/erc165/tests/test_erc165.cairo +++ b/src/openzeppelin/tests/test_erc165.cairo @@ -1,37 +1,36 @@ -use erc165::tests::erc165mock::ERC165Mock; +use openzeppelin::introspection::erc165::ERC165Contract; +use openzeppelin::introspection::erc165::IERC165_ID; +use openzeppelin::introspection::erc165::INVALID_ID; -const ERC165_ID: felt = 0x01ffc9a7; -const INVALID_ID: felt = 0xffffffff; const OTHER_ID: felt = 0x12345678; #[test] #[available_gas(2000000)] fn test_default_behavior() { - let supports_default_interface: bool = ERC165Mock::supports_interface(ERC165_ID); + let supports_default_interface: bool = ERC165Contract::supports_interface(IERC165_ID); assert(supports_default_interface, 'Should support base interface'); } #[test] #[available_gas(2000000)] fn test_not_registered_interface() { - let supports_unregistered_interface: bool = ERC165Mock::supports_interface(OTHER_ID); + let supports_unregistered_interface: bool = ERC165Contract::supports_interface(OTHER_ID); assert(! supports_unregistered_interface, 'Should not support unregistered'); } #[test] #[available_gas(2000000)] -#[should_panic] fn test_supports_invalid_interface() { - let supports_invalid_interface: bool = ERC165Mock::supports_interface(INVALID_ID); + let supports_invalid_interface: bool = ERC165Contract::supports_interface(INVALID_ID); assert(! supports_invalid_interface, 'Should not support invalid id'); } #[test] #[available_gas(2000000)] fn test_register_interface() { - ERC165Mock::register_interface(OTHER_ID); - let supports_new_interface: bool = ERC165Mock::supports_interface(OTHER_ID); + ERC165Contract::register_interface(OTHER_ID); + let supports_new_interface: bool = ERC165Contract::supports_interface(OTHER_ID); assert(supports_new_interface, 'Should support new interface'); } @@ -39,5 +38,5 @@ fn test_register_interface() { #[available_gas(2000000)] #[should_panic] fn test_register_invalid_interface() { - ERC165Mock::register_interface(INVALID_ID); + ERC165Contract::register_interface(INVALID_ID); } From 35ede09ccefc64d982cda71bce7f3db46da1e418 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Tue, 7 Mar 2023 21:00:17 -0300 Subject: [PATCH 031/246] re-structure project --- src/openzeppelin/account.cairo | 53 ---- src/openzeppelin/account/old/Account.cairo | 147 ---------- .../account/old/AddressRegistry.cairo | 27 -- src/openzeppelin/account/old/EthAccount.cairo | 146 ---------- src/openzeppelin/account/old/IAccount.cairo | 45 --- src/openzeppelin/account/old/library.cairo | 260 ------------------ src/openzeppelin/lib.cairo | 1 - src/openzeppelin/tests.cairo | 1 - src/openzeppelin/tests/test_account.cairo | 18 -- 9 files changed, 698 deletions(-) delete mode 100644 src/openzeppelin/account.cairo delete mode 100644 src/openzeppelin/account/old/Account.cairo delete mode 100644 src/openzeppelin/account/old/AddressRegistry.cairo delete mode 100644 src/openzeppelin/account/old/EthAccount.cairo delete mode 100644 src/openzeppelin/account/old/IAccount.cairo delete mode 100644 src/openzeppelin/account/old/library.cairo delete mode 100644 src/openzeppelin/tests/test_account.cairo diff --git a/src/openzeppelin/account.cairo b/src/openzeppelin/account.cairo deleted file mode 100644 index e41be2080..000000000 --- a/src/openzeppelin/account.cairo +++ /dev/null @@ -1,53 +0,0 @@ -const ACCOUNT_ID: felt = 0x4; - -#[account_contract] -mod Account { - use openzeppelin::account::ACCOUNT_ID; - use openzeppelin::introspection::erc165::ERC165Contract; - use starknet::get_caller_address; - use starknet::get_contract_address; - - struct Storage { - public_key: felt, - } - - #[constructor] - fn constructor(_public_key: felt) { - ERC165Contract::register_interface(ACCOUNT_ID); - public_key::write(_public_key); - } - - #[external] - fn __execute__(amount: felt) { - let is_valid = is_valid_signature(); - assert(is_valid == true, 'Invalid signature.'); - } - - #[external] - fn set_public_key(new_public_key: felt) { - only_self(); - public_key::write(new_public_key); - } - - #[view] - fn get_public_key() -> felt { - public_key::read() - } - - #[view] - fn is_valid_signature() -> bool { - true - } - - fn only_self() { - let caller = starknet::get_caller_address(); - let self = starknet::get_contract_address(); - assert(1 == 2, 'Account: unauthorized.'); - } - - // ERC165Contract - #[view] - fn supports_interface(interface_id: felt) -> bool { - ERC165Contract::supports_interface(interface_id) - } -} diff --git a/src/openzeppelin/account/old/Account.cairo b/src/openzeppelin/account/old/Account.cairo deleted file mode 100644 index 23c10199b..000000000 --- a/src/openzeppelin/account/old/Account.cairo +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (account/presets/Account.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin, BitwiseBuiltin -from starkware.starknet.common.syscalls import get_tx_info - -from openzeppelin.account.library import Account, AccountCallArray - - -// -// Constructor -// - -@constructor -func constructor{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - range_check_ptr -}(publicKey: felt) { - Account.initializer(publicKey); - return (); -} - -// -// Getters -// - -@view -func getPublicKey{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - range_check_ptr -} () -> (publicKey: felt) { - let (publicKey: felt) = Account.get_public_key(); - return (publicKey=publicKey); -} - -@view -func supportsInterface{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - range_check_ptr -} (interfaceId: felt) -> (success: felt) { - return Account.supports_interface(interfaceId); -} - -// -// Setters -// - -@external -func setPublicKey{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - range_check_ptr -} (newPublicKey: felt) { - Account.set_public_key(newPublicKey); - return (); -} - -// -// Business logic -// - -@view -func isValidSignature{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - ecdsa_ptr: SignatureBuiltin*, - range_check_ptr -}( - hash: felt, - signature_len: felt, - signature: felt* -) -> (isValid: felt) { - let (isValid: felt) = Account.is_valid_signature(hash, signature_len, signature); - return (isValid=isValid); -} - -@external -func __validate__{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - ecdsa_ptr: SignatureBuiltin*, - range_check_ptr -}( - call_array_len: felt, - call_array: AccountCallArray*, - calldata_len: felt, - calldata: felt* -) { - let (tx_info) = get_tx_info(); - Account.is_valid_signature(tx_info.transaction_hash, tx_info.signature_len, tx_info.signature); - return (); -} - -@external -func __validate_declare__{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - ecdsa_ptr: SignatureBuiltin*, - range_check_ptr -} (class_hash: felt) { - let (tx_info) = get_tx_info(); - Account.is_valid_signature(tx_info.transaction_hash, tx_info.signature_len, tx_info.signature); - return (); -} - -@external -func __validate_deploy__{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - ecdsa_ptr: SignatureBuiltin*, - range_check_ptr -} ( - class_hash: felt, - salt: felt, - publicKey: felt -) { - let (tx_info) = get_tx_info(); - Account.is_valid_signature(tx_info.transaction_hash, tx_info.signature_len, tx_info.signature); - return (); -} - -@external -func __execute__{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - ecdsa_ptr: SignatureBuiltin*, - bitwise_ptr: BitwiseBuiltin*, - range_check_ptr, -}( - call_array_len: felt, - call_array: AccountCallArray*, - calldata_len: felt, - calldata: felt* -) -> ( - response_len: felt, - response: felt* -) { - let (response_len, response) = Account.execute( - call_array_len, call_array, calldata_len, calldata - ); - return (response_len, response); -} diff --git a/src/openzeppelin/account/old/AddressRegistry.cairo b/src/openzeppelin/account/old/AddressRegistry.cairo deleted file mode 100644 index 7076bd25a..000000000 --- a/src/openzeppelin/account/old/AddressRegistry.cairo +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (account/presets/AddressRegistry.cairo) - -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.starknet.common.syscalls import get_caller_address - -@storage_var -func L1_address(L2_address: felt) -> (address: felt) { -} - -@external -func get_L1_address{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - L2_address: felt -) -> (address: felt) { - return L1_address.read(L2_address); -} - -@external -func set_L1_address{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - new_L1_address: felt -) { - let (caller) = get_caller_address(); - L1_address.write(caller, new_L1_address); - return (); -} diff --git a/src/openzeppelin/account/old/EthAccount.cairo b/src/openzeppelin/account/old/EthAccount.cairo deleted file mode 100644 index 50277380d..000000000 --- a/src/openzeppelin/account/old/EthAccount.cairo +++ /dev/null @@ -1,146 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (account/presets/EthAccount.cairo) - -%lang starknet -from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin, BitwiseBuiltin -from starkware.starknet.common.syscalls import get_tx_info - -from openzeppelin.account.library import Account, AccountCallArray - -// -// Constructor -// - -@constructor -func constructor{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - range_check_ptr -}(ethAddress: felt) { - Account.initializer(ethAddress); - return (); -} - -// -// Getters -// - -@view -func getEthAddress{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - range_check_ptr -} () -> (ethAddress: felt) { - let (ethAddress: felt) = Account.get_public_key(); - return (ethAddress=ethAddress); -} - -@view -func supportsInterface{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - range_check_ptr -} (interfaceId: felt) -> (success: felt) { - return Account.supports_interface(interfaceId); -} - -// -// Setters -// - -@external -func setEthAddress{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - range_check_ptr -} (newEthAddress: felt) { - Account.set_public_key(newEthAddress); - return (); -} - -// -// Business logic -// - -@view -func isValidSignature{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - bitwise_ptr: BitwiseBuiltin*, - range_check_ptr, -}( - hash: felt, - signature_len: felt, - signature: felt* -) -> (isValid: felt) { - let (isValid) = Account.is_valid_eth_signature(hash, signature_len, signature); - return (isValid=isValid); -} - -@external -func __validate__{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - bitwise_ptr: BitwiseBuiltin*, - range_check_ptr, -}( - call_array_len: felt, - call_array: AccountCallArray*, - calldata_len: felt, - calldata: felt* -) { - let (tx_info) = get_tx_info(); - Account.is_valid_eth_signature(tx_info.transaction_hash, tx_info.signature_len, tx_info.signature); - return (); -} - -@external -func __validate_declare__{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - bitwise_ptr: BitwiseBuiltin*, - range_check_ptr, -} (class_hash: felt) { - let (tx_info) = get_tx_info(); - Account.is_valid_eth_signature(tx_info.transaction_hash, tx_info.signature_len, tx_info.signature); - return (); -} - - -@external -func __validate_deploy__{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - bitwise_ptr: BitwiseBuiltin*, - range_check_ptr -} ( - class_hash: felt, - salt: felt, - ethAddress: felt -) { - let (tx_info) = get_tx_info(); - Account.is_valid_eth_signature(tx_info.transaction_hash, tx_info.signature_len, tx_info.signature); - return (); -} - -@external -func __execute__{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - ecdsa_ptr: SignatureBuiltin*, - bitwise_ptr: BitwiseBuiltin*, - range_check_ptr, -}( - call_array_len: felt, - call_array: AccountCallArray*, - calldata_len: felt, - calldata: felt* -) -> ( - response_len: felt, - response: felt* -) { - let (response_len, response) = Account.execute( - call_array_len, call_array, calldata_len, calldata - ); - return (response_len, response); -} diff --git a/src/openzeppelin/account/old/IAccount.cairo b/src/openzeppelin/account/old/IAccount.cairo deleted file mode 100644 index 8a22d38c1..000000000 --- a/src/openzeppelin/account/old/IAccount.cairo +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (account/IAccount.cairo) - -%lang starknet - -from openzeppelin.account.library import AccountCallArray - -@contract_interface -namespace IAccount { - func isValidSignature( - hash: felt, - signature_len: felt, - signature: felt* - ) -> (isValid: felt) { - } - - func __validate__( - call_array_len: felt, - call_array: AccountCallArray*, - calldata_len: felt, - calldata: felt* - ) { - } - - // Parameter temporarily named `cls_hash` instead of `class_hash` (expected). - // See https://github.com/starkware-libs/cairo-lang/issues/100 for details. - func __validate_declare__(cls_hash: felt) { - } - - func __execute__( - call_array_len: felt, - call_array: AccountCallArray*, - calldata_len: felt, - calldata: felt* - ) -> ( - response_len: felt, - response: felt* - ) { - } - - // ERC165 - - func supportsInterface(interfaceId: felt) -> (success: felt) { - } -} diff --git a/src/openzeppelin/account/old/library.cairo b/src/openzeppelin/account/old/library.cairo deleted file mode 100644 index 6723032eb..000000000 --- a/src/openzeppelin/account/old/library.cairo +++ /dev/null @@ -1,260 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.6.1 (account/library.cairo) - -%lang starknet - -from starkware.cairo.common.registers import get_fp_and_pc -from starkware.cairo.common.signature import verify_ecdsa_signature -from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin, BitwiseBuiltin -from starkware.cairo.common.alloc import alloc -from starkware.cairo.common.uint256 import Uint256 -from starkware.cairo.common.memcpy import memcpy -from starkware.cairo.common.math import split_felt -from starkware.cairo.common.math_cmp import is_le_felt -from starkware.cairo.common.bool import TRUE, FALSE -from starkware.starknet.common.syscalls import ( - call_contract, - get_caller_address, - get_contract_address, - get_tx_info -) -from starkware.cairo.common.cairo_secp.signature import ( - finalize_keccak, - verify_eth_signature_uint256 -) -from openzeppelin.utils.constants.library import ( - IACCOUNT_ID, - IERC165_ID, - TRANSACTION_VERSION -) - -// -// Storage -// - -@storage_var -func Account_public_key() -> (public_key: felt) { -} - -// -// Structs -// - -struct Call { - to: felt, - selector: felt, - calldata_len: felt, - calldata: felt*, -} - -// Tmp struct introduced while we wait for Cairo -// to support passing `[AccountCall]` to __execute__ -struct AccountCallArray { - to: felt, - selector: felt, - data_offset: felt, - data_len: felt, -} - -namespace Account { - // - // Initializer - // - - func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - _public_key: felt - ) { - Account_public_key.write(_public_key); - return (); - } - - // - // Guards - // - - func assert_only_self{syscall_ptr: felt*}() { - let (self) = get_contract_address(); - let (caller) = get_caller_address(); - with_attr error_message("Account: caller is not this account") { - assert self = caller; - } - return (); - } - - // - // Getters - // - - func get_public_key{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - public_key: felt - ) { - return Account_public_key.read(); - } - - func supports_interface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(interface_id: felt) -> ( - success: felt - ) { - if (interface_id == IERC165_ID) { - return (success=TRUE); - } - if (interface_id == IACCOUNT_ID) { - return (success=TRUE); - } - return (success=FALSE); - } - - // - // Setters - // - - func set_public_key{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - new_public_key: felt - ) { - assert_only_self(); - Account_public_key.write(new_public_key); - return (); - } - - // - // Business logic - // - - func is_valid_signature{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - ecdsa_ptr: SignatureBuiltin*, - range_check_ptr, - }(hash: felt, signature_len: felt, signature: felt*) -> (is_valid: felt) { - let (_public_key) = Account_public_key.read(); - - // This interface expects a signature pointer and length to make - // no assumption about signature validation schemes. - // But this implementation does, and it expects a (sig_r, sig_s) pair. - let sig_r = signature[0]; - let sig_s = signature[1]; - - verify_ecdsa_signature( - message=hash, public_key=_public_key, signature_r=sig_r, signature_s=sig_s - ); - - return (is_valid=TRUE); - } - - func is_valid_eth_signature{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - bitwise_ptr: BitwiseBuiltin*, - range_check_ptr, - }(hash: felt, signature_len: felt, signature: felt*) -> (is_valid: felt) { - alloc_locals; - let (_public_key) = get_public_key(); - let (__fp__, _) = get_fp_and_pc(); - - // This interface expects a signature pointer and length to make - // no assumption about signature validation schemes. - // But this implementation does, and it expects a the sig_v, sig_r, - // sig_s, and hash elements. - let sig_v: felt = signature[0]; - let sig_r: Uint256 = Uint256(low=signature[1], high=signature[2]); - let sig_s: Uint256 = Uint256(low=signature[3], high=signature[4]); - let (high, low) = split_felt(hash); - let msg_hash: Uint256 = Uint256(low=low, high=high); - - let (keccak_ptr: felt*) = alloc(); - local keccak_ptr_start: felt* = keccak_ptr; - - with keccak_ptr { - verify_eth_signature_uint256( - msg_hash=msg_hash, r=sig_r, s=sig_s, v=sig_v, eth_address=_public_key - ); - } - // Required to ensure sequencers cannot spoof validation check. - finalize_keccak(keccak_ptr_start=keccak_ptr_start, keccak_ptr_end=keccak_ptr); - - return (is_valid=TRUE); - } - - func execute{ - syscall_ptr: felt*, - pedersen_ptr: HashBuiltin*, - ecdsa_ptr: SignatureBuiltin*, - bitwise_ptr: BitwiseBuiltin*, - range_check_ptr, - }(call_array_len: felt, call_array: AccountCallArray*, calldata_len: felt, calldata: felt*) -> ( - response_len: felt, response: felt* - ) { - alloc_locals; - - let (tx_info) = get_tx_info(); - // Disallow deprecated tx versions - with_attr error_message("Account: deprecated tx version") { - assert is_le_felt(TRANSACTION_VERSION, tx_info.version) = TRUE; - } - - // Assert not a reentrant call - let (caller) = get_caller_address(); - with_attr error_message("Account: reentrant call") { - assert caller = 0; - } - - // TMP: Convert `AccountCallArray` to 'Call'. - let (calls: Call*) = alloc(); - _from_call_array_to_call(call_array_len, call_array, calldata, calls); - let calls_len = call_array_len; - - // Execute call - let (response: felt*) = alloc(); - let (response_len) = _execute_list(calls_len, calls, response); - - return (response_len=response_len, response=response); - } - - func _execute_list{syscall_ptr: felt*}(calls_len: felt, calls: Call*, response: felt*) -> ( - response_len: felt - ) { - alloc_locals; - - // if no more calls - if (calls_len == 0) { - return (response_len=0); - } - - // do the current call - let this_call: Call = [calls]; - let res = call_contract( - contract_address=this_call.to, - function_selector=this_call.selector, - calldata_size=this_call.calldata_len, - calldata=this_call.calldata, - ); - // copy the result in response - memcpy(response, res.retdata, res.retdata_size); - // do the next calls recursively - let (response_len) = _execute_list( - calls_len - 1, calls + Call.SIZE, response + res.retdata_size - ); - return (response_len=response_len + res.retdata_size); - } - - func _from_call_array_to_call{syscall_ptr: felt*}( - call_array_len: felt, call_array: AccountCallArray*, calldata: felt*, calls: Call* - ) { - // if no more calls - if (call_array_len == 0) { - return (); - } - - // parse the current call - assert [calls] = Call( - to=[call_array].to, - selector=[call_array].selector, - calldata_len=[call_array].data_len, - calldata=calldata + [call_array].data_offset - ); - // parse the remaining calls recursively - _from_call_array_to_call( - call_array_len - 1, call_array + AccountCallArray.SIZE, calldata, calls + Call.SIZE - ); - return (); - } -} diff --git a/src/openzeppelin/lib.cairo b/src/openzeppelin/lib.cairo index 9c80c87ab..ad13b042b 100644 --- a/src/openzeppelin/lib.cairo +++ b/src/openzeppelin/lib.cairo @@ -1,3 +1,2 @@ mod introspection; -mod account; mod tests; diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index 4bc4f188e..4f213d1c3 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -1,2 +1 @@ mod test_erc165; -mod test_account; diff --git a/src/openzeppelin/tests/test_account.cairo b/src/openzeppelin/tests/test_account.cairo deleted file mode 100644 index 7308ec172..000000000 --- a/src/openzeppelin/tests/test_account.cairo +++ /dev/null @@ -1,18 +0,0 @@ -use openzeppelin::account::Account; -use openzeppelin::account::ACCOUNT_ID; -use openzeppelin::introspection::erc165::IERC165_ID; - -const PUB_KEY: felt = 0x123; - - -#[test] -#[available_gas(2000000)] -fn test_erc165() { - Account::constructor(PUB_KEY); - - let supports_default_interface: bool = Account::supports_interface(IERC165_ID); - assert(supports_default_interface, 'Should support base interface'); - - let supports_account_interface: bool = Account::supports_interface(ACCOUNT_ID); - assert(supports_account_interface, 'Should support account id'); -} From e55889a2e5add0ba3731b96a9cab7fde2ba24426 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Tue, 7 Mar 2023 21:02:53 -0300 Subject: [PATCH 032/246] re-structure project --- src/openzeppelin/introspection.cairo | 1 - src/openzeppelin/introspection/erc165.cairo | 40 -------------------- src/openzeppelin/lib.cairo | 1 - src/openzeppelin/tests.cairo | 2 +- src/openzeppelin/tests/test_erc165.cairo | 42 --------------------- src/openzeppelin/tests/test_mytest.cairo | 5 +++ 6 files changed, 6 insertions(+), 85 deletions(-) delete mode 100644 src/openzeppelin/introspection.cairo delete mode 100644 src/openzeppelin/introspection/erc165.cairo delete mode 100644 src/openzeppelin/tests/test_erc165.cairo create mode 100644 src/openzeppelin/tests/test_mytest.cairo diff --git a/src/openzeppelin/introspection.cairo b/src/openzeppelin/introspection.cairo deleted file mode 100644 index 4391460f4..000000000 --- a/src/openzeppelin/introspection.cairo +++ /dev/null @@ -1 +0,0 @@ -mod erc165; diff --git a/src/openzeppelin/introspection/erc165.cairo b/src/openzeppelin/introspection/erc165.cairo deleted file mode 100644 index 533593512..000000000 --- a/src/openzeppelin/introspection/erc165.cairo +++ /dev/null @@ -1,40 +0,0 @@ -const IERC165_ID: felt = 0x01ffc9a7; -const INVALID_ID: felt = 0xffffffff; - -trait IERC165 { - fn supports_interface(interface_id: felt) -> bool; - fn register_interface(interface_id: felt); -} - -#[contract] -mod ERC165Contract { - use openzeppelin::introspection::erc165; - - struct Storage { - supported_interfaces: LegacyMap::, - } - - impl ERC165 of erc165::IERC165 { - fn supports_interface(interface_id: felt) -> bool { - if interface_id == erc165::IERC165_ID { - return true; - } - supported_interfaces::read(interface_id) - } - - fn register_interface(interface_id: felt) { - assert(interface_id != erc165::INVALID_ID, 'Invalid id'); - supported_interfaces::write(interface_id, true); - } - } - - #[view] - fn supports_interface(interface_id: felt) -> bool { - ERC165::supports_interface(interface_id) - } - - #[external] - fn register_interface(interface_id: felt) { - ERC165::register_interface(interface_id) - } -} diff --git a/src/openzeppelin/lib.cairo b/src/openzeppelin/lib.cairo index ad13b042b..14f00389d 100644 --- a/src/openzeppelin/lib.cairo +++ b/src/openzeppelin/lib.cairo @@ -1,2 +1 @@ -mod introspection; mod tests; diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index 4f213d1c3..ba51ab5d3 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -1 +1 @@ -mod test_erc165; +mod test_mytest; diff --git a/src/openzeppelin/tests/test_erc165.cairo b/src/openzeppelin/tests/test_erc165.cairo deleted file mode 100644 index a387dbfc3..000000000 --- a/src/openzeppelin/tests/test_erc165.cairo +++ /dev/null @@ -1,42 +0,0 @@ -use openzeppelin::introspection::erc165::ERC165Contract; -use openzeppelin::introspection::erc165::IERC165_ID; -use openzeppelin::introspection::erc165::INVALID_ID; - -const OTHER_ID: felt = 0x12345678; - - -#[test] -#[available_gas(2000000)] -fn test_default_behavior() { - let supports_default_interface: bool = ERC165Contract::supports_interface(IERC165_ID); - assert(supports_default_interface, 'Should support base interface'); -} - -#[test] -#[available_gas(2000000)] -fn test_not_registered_interface() { - let supports_unregistered_interface: bool = ERC165Contract::supports_interface(OTHER_ID); - assert(! supports_unregistered_interface, 'Should not support unregistered'); -} - -#[test] -#[available_gas(2000000)] -fn test_supports_invalid_interface() { - let supports_invalid_interface: bool = ERC165Contract::supports_interface(INVALID_ID); - assert(! supports_invalid_interface, 'Should not support invalid id'); -} - -#[test] -#[available_gas(2000000)] -fn test_register_interface() { - ERC165Contract::register_interface(OTHER_ID); - let supports_new_interface: bool = ERC165Contract::supports_interface(OTHER_ID); - assert(supports_new_interface, 'Should support new interface'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_register_invalid_interface() { - ERC165Contract::register_interface(INVALID_ID); -} diff --git a/src/openzeppelin/tests/test_mytest.cairo b/src/openzeppelin/tests/test_mytest.cairo new file mode 100644 index 000000000..1f5f2ca0e --- /dev/null +++ b/src/openzeppelin/tests/test_mytest.cairo @@ -0,0 +1,5 @@ +#[test] +#[available_gas(2000000)] +fn test_pass() { + assert(true, 'Should pass'); +} From 9d5079c68a91e009d51bf6d2e3920dc6d5b5f89e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Tue, 7 Mar 2023 21:09:29 -0300 Subject: [PATCH 033/246] update makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 020e49482..9ed2f2af3 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ build: cargo build test: - cargo run --bin cairo-test -- --starknet --path $(dir) + cargo run --bin cairo-test -- --starknet --path src/openzeppelin format: cargo run --bin cairo-format -- --recursive $(SOURCE_FOLDER) --print-parsing-errors From ae89fa538fa58ef4959bda03e24d9c91548d730f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Wed, 8 Mar 2023 13:18:38 -0300 Subject: [PATCH 034/246] bump submodule --- cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cairo b/cairo index 176712e8e..903337885 160000 --- a/cairo +++ b/cairo @@ -1 +1 @@ -Subproject commit 176712e8e54404a79f42bd90251571a5d90d7250 +Subproject commit 903337885fa19e28f59988a4952d670f72b44b34 From a54e98ca74e0d2c127680f3a3ca23d4643eb0ddf Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Tue, 28 Mar 2023 08:57:20 -0400 Subject: [PATCH 035/246] Update erc20 migration branch (#586) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Change the spelling of "StarkNet" to "Starknet" (#557) * fix: link (#545) * change StarkNet to Starknet --------- Co-authored-by: Eric Nordelo * add Cargo and Makefile * add deps in cairo_project, create lib * add presets * add base lib * add tests * remove old cairo lib and interface * remove unused import * fix vars * change external funcs to snake case * remove unused import * add tests for externals * add bool assertions * remove preset mods * add IERC20 trait * clean up test * remove assertion * simplify max_u256 * clarify error msg * clean up python project. ready for rust/cairo1 * add erc165 + tests * kickstart account module * re-structure project * re-structure project * re-structure project * update makefile * re-structure project * update to felt252, add use ContractAddress * formatting * remove colons from storage, remove _mint from initializer * add constructor test * add comments * fix format * check error msg in tests * set max_u256 as func * update cairo * update cargo * revert doc commit * remove __init__ * refix link --------- Co-authored-by: kongtaoxing <69096526+kongtaoxing@users.noreply.github.com> Co-authored-by: Eric Nordelo Co-authored-by: Martín Triay --- Cargo.lock | 327 ++++++++++++---- Cargo.toml | 6 +- cairo | 2 +- src/openzeppelin/lib.cairo | 1 + src/openzeppelin/tests.cairo | 2 +- src/openzeppelin/tests/test_erc20.cairo | 455 +++++++++++++++++++++++ src/openzeppelin/tests/test_mytest.cairo | 5 - src/openzeppelin/token.cairo | 1 + src/openzeppelin/token/erc20.cairo | 207 +++++++++++ 9 files changed, 918 insertions(+), 88 deletions(-) create mode 100644 src/openzeppelin/tests/test_erc20.cairo delete mode 100644 src/openzeppelin/tests/test_mytest.cairo create mode 100644 src/openzeppelin/token.cairo create mode 100644 src/openzeppelin/token/erc20.cairo diff --git a/Cargo.lock b/Cargo.lock index e6cd6443e..2094098a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,7 +71,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" dependencies = [ "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -81,7 +81,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3ba0a3cf584a8ede533a1749b86040e9018cf752265fc39a71c69fe1fafaba5" dependencies = [ "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -93,7 +93,7 @@ dependencies = [ "num-bigint", "num-traits 0.2.15", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -106,7 +106,7 @@ dependencies = [ "num-traits 0.2.15", "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -173,7 +173,7 @@ checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -196,7 +196,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -307,7 +307,7 @@ dependencies = [ [[package]] name = "cairo-lang-casm" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "cairo-lang-utils", "env_logger", @@ -316,6 +316,7 @@ dependencies = [ "num-bigint", "num-traits 0.2.15", "pretty_assertions", + "serde", "test-case", "test-log", "thiserror", @@ -323,7 +324,7 @@ dependencies = [ [[package]] name = "cairo-lang-compiler" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "anyhow", "cairo-lang-defs", @@ -347,7 +348,7 @@ dependencies = [ [[package]] name = "cairo-lang-debug" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "cairo-lang-proc-macros", "cairo-lang-utils", @@ -358,7 +359,7 @@ dependencies = [ [[package]] name = "cairo-lang-defs" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "cairo-lang-debug", "cairo-lang-diagnostics", @@ -379,7 +380,7 @@ dependencies = [ [[package]] name = "cairo-lang-diagnostics" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "cairo-lang-filesystem", "cairo-lang-proc-macros", @@ -394,7 +395,7 @@ dependencies = [ [[package]] name = "cairo-lang-eq-solver" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "cairo-lang-utils", "env_logger", @@ -408,7 +409,7 @@ dependencies = [ [[package]] name = "cairo-lang-filesystem" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "cairo-lang-debug", "cairo-lang-utils", @@ -421,7 +422,7 @@ dependencies = [ [[package]] name = "cairo-lang-formatter" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "anyhow", "cairo-lang-diagnostics", @@ -444,8 +445,9 @@ dependencies = [ [[package]] name = "cairo-lang-language-server" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ + "anyhow", "cairo-lang-compiler", "cairo-lang-debug", "cairo-lang-defs", @@ -460,10 +462,14 @@ dependencies = [ "cairo-lang-starknet", "cairo-lang-syntax", "cairo-lang-utils", + "indoc", + "log", "lsp-types", "salsa", + "scarb-metadata", "serde", "serde_json", + "smol_str", "test-log", "tokio", "tower-lsp", @@ -471,7 +477,7 @@ dependencies = [ [[package]] name = "cairo-lang-lowering" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -486,6 +492,7 @@ dependencies = [ "cairo-lang-utils", "env_logger", "id-arena", + "indexmap", "indoc", "itertools", "log", @@ -499,7 +506,7 @@ dependencies = [ [[package]] name = "cairo-lang-parser" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "cairo-lang-diagnostics", "cairo-lang-filesystem", @@ -520,7 +527,7 @@ dependencies = [ [[package]] name = "cairo-lang-plugins" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -543,16 +550,16 @@ dependencies = [ [[package]] name = "cairo-lang-proc-macros" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "cairo-lang-debug", "quote", - "syn", + "syn 1.0.103", ] [[package]] name = "cairo-lang-project" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "cairo-lang-filesystem", "indoc", @@ -565,7 +572,7 @@ dependencies = [ [[package]] name = "cairo-lang-runner" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "anyhow", "ark-ff 0.4.0-alpha.7", @@ -593,7 +600,7 @@ dependencies = [ [[package]] name = "cairo-lang-semantic" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "assert_matches", "cairo-lang-debug", @@ -623,7 +630,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "assert_matches", "bimap", @@ -651,7 +658,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-ap-change" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", @@ -666,7 +673,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-gas" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", @@ -683,7 +690,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-generator" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -714,8 +721,9 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-to-casm" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ + "anyhow", "assert_matches", "cairo-felt", "cairo-lang-casm", @@ -738,9 +746,10 @@ dependencies = [ [[package]] name = "cairo-lang-starknet" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "anyhow", + "cairo-lang-casm", "cairo-lang-compiler", "cairo-lang-defs", "cairo-lang-diagnostics", @@ -780,7 +789,7 @@ dependencies = [ [[package]] name = "cairo-lang-syntax" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "cairo-lang-debug", "cairo-lang-filesystem", @@ -794,7 +803,7 @@ dependencies = [ [[package]] name = "cairo-lang-syntax-codegen" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "cairo-lang-utils", "env_logger", @@ -806,7 +815,7 @@ dependencies = [ [[package]] name = "cairo-lang-test-runner" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "anyhow", "cairo-felt", @@ -839,7 +848,7 @@ dependencies = [ [[package]] name = "cairo-lang-test-utils" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "cairo-lang-utils", "env_logger", @@ -850,13 +859,18 @@ dependencies = [ [[package]] name = "cairo-lang-utils" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "chrono", "env_logger", "indexmap", "itertools", "log", + "num-bigint", + "num-integer", + "num-traits 0.2.15", + "serde", + "serde_json", "test-case", "test-log", ] @@ -890,6 +904,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "camino" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2" +dependencies = [ + "serde", +] + [[package]] name = "cc" version = "1.0.77" @@ -959,7 +982,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -972,7 +995,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -1132,7 +1155,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -1159,7 +1182,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn", + "syn 1.0.103", ] [[package]] @@ -1176,7 +1199,42 @@ checksum = "a08a6e2fcc370a089ad3b4aaf54db3b1b4cee38ddabce5896b33eb693275f470" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.103", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.103", ] [[package]] @@ -1200,7 +1258,38 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", +] + +[[package]] +name = "derive_builder" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.103", +] + +[[package]] +name = "derive_builder_macro" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" +dependencies = [ + "derive_builder_core", + "syn 1.0.103", ] [[package]] @@ -1315,6 +1404,7 @@ checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" dependencies = [ "futures-channel", "futures-core", + "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -1337,6 +1427,17 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +[[package]] +name = "futures-executor" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + [[package]] name = "futures-io" version = "0.3.25" @@ -1351,7 +1452,7 @@ checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -1366,6 +1467,12 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + [[package]] name = "futures-util" version = "0.3.25" @@ -1403,7 +1510,7 @@ checksum = "40803f2757f84c877f088e62420931f6e05a72514f1f03630384aa30b91d667b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -1549,6 +1656,12 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.3.0" @@ -2058,7 +2171,7 @@ checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -2106,7 +2219,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.103", "version_check", ] @@ -2123,18 +2236,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -2176,9 +2289,9 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "0.9.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed02d09394c94ffbdfdc755ad62a132e94c3224a8354e78a1200ced34df12edf" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ "either", "rayon-core", @@ -2186,9 +2299,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -2250,6 +2363,32 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rstest" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b07f2d176c472198ec1e6551dc7da28f1c089652f66a7b722676c2238ebc0edf" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros", + "rustc_version 0.4.0", +] + +[[package]] +name = "rstest_macros" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7229b505ae0706e64f37ffc54a9c163e11022a6636d58fe1f3f52018257ff9f7" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "rustc_version 0.4.0", + "syn 1.0.103", + "unicode-ident", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -2271,7 +2410,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.16", + "semver 1.0.17", ] [[package]] @@ -2312,7 +2451,7 @@ dependencies = [ "heck 0.3.3", "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -2324,6 +2463,20 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scarb-metadata" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2480a9a896395a1414feebcfe8550b35f4dce0e6a437eeaa0c3d79ec938ddf42" +dependencies = [ + "camino", + "derive_builder", + "semver 1.0.17", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -2347,9 +2500,12 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +dependencies = [ + "serde", +] [[package]] name = "semver-parser" @@ -2362,9 +2518,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.147" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" dependencies = [ "serde_derive", ] @@ -2380,20 +2536,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.147" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.3", ] [[package]] name = "serde_json" -version = "1.0.89" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" dependencies = [ "itoa", "ryu", @@ -2408,7 +2564,7 @@ checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -2534,7 +2690,7 @@ checksum = "6569d70430f0f6edc41f6820d00acf63356e6308046ca01e57eeac22ad258c47" dependencies = [ "starknet-curve", "starknet-ff", - "syn", + "syn 1.0.103", ] [[package]] @@ -2598,6 +2754,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8234ae35e70582bfa0f1fedffa6daa248e41dd045310b19800c4a36382c8f60" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "synstructure" version = "0.12.6" @@ -2606,7 +2773,7 @@ checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", "unicode-xid", ] @@ -2649,7 +2816,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -2660,12 +2827,12 @@ checksum = "38f0c854faeb68a048f0f2dc410c5ddae3bf83854ef0e4977d58306a5edef50e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] name = "tests" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" dependencies = [ "assert_matches", "cairo-felt", @@ -2674,6 +2841,7 @@ dependencies = [ "cairo-lang-defs", "cairo-lang-diagnostics", "cairo-lang-filesystem", + "cairo-lang-lowering", "cairo-lang-parser", "cairo-lang-plugins", "cairo-lang-runner", @@ -2690,6 +2858,7 @@ dependencies = [ "log", "num-bigint", "pretty_assertions", + "rstest", "salsa", "test-case", "test-log", @@ -2703,22 +2872,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.3", ] [[package]] @@ -2793,7 +2962,7 @@ checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -2870,7 +3039,7 @@ checksum = "7ebd99eec668d0a450c177acbc4d05e0d0d13b1f8d3db13cd706c52cbec4ac04" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -3027,7 +3196,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.103", "wasm-bindgen-shared", ] @@ -3049,7 +3218,7 @@ checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3186,6 +3355,6 @@ checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", "synstructure", ] diff --git a/Cargo.toml b/Cargo.toml index 4294fd6aa..107fb5d91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ members = [ ] [workspace.package] -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.6" edition = "2021" repository = "https://github.com/starkware-libs/cairo/" license = "Apache-2.0" @@ -71,8 +71,10 @@ path-clean = "0.1.0" pretty_assertions = "1.2.1" proc-macro2 = "1.0" quote = "1.0.21" -rayon = "0.9.0" +rayon = "1.7.0" +rstest = "0.16.0" salsa = "0.16.1" +scarb-metadata = "1.0.1" serde = { version = "1.0.130", features = ["derive"] } serde_json = "1.0" sha3 = "0.10.6" diff --git a/cairo b/cairo index 903337885..9c190561c 160000 --- a/cairo +++ b/cairo @@ -1 +1 @@ -Subproject commit 903337885fa19e28f59988a4952d670f72b44b34 +Subproject commit 9c190561ce1e8323665857f1a77082925c817b4c diff --git a/src/openzeppelin/lib.cairo b/src/openzeppelin/lib.cairo index 14f00389d..daffe3c5e 100644 --- a/src/openzeppelin/lib.cairo +++ b/src/openzeppelin/lib.cairo @@ -1 +1,2 @@ +mod token; mod tests; diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index ba51ab5d3..0117a1281 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -1 +1 @@ -mod test_mytest; +mod test_erc20; diff --git a/src/openzeppelin/tests/test_erc20.cairo b/src/openzeppelin/tests/test_erc20.cairo new file mode 100644 index 000000000..0b4f7f230 --- /dev/null +++ b/src/openzeppelin/tests/test_erc20.cairo @@ -0,0 +1,455 @@ +use openzeppelin::token::erc20::ERC20; +use starknet::contract_address_const; +use starknet::ContractAddress; +use starknet::testing::set_caller_address; +use integer::u256; +use integer::u256_from_felt252; + +// +// Constants +// + +const NAME: felt252 = 111; +const SYMBOL: felt252 = 222; + +fn MAX_U256() -> u256 { + u256 { + low: 0xffffffffffffffffffffffffffffffff_u128, high: 0xffffffffffffffffffffffffffffffff_u128 + } +} + +// +// Helper functions +// + +fn setup() -> (ContractAddress, u256) { + let initial_supply: u256 = u256_from_felt252(2000); + let account: ContractAddress = contract_address_const::<1>(); + // Set account as default caller + set_caller_address(account); + + ERC20::constructor(NAME, SYMBOL, initial_supply, account); + (account, initial_supply) +} + +fn set_caller_as_zero() { + set_caller_address(contract_address_const::<0>()); +} + +// +// Tests +// + +#[test] +#[available_gas(2000000)] +fn test_initializer() { + ERC20::initializer(NAME, SYMBOL); + + assert(ERC20::name() == NAME, 'Name should be NAME'); + assert(ERC20::symbol() == SYMBOL, 'Symbol should be SYMBOL'); + assert(ERC20::decimals() == 18_u8, 'Decimals should be 18'); + assert(ERC20::total_supply() == u256_from_felt252(0), 'Supply should eq 0'); +} + + +#[test] +#[available_gas(2000000)] +fn test_constructor() { + let initial_supply: u256 = u256_from_felt252(2000); + let account: ContractAddress = contract_address_const::<1>(); + let decimals: u8 = 18_u8; + + ERC20::constructor(NAME, SYMBOL, initial_supply, account); + + let owner_balance: u256 = ERC20::balance_of(account); + assert(owner_balance == initial_supply, 'Should eq inital_supply'); + + assert(ERC20::total_supply() == initial_supply, 'Should eq inital_supply'); + assert(ERC20::name() == NAME, 'Name should be NAME'); + assert(ERC20::symbol() == SYMBOL, 'Symbol should be SYMBOL'); + assert(ERC20::decimals() == decimals, 'Decimals should be 18'); +} + +#[test] +#[available_gas(2000000)] +fn test_approve() { + let (owner, supply) = setup(); + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt252(100); + + let success: bool = ERC20::approve(spender, amount); + assert(success, 'Should return true'); + assert(ERC20::allowance(owner, spender) == amount, 'Spender not approved correctly'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('ERC20: approve from 0', ))] +fn test_approve_from_zero() { + let (owner, supply) = setup(); + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt252(100); + + set_caller_as_zero(); + + ERC20::approve(spender, amount); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('ERC20: approve to 0', ))] +fn test_approve_to_zero() { + let (owner, supply) = setup(); + let spender: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt252(100); + + ERC20::approve(spender, amount); +} + +#[test] +#[available_gas(2000000)] +fn test__approve() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt252(100); + + ERC20::_approve(owner, spender, amount); + assert(ERC20::allowance(owner, spender) == amount, 'Spender not approved correctly'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('ERC20: approve from 0', ))] +fn test__approve_from_zero() { + let owner: ContractAddress = contract_address_const::<0>(); + let spender: ContractAddress = contract_address_const::<1>(); + let amount: u256 = u256_from_felt252(100); + ERC20::_approve(owner, spender, amount); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('ERC20: approve to 0', ))] +fn test__approve_to_zero() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt252(100); + ERC20::_approve(owner, spender, amount); +} + +#[test] +#[available_gas(2000000)] +fn test_transfer() { + let (sender, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt252(100); + let success: bool = ERC20::transfer(recipient, amount); + + assert(success, 'Should return true'); + assert(ERC20::balance_of(recipient) == amount, 'Balance should eq amount'); + assert(ERC20::balance_of(sender) == supply - amount, 'Should eq supply - amount'); + assert(ERC20::total_supply() == supply, 'Total supply should not change'); +} + +#[test] +#[available_gas(2000000)] +fn test__transfer() { + let (sender, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt252(100); + ERC20::_transfer(sender, recipient, amount); + + assert(ERC20::balance_of(recipient) == amount, 'Balance should eq amount'); + assert(ERC20::balance_of(sender) == supply - amount, 'Should eq supply - amount'); + assert(ERC20::total_supply() == supply, 'Total supply should not change'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('u256_sub Overflow', ))] +fn test__transfer_not_enough_balance() { + let (sender, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<2>(); + let amount: u256 = supply + u256_from_felt252(1); + ERC20::_transfer(sender, recipient, amount); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('ERC20: transfer from 0', ))] +fn test__transfer_from_zero() { + let sender: ContractAddress = contract_address_const::<0>(); + let recipient: ContractAddress = contract_address_const::<1>(); + let amount: u256 = u256_from_felt252(100); + ERC20::_transfer(sender, recipient, amount); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('ERC20: transfer to 0', ))] +fn test__transfer_to_zero() { + let (sender, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt252(100); + ERC20::_transfer(sender, recipient, amount); +} + +#[test] +#[available_gas(2000000)] +fn test_transfer_from() { + let (owner, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<2>(); + let spender: ContractAddress = contract_address_const::<3>(); + let amount: u256 = u256_from_felt252(100); + + ERC20::approve(spender, amount); + + set_caller_address(spender); + + let success: bool = ERC20::transfer_from(owner, recipient, amount); + assert(success, 'Should return true'); + + // Will dangle without setting as a var + let spender_allowance: u256 = ERC20::allowance(owner, spender); + + assert(ERC20::balance_of(recipient) == amount, 'Should eq amount'); + assert(ERC20::balance_of(owner) == supply - amount, 'Should eq suppy - amount'); + assert(spender_allowance == u256_from_felt252(0), 'Should eq 0'); + assert(ERC20::total_supply() == supply, 'Total supply should not change'); +} + +#[test] +#[available_gas(2000000)] +fn test_transfer_from_doesnt_consume_infinite_allowance() { + let (owner, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<2>(); + let spender: ContractAddress = contract_address_const::<3>(); + let amount: u256 = u256_from_felt252(100); + + ERC20::approve(spender, MAX_U256()); + + set_caller_address(spender); + ERC20::transfer_from(owner, recipient, amount); + + let spender_allowance: u256 = ERC20::allowance(owner, spender); + assert(spender_allowance == MAX_U256(), 'Allowance should not change'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('u256_sub Overflow', ))] +fn test_transfer_from_greater_than_allowance() { + let (owner, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<2>(); + let spender: ContractAddress = contract_address_const::<3>(); + let amount: u256 = u256_from_felt252(100); + let amount_plus_one: u256 = amount + u256_from_felt252(1); + + ERC20::approve(spender, amount); + + set_caller_address(spender); + + ERC20::transfer_from(owner, recipient, amount_plus_one); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('ERC20: transfer to 0', ))] +fn test_transfer_from_to_zero_address() { + let (owner, supply) = setup(); + + let recipient: ContractAddress = contract_address_const::<0>(); + let spender: ContractAddress = contract_address_const::<3>(); + let amount: u256 = u256_from_felt252(100); + + ERC20::approve(spender, amount); + + set_caller_address(spender); + + ERC20::transfer_from(owner, recipient, amount); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('u256_sub Overflow', ))] +fn test_transfer_from_from_zero_address() { + let (owner, supply) = setup(); + + let zero_address: ContractAddress = contract_address_const::<0>(); + let recipient: ContractAddress = contract_address_const::<2>(); + let spender: ContractAddress = contract_address_const::<3>(); + let amount: u256 = u256_from_felt252(100); + + set_caller_address(zero_address); + + ERC20::transfer_from(owner, recipient, amount); +} + +#[test] +#[available_gas(2000000)] +fn test_increase_allowance() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt252(100); + + ERC20::approve(spender, amount); + let success: bool = ERC20::increase_allowance(spender, amount); + assert(success, 'Should return true'); + + let spender_allowance: u256 = ERC20::allowance(owner, spender); + assert(spender_allowance == amount + amount, 'Should be amount * 2'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('ERC20: approve to 0', ))] +fn test_increase_allowance_to_zero_address() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt252(100); + + ERC20::increase_allowance(spender, amount); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('ERC20: approve from 0', ))] +fn test_increase_allowance_from_zero_address() { + let (owner, supply) = setup(); + + let zero_address: ContractAddress = contract_address_const::<0>(); + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt252(100); + + set_caller_address(zero_address); + + ERC20::increase_allowance(spender, amount); +} + +#[test] +#[available_gas(2000000)] +fn test_decrease_allowance() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt252(100); + + ERC20::approve(spender, amount); + let success: bool = ERC20::decrease_allowance(spender, amount); + assert(success, 'Should return true'); + + let spender_allowance: u256 = ERC20::allowance(owner, spender); + assert(spender_allowance == amount - amount, 'Should be 0'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('u256_sub Overflow', ))] +fn test_decrease_allowance_to_zero_address() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt252(100); + + ERC20::decrease_allowance(spender, amount); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('u256_sub Overflow', ))] +fn test_decrease_allowance_from_zero_address() { + let (owner, supply) = setup(); + + let zero_address: ContractAddress = contract_address_const::<0>(); + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt252(100); + + set_caller_address(zero_address); + + ERC20::decrease_allowance(spender, amount); +} + +#[test] +#[available_gas(2000000)] +fn test__spend_allowance_not_unlimited() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt252(100); + + ERC20::_approve(owner, spender, supply); + ERC20::_spend_allowance(owner, spender, amount); + assert(ERC20::allowance(owner, spender) == supply - amount, 'Should eq supply - amount'); +} + +#[test] +#[available_gas(2000000)] +fn test__spend_allowance_unlimited() { + let (owner, supply) = setup(); + + let spender: ContractAddress = contract_address_const::<2>(); + let max_minus_one: u256 = MAX_U256() - u256_from_felt252(1); + + ERC20::_approve(owner, spender, MAX_U256()); + ERC20::_spend_allowance(owner, spender, max_minus_one); + + assert(ERC20::allowance(owner, spender) == MAX_U256(), 'Allowance should not change'); +} + +#[test] +#[available_gas(2000000)] +fn test__mint() { + let minter: ContractAddress = contract_address_const::<2>(); + let amount: u256 = u256_from_felt252(100); + + ERC20::_mint(minter, amount); + + let minter_balance: u256 = ERC20::balance_of(minter); + assert(minter_balance == amount, 'Should eq amount'); + + assert(ERC20::total_supply() == amount, 'Should eq total supply'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('ERC20: mint to 0', ))] +fn test__mint_to_zero() { + let minter: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt252(100); + + ERC20::_mint(minter, amount); +} + +#[test] +#[available_gas(2000000)] +fn test__burn() { + let (owner, supply) = setup(); + + let amount: u256 = u256_from_felt252(100); + ERC20::_burn(owner, amount); + + assert(ERC20::total_supply() == supply - amount, 'Should eq supply - amount'); + assert(ERC20::balance_of(owner) == supply - amount, 'Should eq supply - amount'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('ERC20: burn from 0', ))] +fn test__burn_from_zero() { + setup(); + let zero_address: ContractAddress = contract_address_const::<0>(); + let amount: u256 = u256_from_felt252(100); + + ERC20::_burn(zero_address, amount); +} diff --git a/src/openzeppelin/tests/test_mytest.cairo b/src/openzeppelin/tests/test_mytest.cairo deleted file mode 100644 index 1f5f2ca0e..000000000 --- a/src/openzeppelin/tests/test_mytest.cairo +++ /dev/null @@ -1,5 +0,0 @@ -#[test] -#[available_gas(2000000)] -fn test_pass() { - assert(true, 'Should pass'); -} diff --git a/src/openzeppelin/token.cairo b/src/openzeppelin/token.cairo new file mode 100644 index 000000000..bfe4665e0 --- /dev/null +++ b/src/openzeppelin/token.cairo @@ -0,0 +1 @@ +mod erc20; diff --git a/src/openzeppelin/token/erc20.cairo b/src/openzeppelin/token/erc20.cairo new file mode 100644 index 000000000..ad231f2d2 --- /dev/null +++ b/src/openzeppelin/token/erc20.cairo @@ -0,0 +1,207 @@ +use starknet::ContractAddress; + +trait IERC20 { + fn name() -> felt252; + fn symbol() -> felt252; + fn decimals() -> u8; + fn total_supply() -> u256; + fn balance_of(account: ContractAddress) -> u256; + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256; + fn transfer(recipient: ContractAddress, amount: u256) -> bool; + fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool; + fn approve(spender: ContractAddress, amount: u256) -> bool; +} + +#[contract] +mod ERC20 { + use openzeppelin::token::erc20::IERC20; + use starknet::get_caller_address; + use starknet::ContractAddress; + use starknet::contract_address_const; + use starknet::ContractAddressZeroable; + use zeroable::Zeroable; + + struct Storage { + _name: felt252, + _symbol: felt252, + _total_supply: u256, + _balances: LegacyMap, + _allowances: LegacyMap<(ContractAddress, ContractAddress), u256>, + } + + #[event] + fn Transfer(from: ContractAddress, to: ContractAddress, value: u256) {} + + #[event] + fn Approval(owner: ContractAddress, spender: ContractAddress, value: u256) {} + + impl ERC20 of IERC20 { + fn name() -> felt252 { + _name::read() + } + + fn symbol() -> felt252 { + _symbol::read() + } + + fn decimals() -> u8 { + 18_u8 + } + + fn total_supply() -> u256 { + _total_supply::read() + } + + fn balance_of(account: ContractAddress) -> u256 { + _balances::read(account) + } + + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { + _allowances::read((owner, spender)) + } + + fn transfer(recipient: ContractAddress, amount: u256) -> bool { + let sender = get_caller_address(); + _transfer(sender, recipient, amount); + true + } + + fn transfer_from( + sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool { + let caller = get_caller_address(); + _spend_allowance(sender, caller, amount); + _transfer(sender, recipient, amount); + true + } + + fn approve(spender: ContractAddress, amount: u256) -> bool { + let caller = get_caller_address(); + _approve(caller, spender, amount); + true + } + } + + #[constructor] + fn constructor( + name: felt252, symbol: felt252, initial_supply: u256, recipient: ContractAddress + ) { + initializer(name, symbol); + _mint(recipient, initial_supply); + } + + #[view] + fn name() -> felt252 { + ERC20::name() + } + + #[view] + fn symbol() -> felt252 { + ERC20::symbol() + } + + #[view] + fn decimals() -> u8 { + ERC20::decimals() + } + + #[view] + fn total_supply() -> u256 { + ERC20::total_supply() + } + + #[view] + fn balance_of(account: ContractAddress) -> u256 { + ERC20::balance_of(account) + } + + #[view] + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { + ERC20::allowance(owner, spender) + } + + #[external] + fn transfer(recipient: ContractAddress, amount: u256) -> bool { + ERC20::transfer(recipient, amount) + } + + #[external] + fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { + ERC20::transfer_from(sender, recipient, amount) + } + + #[external] + fn approve(spender: ContractAddress, amount: u256) -> bool { + ERC20::approve(spender, amount) + } + + #[external] + fn increase_allowance(spender: ContractAddress, added_value: u256) -> bool { + _increase_allowance(spender, added_value) + } + + #[external] + fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { + _decrease_allowance(spender, subtracted_value) + } + + /// + /// Internals + /// + + fn initializer(name_: felt252, symbol_: felt252) { + _name::write(name_); + _symbol::write(symbol_); + } + + fn _increase_allowance(spender: ContractAddress, added_value: u256) -> bool { + let caller = get_caller_address(); + _approve(caller, spender, _allowances::read((caller, spender)) + added_value); + true + } + + fn _decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { + let caller = get_caller_address(); + _approve(caller, spender, _allowances::read((caller, spender)) - subtracted_value); + true + } + + fn _mint(recipient: ContractAddress, amount: u256) { + assert(!recipient.is_zero(), 'ERC20: mint to 0'); + _total_supply::write(_total_supply::read() + amount); + _balances::write(recipient, _balances::read(recipient) + amount); + Transfer(contract_address_const::<0>(), recipient, amount); + } + + fn _burn(account: ContractAddress, amount: u256) { + assert(!account.is_zero(), 'ERC20: burn from 0'); + _total_supply::write(_total_supply::read() - amount); + _balances::write(account, _balances::read(account) - amount); + Transfer(account, contract_address_const::<0>(), amount); + } + + fn _approve(owner: ContractAddress, spender: ContractAddress, amount: u256) { + assert(!owner.is_zero(), 'ERC20: approve from 0'); + assert(!spender.is_zero(), 'ERC20: approve to 0'); + _allowances::write((owner, spender), amount); + Approval(owner, spender, amount); + } + + fn _transfer(sender: ContractAddress, recipient: ContractAddress, amount: u256) { + assert(!sender.is_zero(), 'ERC20: transfer from 0'); + assert(!recipient.is_zero(), 'ERC20: transfer to 0'); + _balances::write(sender, _balances::read(sender) - amount); + _balances::write(recipient, _balances::read(recipient) + amount); + Transfer(sender, recipient, amount); + } + + fn _spend_allowance(owner: ContractAddress, spender: ContractAddress, amount: u256) { + let current_allowance = _allowances::read((owner, spender)); + let ONES_MASK = 0xffffffffffffffffffffffffffffffff_u128; + let is_unlimited_allowance = + current_allowance.low == ONES_MASK & current_allowance.high == ONES_MASK; + if !is_unlimited_allowance { + _approve(owner, spender, current_allowance - amount); + } + } +} From 91dcad3010fd82961347b881f09662f769e70dbe Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Fri, 31 Mar 2023 14:24:56 -0400 Subject: [PATCH 036/246] Migrate security/initializable (#592) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update cairo * fix path * add security mod * add initializable * add tests * Update src/openzeppelin/security/initializable.cairo Co-authored-by: Martín Triay * remove underscore * add internal macros --------- Co-authored-by: Martín Triay --- Makefile | 2 +- src/openzeppelin/lib.cairo | 1 + src/openzeppelin/security.cairo | 1 + src/openzeppelin/security/initializable.cairo | 17 +++++++++++++++++ src/openzeppelin/tests.cairo | 1 + src/openzeppelin/tests/test_initializable.cairo | 17 +++++++++++++++++ 6 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 src/openzeppelin/security.cairo create mode 100644 src/openzeppelin/security/initializable.cairo create mode 100644 src/openzeppelin/tests/test_initializable.cairo diff --git a/Makefile b/Makefile index 9ed2f2af3..7969ea043 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ build: cargo build test: - cargo run --bin cairo-test -- --starknet --path src/openzeppelin + cargo run --bin cairo-test -- --starknet --path $(SOURCE_FOLDER) format: cargo run --bin cairo-format -- --recursive $(SOURCE_FOLDER) --print-parsing-errors diff --git a/src/openzeppelin/lib.cairo b/src/openzeppelin/lib.cairo index daffe3c5e..74152ca25 100644 --- a/src/openzeppelin/lib.cairo +++ b/src/openzeppelin/lib.cairo @@ -1,2 +1,3 @@ +mod security; mod token; mod tests; diff --git a/src/openzeppelin/security.cairo b/src/openzeppelin/security.cairo new file mode 100644 index 000000000..c3e918b1e --- /dev/null +++ b/src/openzeppelin/security.cairo @@ -0,0 +1 @@ +mod initializable; diff --git a/src/openzeppelin/security/initializable.cairo b/src/openzeppelin/security/initializable.cairo new file mode 100644 index 000000000..80df38da6 --- /dev/null +++ b/src/openzeppelin/security/initializable.cairo @@ -0,0 +1,17 @@ +#[contract] +mod Initializable { + struct Storage { + initialized: bool, + } + + #[internal] + fn is_initialized() -> bool { + initialized::read() + } + + #[internal] + fn initialize() { + assert(!is_initialized(), 'Contract already initialized'); + initialized::write(true); + } +} diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index 0117a1281..4ef4b5247 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -1 +1,2 @@ mod test_erc20; +mod test_initializable; diff --git a/src/openzeppelin/tests/test_initializable.cairo b/src/openzeppelin/tests/test_initializable.cairo new file mode 100644 index 000000000..bcbb7c503 --- /dev/null +++ b/src/openzeppelin/tests/test_initializable.cairo @@ -0,0 +1,17 @@ +use openzeppelin::security::initializable::Initializable; + +#[test] +#[available_gas(2000000)] +fn test_initialize() { + assert(!Initializable::is_initialized(),'Should not be initialized'); + Initializable::initialize(); + assert(Initializable::is_initialized(),'Should be initialized'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('Contract already initialized', ))] +fn test_initialize_when_initialized() { + Initializable::initialize(); + Initializable::initialize(); +} From f48da2c00d4252204a53a5b496b445f2b87a1856 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 31 Mar 2023 20:31:00 +0200 Subject: [PATCH 037/246] Use zeroable::zero() instead of contract_address_const::<0>() (#598) * use zeroable for address * cleanup import --- src/openzeppelin/token/erc20.cairo | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/openzeppelin/token/erc20.cairo b/src/openzeppelin/token/erc20.cairo index ad231f2d2..8b84c1a1f 100644 --- a/src/openzeppelin/token/erc20.cairo +++ b/src/openzeppelin/token/erc20.cairo @@ -17,7 +17,6 @@ mod ERC20 { use openzeppelin::token::erc20::IERC20; use starknet::get_caller_address; use starknet::ContractAddress; - use starknet::contract_address_const; use starknet::ContractAddressZeroable; use zeroable::Zeroable; @@ -170,14 +169,14 @@ mod ERC20 { assert(!recipient.is_zero(), 'ERC20: mint to 0'); _total_supply::write(_total_supply::read() + amount); _balances::write(recipient, _balances::read(recipient) + amount); - Transfer(contract_address_const::<0>(), recipient, amount); + Transfer(Zeroable::zero(), recipient, amount); } fn _burn(account: ContractAddress, amount: u256) { assert(!account.is_zero(), 'ERC20: burn from 0'); _total_supply::write(_total_supply::read() - amount); _balances::write(account, _balances::read(account) - amount); - Transfer(account, contract_address_const::<0>(), amount); + Transfer(account, Zeroable::zero(), amount); } fn _approve(owner: ContractAddress, spender: ContractAddress, amount: u256) { From d671d4c849a90b9f005fef76287413d84fbf64e2 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Fri, 31 Mar 2023 14:46:10 -0400 Subject: [PATCH 038/246] Migrate security/pausable (#593) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix path * add security mod * add pausable * add mock * add tests * add security mod * update cargo * update cairo * Apply suggestions from code review Co-authored-by: Martín Triay * remove underscore from _paused * remove unused imports * add test assertion for is_paused * move mocks inside tests crate * remove underscore * add blank line --------- Co-authored-by: Martín Triay --- src/openzeppelin/security.cairo | 1 + src/openzeppelin/security/pausable.cairo | 39 +++++++++++++++ src/openzeppelin/tests.cairo | 2 + src/openzeppelin/tests/mocks.cairo | 1 + .../tests/mocks/mock_pausable.cairo | 34 ++++++++++++++ src/openzeppelin/tests/test_pausable.cairo | 47 +++++++++++++++++++ 6 files changed, 124 insertions(+) create mode 100644 src/openzeppelin/security/pausable.cairo create mode 100644 src/openzeppelin/tests/mocks.cairo create mode 100644 src/openzeppelin/tests/mocks/mock_pausable.cairo create mode 100644 src/openzeppelin/tests/test_pausable.cairo diff --git a/src/openzeppelin/security.cairo b/src/openzeppelin/security.cairo index c3e918b1e..871349507 100644 --- a/src/openzeppelin/security.cairo +++ b/src/openzeppelin/security.cairo @@ -1 +1,2 @@ +mod pausable; mod initializable; diff --git a/src/openzeppelin/security/pausable.cairo b/src/openzeppelin/security/pausable.cairo new file mode 100644 index 000000000..4fcd68384 --- /dev/null +++ b/src/openzeppelin/security/pausable.cairo @@ -0,0 +1,39 @@ +#[contract] +mod Pausable { + use starknet::ContractAddress; + use starknet::get_caller_address; + + struct Storage { + paused: bool, + } + + #[event] + fn Paused(account: ContractAddress) {} + + #[event] + fn Unpaused(account: ContractAddress) {} + + fn is_paused() -> bool { + paused::read() + } + + fn assert_not_paused() { + assert(!is_paused(), 'Pausable: paused'); + } + + fn assert_paused() { + assert(is_paused(), 'Pausable: not paused'); + } + + fn pause() { + assert_not_paused(); + paused::write(true); + Paused(get_caller_address()); + } + + fn unpause() { + assert_paused(); + paused::write(false); + Unpaused(get_caller_address()); + } +} diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index 4ef4b5247..dee2e7cfa 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -1,2 +1,4 @@ mod test_erc20; +mod test_pausable; mod test_initializable; +mod mocks; diff --git a/src/openzeppelin/tests/mocks.cairo b/src/openzeppelin/tests/mocks.cairo new file mode 100644 index 000000000..60df001ef --- /dev/null +++ b/src/openzeppelin/tests/mocks.cairo @@ -0,0 +1 @@ +mod mock_pausable; diff --git a/src/openzeppelin/tests/mocks/mock_pausable.cairo b/src/openzeppelin/tests/mocks/mock_pausable.cairo new file mode 100644 index 000000000..799449b4a --- /dev/null +++ b/src/openzeppelin/tests/mocks/mock_pausable.cairo @@ -0,0 +1,34 @@ +#[contract] +mod MockPausable { + use openzeppelin::security::pausable::Pausable; + + struct Storage { + counter: felt252 + } + + #[view] + fn is_paused() -> bool { + Pausable::is_paused() + } + + #[view] + fn get_count() -> felt252 { + counter::read() + } + + #[external] + fn assert_unpaused_and_increment() { + Pausable::assert_not_paused(); + counter::write(counter::read() + 1); + } + + #[external] + fn pause() { + Pausable::pause(); + } + + #[external] + fn unpause() { + Pausable::unpause(); + } +} diff --git a/src/openzeppelin/tests/test_pausable.cairo b/src/openzeppelin/tests/test_pausable.cairo new file mode 100644 index 000000000..6fe484505 --- /dev/null +++ b/src/openzeppelin/tests/test_pausable.cairo @@ -0,0 +1,47 @@ +use openzeppelin::tests::mocks::mock_pausable::MockPausable; + +#[test] +#[available_gas(2000000)] +fn test_pause_when_unpaused() { + assert(! MockPausable::is_paused(), 'Should not be paused'); + assert(MockPausable::get_count() == 0, 'Should be 0'); + MockPausable::assert_unpaused_and_increment(); + assert(MockPausable::get_count() == 1, 'Should increment'); + MockPausable::pause(); + assert(MockPausable::is_paused(), 'Should be paused'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('Pausable: paused', ))] +fn test_pause_when_paused() { + MockPausable::pause(); + MockPausable::pause(); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('Pausable: paused', ))] +fn test_pause_increment() { + MockPausable::pause(); + MockPausable::assert_unpaused_and_increment(); +} + +#[test] +#[available_gas(2000000)] +fn test_unpause_when_paused() { + MockPausable::pause(); + assert(MockPausable::is_paused(), 'Should be paused'); + MockPausable::unpause(); + assert(! MockPausable::is_paused(), 'Should not be paused'); + MockPausable::assert_unpaused_and_increment(); + assert(MockPausable::get_count() == 1, 'Should increment'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('Pausable: not paused', ))] +fn test_unpause_when_unpaused() { + assert(! MockPausable::is_paused(), 'Should be unpaused'); + MockPausable::unpause(); +} From e86bcd2107b052e13c58a9af7907e1f82e427a60 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 18 Apr 2023 11:58:18 -0400 Subject: [PATCH 039/246] add utils and constants --- src/openzeppelin/lib.cairo | 1 + src/openzeppelin/utils.cairo | 1 + src/openzeppelin/utils/constants.cairo | 32 ++++++++++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 src/openzeppelin/utils.cairo create mode 100644 src/openzeppelin/utils/constants.cairo diff --git a/src/openzeppelin/lib.cairo b/src/openzeppelin/lib.cairo index 74152ca25..c0f1f3ad0 100644 --- a/src/openzeppelin/lib.cairo +++ b/src/openzeppelin/lib.cairo @@ -1,3 +1,4 @@ mod security; mod token; mod tests; +mod utils; diff --git a/src/openzeppelin/utils.cairo b/src/openzeppelin/utils.cairo new file mode 100644 index 000000000..3709db21f --- /dev/null +++ b/src/openzeppelin/utils.cairo @@ -0,0 +1 @@ +mod constants; diff --git a/src/openzeppelin/utils/constants.cairo b/src/openzeppelin/utils/constants.cairo new file mode 100644 index 000000000..84fbe24b9 --- /dev/null +++ b/src/openzeppelin/utils/constants.cairo @@ -0,0 +1,32 @@ +// +// Interface ids +// + +// ERC165 +const IERC165_ID: u32 = 0x01ffc9a7_u32; +const INVALID_ID: u32 = 0xffffffff_u32; + +// Account +const IACCOUNT_ID: u32 = 0xa66bd575_u32; + +// ERC721 +const IERC721_ID: u32 = 0x80ac58cd_u32; +const IERC721_RECEIVER_ID: u32 = 0x150b7a02_u32; +const IERC721_METADATA_ID: u32 = 0x5b5e139f_u32; +const IERC721_ENUMERABLE_ID: u32 = 0x780e9d63_u32; + +// ERC1155 +const IERC1155_ID: u32 = 0xd9b67a26_u32; +const IERC1155_METADATA_ID: u32 = 0x0e89341c_u32; +const IERC1155_RECEIVER_ID: u32 = 0x4e2312e0_u32; +const ON_ERC1155_RECEIVED_SELECTOR: u32 = 0xf23a6e61_u32; +const ON_ERC1155_BATCH_RECEIVED_SELECTOR: u32 = 0xbc197c81_u32; + +// AccessControl +const IACCESSCONTROL_ID: u32 = 0x7965db0b_u32; + +// +// Roles +// + +const DEFAULT_ADMIN_ROLE: felt252 = 0x00; From 42a160f1f011414e5044b5174e56dcaa73876aae Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 18 Apr 2023 12:02:28 -0400 Subject: [PATCH 040/246] Revert "add utils and constants" This reverts commit e86bcd2107b052e13c58a9af7907e1f82e427a60. --- src/openzeppelin/lib.cairo | 1 - src/openzeppelin/utils.cairo | 1 - src/openzeppelin/utils/constants.cairo | 32 -------------------------- 3 files changed, 34 deletions(-) delete mode 100644 src/openzeppelin/utils.cairo delete mode 100644 src/openzeppelin/utils/constants.cairo diff --git a/src/openzeppelin/lib.cairo b/src/openzeppelin/lib.cairo index c0f1f3ad0..74152ca25 100644 --- a/src/openzeppelin/lib.cairo +++ b/src/openzeppelin/lib.cairo @@ -1,4 +1,3 @@ mod security; mod token; mod tests; -mod utils; diff --git a/src/openzeppelin/utils.cairo b/src/openzeppelin/utils.cairo deleted file mode 100644 index 3709db21f..000000000 --- a/src/openzeppelin/utils.cairo +++ /dev/null @@ -1 +0,0 @@ -mod constants; diff --git a/src/openzeppelin/utils/constants.cairo b/src/openzeppelin/utils/constants.cairo deleted file mode 100644 index 84fbe24b9..000000000 --- a/src/openzeppelin/utils/constants.cairo +++ /dev/null @@ -1,32 +0,0 @@ -// -// Interface ids -// - -// ERC165 -const IERC165_ID: u32 = 0x01ffc9a7_u32; -const INVALID_ID: u32 = 0xffffffff_u32; - -// Account -const IACCOUNT_ID: u32 = 0xa66bd575_u32; - -// ERC721 -const IERC721_ID: u32 = 0x80ac58cd_u32; -const IERC721_RECEIVER_ID: u32 = 0x150b7a02_u32; -const IERC721_METADATA_ID: u32 = 0x5b5e139f_u32; -const IERC721_ENUMERABLE_ID: u32 = 0x780e9d63_u32; - -// ERC1155 -const IERC1155_ID: u32 = 0xd9b67a26_u32; -const IERC1155_METADATA_ID: u32 = 0x0e89341c_u32; -const IERC1155_RECEIVER_ID: u32 = 0x4e2312e0_u32; -const ON_ERC1155_RECEIVED_SELECTOR: u32 = 0xf23a6e61_u32; -const ON_ERC1155_BATCH_RECEIVED_SELECTOR: u32 = 0xbc197c81_u32; - -// AccessControl -const IACCESSCONTROL_ID: u32 = 0x7965db0b_u32; - -// -// Roles -// - -const DEFAULT_ADMIN_ROLE: felt252 = 0x00; From 598c2324f2ff2c558f477abbec6fdb5faa43ee8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Wed, 26 Apr 2023 08:58:20 -0300 Subject: [PATCH 041/246] Migrate ERC165 (#582) * Apply suggestions from code review Co-authored-by: Andrew Fleming Co-authored-by: Hadrien Croubois * apply review suggestions * remove unused import * add internal macro * add deregister --------- Co-authored-by: Andrew Fleming Co-authored-by: Hadrien Croubois --- Cargo.lock | 12 ++--- src/openzeppelin/introspection.cairo | 1 + src/openzeppelin/introspection/erc165.cairo | 41 +++++++++++++++ src/openzeppelin/lib.cairo | 1 + src/openzeppelin/tests.cairo | 1 + src/openzeppelin/tests/test_erc165.cairo | 57 +++++++++++++++++++++ 6 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 src/openzeppelin/introspection.cairo create mode 100644 src/openzeppelin/introspection/erc165.cairo create mode 100644 src/openzeppelin/tests/test_erc165.cairo diff --git a/Cargo.lock b/Cargo.lock index 2094098a2..02e70d1c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2236,9 +2236,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.52" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" +checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" dependencies = [ "unicode-ident", ] @@ -2542,7 +2542,7 @@ checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" dependencies = [ "proc-macro2", "quote", - "syn 2.0.3", + "syn 2.0.9", ] [[package]] @@ -2756,9 +2756,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.3" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8234ae35e70582bfa0f1fedffa6daa248e41dd045310b19800c4a36382c8f60" +checksum = "0da4a3c17e109f700685ec577c0f85efd9b19bcf15c913985f14dc1ac01775aa" dependencies = [ "proc-macro2", "quote", @@ -2887,7 +2887,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.3", + "syn 2.0.9", ] [[package]] diff --git a/src/openzeppelin/introspection.cairo b/src/openzeppelin/introspection.cairo new file mode 100644 index 000000000..4391460f4 --- /dev/null +++ b/src/openzeppelin/introspection.cairo @@ -0,0 +1 @@ +mod erc165; diff --git a/src/openzeppelin/introspection/erc165.cairo b/src/openzeppelin/introspection/erc165.cairo new file mode 100644 index 000000000..10eb980a9 --- /dev/null +++ b/src/openzeppelin/introspection/erc165.cairo @@ -0,0 +1,41 @@ +const IERC165_ID: u32 = 0x01ffc9a7_u32; +const INVALID_ID: u32 = 0xffffffff_u32; + +trait IERC165 { + fn supports_interface(interface_id: u32) -> bool; +} + +#[contract] +mod ERC165 { + use openzeppelin::introspection::erc165; + + struct Storage { + supported_interfaces: LegacyMap, + } + + impl ERC165 of erc165::IERC165 { + fn supports_interface(interface_id: u32) -> bool { + if interface_id == erc165::IERC165_ID { + return true; + } + supported_interfaces::read(interface_id) + } + } + + #[view] + fn supports_interface(interface_id: u32) -> bool { + ERC165::supports_interface(interface_id) + } + + #[internal] + fn register_interface(interface_id: u32) { + assert(interface_id != erc165::INVALID_ID, 'Invalid id'); + supported_interfaces::write(interface_id, true); + } + + #[internal] + fn deregister_interface(interface_id: u32) { + assert(interface_id != erc165::IERC165_ID, 'Invalid id'); + supported_interfaces::write(interface_id, false); + } +} diff --git a/src/openzeppelin/lib.cairo b/src/openzeppelin/lib.cairo index 74152ca25..97e2a1021 100644 --- a/src/openzeppelin/lib.cairo +++ b/src/openzeppelin/lib.cairo @@ -1,3 +1,4 @@ +mod introspection; mod security; mod token; mod tests; diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index dee2e7cfa..ac9e6b924 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -1,3 +1,4 @@ +mod test_erc165; mod test_erc20; mod test_pausable; mod test_initializable; diff --git a/src/openzeppelin/tests/test_erc165.cairo b/src/openzeppelin/tests/test_erc165.cairo new file mode 100644 index 000000000..79b1c40cd --- /dev/null +++ b/src/openzeppelin/tests/test_erc165.cairo @@ -0,0 +1,57 @@ +use openzeppelin::introspection::erc165::ERC165; +use openzeppelin::introspection::erc165::IERC165_ID; +use openzeppelin::introspection::erc165::INVALID_ID; + +const OTHER_ID: u32 = 0x12345678_u32; + +#[test] +#[available_gas(2000000)] +fn test_default_behavior() { + let supports_default_interface: bool = ERC165::supports_interface(IERC165_ID); + assert(supports_default_interface, 'Should support base interface'); +} + +#[test] +#[available_gas(2000000)] +fn test_not_registered_interface() { + let supports_unregistered_interface: bool = ERC165::supports_interface(OTHER_ID); + assert(! supports_unregistered_interface, 'Should not support unregistered'); +} + +#[test] +#[available_gas(2000000)] +fn test_supports_invalid_interface() { + let supports_invalid_interface: bool = ERC165::supports_interface(INVALID_ID); + assert(! supports_invalid_interface, 'Should not support invalid id'); +} + +#[test] +#[available_gas(2000000)] +fn test_register_interface() { + ERC165::register_interface(OTHER_ID); + let supports_new_interface: bool = ERC165::supports_interface(OTHER_ID); + assert(supports_new_interface, 'Should support new interface'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('Invalid id', ))] +fn test_register_invalid_interface() { + ERC165::register_interface(INVALID_ID); +} + +#[test] +#[available_gas(2000000)] +fn test_deregister_interface() { + ERC165::register_interface(OTHER_ID); + ERC165::deregister_interface(OTHER_ID); + let supports_old_interface: bool = ERC165::supports_interface(OTHER_ID); + assert(! supports_old_interface, 'Should not support interface'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected = ('Invalid id', ))] +fn test_deregister_default_interface() { + ERC165::deregister_interface(IERC165_ID); +} From b2624eed4ff3261271b2621f0316320bad0746b5 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Wed, 26 Apr 2023 13:43:08 -0400 Subject: [PATCH 042/246] Migrate constants (#611) * add utils and constants * simplify role val * add context comments for interface ids --- src/openzeppelin/lib.cairo | 1 + src/openzeppelin/utils.cairo | 1 + src/openzeppelin/utils/constants.cairo | 38 ++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 src/openzeppelin/utils.cairo create mode 100644 src/openzeppelin/utils/constants.cairo diff --git a/src/openzeppelin/lib.cairo b/src/openzeppelin/lib.cairo index 97e2a1021..14a9204a4 100644 --- a/src/openzeppelin/lib.cairo +++ b/src/openzeppelin/lib.cairo @@ -2,3 +2,4 @@ mod introspection; mod security; mod token; mod tests; +mod utils; diff --git a/src/openzeppelin/utils.cairo b/src/openzeppelin/utils.cairo new file mode 100644 index 000000000..3709db21f --- /dev/null +++ b/src/openzeppelin/utils.cairo @@ -0,0 +1 @@ +mod constants; diff --git a/src/openzeppelin/utils/constants.cairo b/src/openzeppelin/utils/constants.cairo new file mode 100644 index 000000000..d7346bf55 --- /dev/null +++ b/src/openzeppelin/utils/constants.cairo @@ -0,0 +1,38 @@ +// +// Interface ids +// + +// ERC165 +// See: https://eips.ethereum.org/EIPS/eip-165 +const IERC165_ID: u32 = 0x01ffc9a7_u32; +const INVALID_ID: u32 = 0xffffffff_u32; + +// Account +// See: https://github.com/OpenZeppelin/cairo-contracts/pull/449#discussion_r966242914 +const IACCOUNT_ID: u32 = 0xa66bd575_u32; + +// ERC721 +// See: https://eips.ethereum.org/EIPS/eip-721 +const IERC721_ID: u32 = 0x80ac58cd_u32; +const IERC721_RECEIVER_ID: u32 = 0x150b7a02_u32; +const IERC721_METADATA_ID: u32 = 0x5b5e139f_u32; +const IERC721_ENUMERABLE_ID: u32 = 0x780e9d63_u32; + +// ERC1155 +// See: https://eips.ethereum.org/EIPS/eip-1155 +const IERC1155_ID: u32 = 0xd9b67a26_u32; +const IERC1155_METADATA_ID: u32 = 0x0e89341c_u32; +const IERC1155_RECEIVER_ID: u32 = 0x4e2312e0_u32; +const ON_ERC1155_RECEIVED_SELECTOR: u32 = 0xf23a6e61_u32; +const ON_ERC1155_BATCH_RECEIVED_SELECTOR: u32 = 0xbc197c81_u32; + +// AccessControl +// Calculated from XOR of all function selectors in IAccessControl. +// See: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/IAccessControl.sol +const IACCESSCONTROL_ID: u32 = 0x7965db0b_u32; + +// +// Roles +// + +const DEFAULT_ADMIN_ROLE: felt252 = 0; From 4a4bca98998f13e93b41c7dd69e0ad305c8e77b7 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Thu, 27 Apr 2023 16:52:45 -0400 Subject: [PATCH 043/246] Set up new CI (#599) * add lint and test * fix name * add md linter * add header * remove extra line * add blank line * remove env * change name * fix on condition * change name * fix formatting * remove trailing comma in storage --- .github/workflows/coverage.yml | 52 ------------------ .github/workflows/release.yml | 41 -------------- .github/workflows/test.yml | 54 +++++++------------ .markdownlint.jsonc | 5 ++ .markdownlintrc | 8 --- PULL_REQUEST_TEMPLATE.md | 1 - SECURITY.md | 4 +- src/openzeppelin/security/initializable.cairo | 2 +- src/openzeppelin/security/pausable.cairo | 2 +- .../tests/test_initializable.cairo | 4 +- src/openzeppelin/tests/test_pausable.cairo | 6 +-- 11 files changed, 34 insertions(+), 145 deletions(-) delete mode 100644 .github/workflows/coverage.yml delete mode 100644 .github/workflows/release.yml create mode 100644 .markdownlint.jsonc delete mode 100644 .markdownlintrc diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml deleted file mode 100644 index 4aee047f0..000000000 --- a/.github/workflows/coverage.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Coverage and linter - -on: - pull_request: - workflow_dispatch: - push: - branches: - - main - -jobs: - linter: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Markdown Linter - uses: docker://avtodev/markdown-lint:v1 - with: - args: README.md CONTRIBUTING.md docs - - name: Set up Python 3.8 - uses: actions/setup-python@v2 - with: - python-version: 3.8 - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install flake8 - - name: Lint with flake8 - run: | - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - coverage: - runs-on: ubuntu-latest - needs: [linter] - steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.8 - uses: actions/setup-python@v2 - with: - python-version: 3.8 - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pytest tox - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - - name: Run tests and collect coverage - run: | - tox -e coverage -- --xml - - name: Upload coverage reports to Codecov with GitHub Action - uses: codecov/codecov-action@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 4474058a5..000000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Build and Release - -on: - workflow_run: - workflows: [Test] - types: - - completed - -jobs: - dist: - runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' }} - name: Build package - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-python@v2 - with: - python-version: "3.8" - - run: python -m pip install -U setuptools - - run: python -m pip install -e .[testing] - - name: Build package - run: tox -e build - - uses: actions/upload-artifact@v2 - with: - name: dist - path: dist - - dist_upload: - runs-on: ubuntu-latest - needs: [dist] - name: Upload to PyPi - steps: - - uses: actions/download-artifact@v2 - with: - name: dist - path: dist - - name: Publish package to PyPI - uses: pypa/gh-action-pypi-publish@master - with: - user: __token__ - password: ${{ secrets.pypi_token }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d199db298..fa705adfa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,44 +1,28 @@ -name: Test +name: Lint and test on: + pull_request: + branches: + - cairo-1 push: - tags: - - "v*" + branches: + - cairo-1 jobs: - validate: + lint_and_test: + name: Lint and test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - name: Get commits - id: commits - run: | - echo "MAIN=$(git show -s --format="%H" origin/main)" >> $GITHUB_OUTPUT - echo "TAG=$(git rev-list -n 1 ${GITHUB_REF#refs/*/})" >> $GITHUB_OUTPUT - - name: Print commits - run: | - echo "Main commit: ${{ steps.commits.outputs.MAIN }}" - echo "Tag commit: ${{ steps.commits.outputs.TAG }}" - - name: Compare commits - if: ${{ steps.commits.outputs.MAIN != steps.commits.outputs.TAG }} - uses: actions/github-script@d556feaca394842dc55e4734bf3bb9f685482fa0 # v6.3.3 + - uses: actions/checkout@v3 with: - script: | - core.setFailed('Tagged commit does not match main') - - test: - runs-on: ubuntu-latest - needs: [validate] - steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.8 - uses: actions/setup-python@v2 + submodules: recursive + - name: Markdown lint + uses: DavidAnson/markdownlint-cli2-action@5b7c9f74fec47e6b15667b2cc23c63dff11e449e # v9 with: - python-version: 3.8 - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pytest tox - - name: Run tests - run: | - tox + globs: | + *.md + !PULL_REQUEST_TEMPLATE.md + - name: Cairo lint + run: make check-format + - name: Cairo test + run: make test diff --git a/.markdownlint.jsonc b/.markdownlint.jsonc new file mode 100644 index 000000000..97c4068dc --- /dev/null +++ b/.markdownlint.jsonc @@ -0,0 +1,5 @@ +{ + // Disable line length check to enable paragraphs without internal line breaks. + // See https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md013---line-length + "MD013": false +} diff --git a/.markdownlintrc b/.markdownlintrc deleted file mode 100644 index a90ec93ab..000000000 --- a/.markdownlintrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - // Disable line length check to enable paragraphs without internal line breaks. - // See https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md013---line-length - "MD013": false, - // Disable inline HTML check to enable duplicate headers with separate ids. - // See https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md033---inline-html - "MD033": false -} diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 5d0b808a3..f751872d8 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -8,7 +8,6 @@ Fixes #??? - #### PR Checklist diff --git a/SECURITY.md b/SECURITY.md index 7c633259e..8bd19014b 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,4 +1,6 @@ +# Security + > ⚠️ Warning! ⚠️ > This project is still in a very early and experimental phase. It has never been audited nor thoroughly reviewed for security vulnerabilities. Do not use in production. -Please report any security issues you find to security@openzeppelin.com. \ No newline at end of file +Please report any security issues you find to security@openzeppelin.com. diff --git a/src/openzeppelin/security/initializable.cairo b/src/openzeppelin/security/initializable.cairo index 80df38da6..00079d280 100644 --- a/src/openzeppelin/security/initializable.cairo +++ b/src/openzeppelin/security/initializable.cairo @@ -1,7 +1,7 @@ #[contract] mod Initializable { struct Storage { - initialized: bool, + initialized: bool } #[internal] diff --git a/src/openzeppelin/security/pausable.cairo b/src/openzeppelin/security/pausable.cairo index 4fcd68384..0a9018f51 100644 --- a/src/openzeppelin/security/pausable.cairo +++ b/src/openzeppelin/security/pausable.cairo @@ -4,7 +4,7 @@ mod Pausable { use starknet::get_caller_address; struct Storage { - paused: bool, + paused: bool } #[event] diff --git a/src/openzeppelin/tests/test_initializable.cairo b/src/openzeppelin/tests/test_initializable.cairo index bcbb7c503..37e1b6b78 100644 --- a/src/openzeppelin/tests/test_initializable.cairo +++ b/src/openzeppelin/tests/test_initializable.cairo @@ -3,9 +3,9 @@ use openzeppelin::security::initializable::Initializable; #[test] #[available_gas(2000000)] fn test_initialize() { - assert(!Initializable::is_initialized(),'Should not be initialized'); + assert(!Initializable::is_initialized(), 'Should not be initialized'); Initializable::initialize(); - assert(Initializable::is_initialized(),'Should be initialized'); + assert(Initializable::is_initialized(), 'Should be initialized'); } #[test] diff --git a/src/openzeppelin/tests/test_pausable.cairo b/src/openzeppelin/tests/test_pausable.cairo index 6fe484505..598f98be9 100644 --- a/src/openzeppelin/tests/test_pausable.cairo +++ b/src/openzeppelin/tests/test_pausable.cairo @@ -3,7 +3,7 @@ use openzeppelin::tests::mocks::mock_pausable::MockPausable; #[test] #[available_gas(2000000)] fn test_pause_when_unpaused() { - assert(! MockPausable::is_paused(), 'Should not be paused'); + assert(!MockPausable::is_paused(), 'Should not be paused'); assert(MockPausable::get_count() == 0, 'Should be 0'); MockPausable::assert_unpaused_and_increment(); assert(MockPausable::get_count() == 1, 'Should increment'); @@ -33,7 +33,7 @@ fn test_unpause_when_paused() { MockPausable::pause(); assert(MockPausable::is_paused(), 'Should be paused'); MockPausable::unpause(); - assert(! MockPausable::is_paused(), 'Should not be paused'); + assert(!MockPausable::is_paused(), 'Should not be paused'); MockPausable::assert_unpaused_and_increment(); assert(MockPausable::get_count() == 1, 'Should increment'); } @@ -42,6 +42,6 @@ fn test_unpause_when_paused() { #[available_gas(2000000)] #[should_panic(expected = ('Pausable: not paused', ))] fn test_unpause_when_unpaused() { - assert(! MockPausable::is_paused(), 'Should be unpaused'); + assert(!MockPausable::is_paused(), 'Should be unpaused'); MockPausable::unpause(); } From 09620519f369306bd9afe7b9870506aa415b0530 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Thu, 27 Apr 2023 22:42:24 -0400 Subject: [PATCH 044/246] fix formatting (#613) --- src/openzeppelin/introspection/erc165.cairo | 6 +++--- src/openzeppelin/tests/test_erc165.cairo | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/openzeppelin/introspection/erc165.cairo b/src/openzeppelin/introspection/erc165.cairo index 10eb980a9..16f13205a 100644 --- a/src/openzeppelin/introspection/erc165.cairo +++ b/src/openzeppelin/introspection/erc165.cairo @@ -2,7 +2,7 @@ const IERC165_ID: u32 = 0x01ffc9a7_u32; const INVALID_ID: u32 = 0xffffffff_u32; trait IERC165 { - fn supports_interface(interface_id: u32) -> bool; + fn supports_interface(interface_id: u32) -> bool; } #[contract] @@ -10,7 +10,7 @@ mod ERC165 { use openzeppelin::introspection::erc165; struct Storage { - supported_interfaces: LegacyMap, + supported_interfaces: LegacyMap } impl ERC165 of erc165::IERC165 { @@ -26,7 +26,7 @@ mod ERC165 { fn supports_interface(interface_id: u32) -> bool { ERC165::supports_interface(interface_id) } - + #[internal] fn register_interface(interface_id: u32) { assert(interface_id != erc165::INVALID_ID, 'Invalid id'); diff --git a/src/openzeppelin/tests/test_erc165.cairo b/src/openzeppelin/tests/test_erc165.cairo index 79b1c40cd..7a4efeac8 100644 --- a/src/openzeppelin/tests/test_erc165.cairo +++ b/src/openzeppelin/tests/test_erc165.cairo @@ -15,14 +15,14 @@ fn test_default_behavior() { #[available_gas(2000000)] fn test_not_registered_interface() { let supports_unregistered_interface: bool = ERC165::supports_interface(OTHER_ID); - assert(! supports_unregistered_interface, 'Should not support unregistered'); + assert(!supports_unregistered_interface, 'Should not support unregistered'); } #[test] #[available_gas(2000000)] fn test_supports_invalid_interface() { let supports_invalid_interface: bool = ERC165::supports_interface(INVALID_ID); - assert(! supports_invalid_interface, 'Should not support invalid id'); + assert(!supports_invalid_interface, 'Should not support invalid id'); } #[test] @@ -46,7 +46,7 @@ fn test_deregister_interface() { ERC165::register_interface(OTHER_ID); ERC165::deregister_interface(OTHER_ID); let supports_old_interface: bool = ERC165::supports_interface(OTHER_ID); - assert(! supports_old_interface, 'Should not support interface'); + assert(!supports_old_interface, 'Should not support interface'); } #[test] From a1950eec131bc280877fa7a90c7ade291415a76c Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Fri, 28 Apr 2023 11:34:52 -0400 Subject: [PATCH 045/246] Normalize error message style (#606) * normalize error msg * update cairo * update Cargo * fix test command * update should_panic syntax * use super for interface import --- Cargo.lock | 522 ++++++++++-------- Cargo.toml | 16 +- Makefile | 2 +- cairo | 2 +- src/openzeppelin/security/initializable.cairo | 2 +- src/openzeppelin/tests/test_erc20.cairo | 32 +- .../tests/test_initializable.cairo | 2 +- src/openzeppelin/tests/test_pausable.cairo | 6 +- src/openzeppelin/token/erc20.cairo | 2 +- 9 files changed, 329 insertions(+), 257 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 02e70d1c7..e4ddc69bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,17 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.20" @@ -22,9 +33,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.66" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "ark-ff" @@ -182,7 +193,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -205,17 +216,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "bigdecimal" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aaf33151a6429fe9211d1b276eafdf70cdff28b071e76c0b0e1503221ea3744" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits 0.2.15", -] - [[package]] name = "bimap" version = "0.6.2" @@ -224,9 +224,9 @@ checksum = "bc0455254eb5c6964c4545d8bac815e1a1be4f3afe0ae695ea539c12d728d44b" [[package]] name = "bincode" -version = "1.3.3" +version = "2.0.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +checksum = "7bb50c5a2ef4b9b1e7ae73e3a73b52ea24b20312d629f9c4df28260b7ad2c3c4" dependencies = [ "serde", ] @@ -253,12 +253,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "block-buffer" -version = "0.9.0" +name = "bitvec" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ - "generic-array", + "funty", + "radium", + "tap", + "wyz", ] [[package]] @@ -294,9 +297,9 @@ checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" [[package]] name = "cairo-felt" -version = "0.1.1" +version = "0.3.0-rc1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a792f409586c42c2d62cff582989e573d45b0855be95e022421d25f608364cc1" +checksum = "a93dedd19b8edf685798f1f12e4e0ac21ac196ea5262c300783f69f3fa0cb28b" dependencies = [ "lazy_static", "num-bigint", @@ -307,7 +310,7 @@ dependencies = [ [[package]] name = "cairo-lang-casm" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "cairo-lang-utils", "env_logger", @@ -324,7 +327,7 @@ dependencies = [ [[package]] name = "cairo-lang-compiler" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "anyhow", "cairo-lang-defs", @@ -339,7 +342,7 @@ dependencies = [ "cairo-lang-sierra-generator", "cairo-lang-syntax", "cairo-lang-utils", - "clap 4.0.26", + "clap", "log", "salsa", "test-log", @@ -348,7 +351,7 @@ dependencies = [ [[package]] name = "cairo-lang-debug" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "cairo-lang-proc-macros", "cairo-lang-utils", @@ -359,7 +362,7 @@ dependencies = [ [[package]] name = "cairo-lang-defs" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "cairo-lang-debug", "cairo-lang-diagnostics", @@ -380,7 +383,7 @@ dependencies = [ [[package]] name = "cairo-lang-diagnostics" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "cairo-lang-filesystem", "cairo-lang-proc-macros", @@ -395,7 +398,7 @@ dependencies = [ [[package]] name = "cairo-lang-eq-solver" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "cairo-lang-utils", "env_logger", @@ -409,20 +412,22 @@ dependencies = [ [[package]] name = "cairo-lang-filesystem" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "cairo-lang-debug", "cairo-lang-utils", "env_logger", "path-clean", "salsa", + "serde", + "serde_json", "smol_str", "test-log", ] [[package]] name = "cairo-lang-formatter" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "anyhow", "cairo-lang-diagnostics", @@ -430,7 +435,7 @@ dependencies = [ "cairo-lang-parser", "cairo-lang-syntax", "cairo-lang-utils", - "clap 4.0.26", + "clap", "colored", "diffy", "ignore", @@ -445,7 +450,7 @@ dependencies = [ [[package]] name = "cairo-lang-language-server" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "anyhow", "cairo-lang-compiler", @@ -477,7 +482,7 @@ dependencies = [ [[package]] name = "cairo-lang-lowering" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -506,7 +511,7 @@ dependencies = [ [[package]] name = "cairo-lang-parser" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "cairo-lang-diagnostics", "cairo-lang-filesystem", @@ -527,7 +532,7 @@ dependencies = [ [[package]] name = "cairo-lang-plugins" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -541,16 +546,17 @@ dependencies = [ "env_logger", "indoc", "itertools", - "pretty_assertions", "salsa", + "serde_json", "smol_str", "test-case", "test-log", + "unescaper", ] [[package]] name = "cairo-lang-proc-macros" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "cairo-lang-debug", "quote", @@ -559,7 +565,7 @@ dependencies = [ [[package]] name = "cairo-lang-project" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "cairo-lang-filesystem", "indoc", @@ -572,7 +578,7 @@ dependencies = [ [[package]] name = "cairo-lang-runner" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "anyhow", "ark-ff 0.4.0-alpha.7", @@ -588,9 +594,10 @@ dependencies = [ "cairo-lang-sierra-to-casm", "cairo-lang-utils", "cairo-vm", - "clap 4.0.26", + "clap", "itertools", "num-bigint", + "num-integer", "num-traits 0.2.15", "pretty_assertions", "salsa", @@ -600,7 +607,7 @@ dependencies = [ [[package]] name = "cairo-lang-semantic" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "assert_matches", "cairo-lang-debug", @@ -630,7 +637,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "assert_matches", "bimap", @@ -658,7 +665,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-ap-change" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", @@ -673,7 +680,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-gas" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", @@ -690,7 +697,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-generator" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -721,7 +728,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-to-casm" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "anyhow", "assert_matches", @@ -731,7 +738,7 @@ dependencies = [ "cairo-lang-sierra-ap-change", "cairo-lang-sierra-gas", "cairo-lang-utils", - "clap 4.0.26", + "clap", "env_logger", "indoc", "itertools", @@ -746,7 +753,7 @@ dependencies = [ [[package]] name = "cairo-lang-starknet" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "anyhow", "cairo-lang-casm", @@ -766,17 +773,17 @@ dependencies = [ "cairo-lang-syntax", "cairo-lang-test-utils", "cairo-lang-utils", - "clap 4.0.26", + "clap", "convert_case", "env_logger", "genco", "indoc", "itertools", - "lazy_static", "log", "num-bigint", "num-integer", "num-traits 0.2.15", + "once_cell", "pretty_assertions", "serde", "serde_json", @@ -789,7 +796,7 @@ dependencies = [ [[package]] name = "cairo-lang-syntax" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "cairo-lang-debug", "cairo-lang-filesystem", @@ -803,7 +810,7 @@ dependencies = [ [[package]] name = "cairo-lang-syntax-codegen" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "cairo-lang-utils", "env_logger", @@ -815,7 +822,7 @@ dependencies = [ [[package]] name = "cairo-lang-test-runner" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "anyhow", "cairo-felt", @@ -825,6 +832,7 @@ dependencies = [ "cairo-lang-defs", "cairo-lang-diagnostics", "cairo-lang-filesystem", + "cairo-lang-lowering", "cairo-lang-plugins", "cairo-lang-project", "cairo-lang-runner", @@ -836,7 +844,7 @@ dependencies = [ "cairo-lang-syntax", "cairo-lang-utils", "cairo-vm", - "clap 4.0.26", + "clap", "colored", "itertools", "num-bigint", @@ -848,7 +856,7 @@ dependencies = [ [[package]] name = "cairo-lang-test-utils" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "cairo-lang-utils", "env_logger", @@ -859,7 +867,7 @@ dependencies = [ [[package]] name = "cairo-lang-utils" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "chrono", "env_logger", @@ -875,16 +883,28 @@ dependencies = [ "test-log", ] +[[package]] +name = "cairo-take_until_unbalanced" +version = "0.24.2-rc1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e174056df7cfe9b579376f32de405722e1090c74deb2540bb0cd9e7931772d" +dependencies = [ + "nom", +] + [[package]] name = "cairo-vm" -version = "0.1.2" +version = "0.3.0-rc1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4fa1ade23dd2a6e671a923e748406c2c5f6c27fc186950ed04fae392b552a51" +checksum = "9f4af8c3e7be5ac46b7da4a3ab551ee4c8c8e2fdb501a4dda4dceef4c66dbcde" dependencies = [ + "anyhow", "bincode", + "bitvec", "cairo-felt", - "clap 3.2.23", + "cairo-take_until_unbalanced", "generic-array", + "hashbrown 0.13.2", "hex", "keccak", "lazy_static", @@ -893,15 +913,15 @@ dependencies = [ "num-bigint", "num-integer", "num-traits 0.2.15", - "parse-hyperlinks", "rand_core", "serde", "serde_bytes", "serde_json", - "sha2 0.10.6", + "sha2", "sha3", "starknet-crypto", "thiserror", + "thiserror-no-std", ] [[package]] @@ -940,23 +960,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "clap" -version = "3.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" -dependencies = [ - "atty", - "bitflags", - "clap_derive 3.2.18", - "clap_lex 0.2.4", - "indexmap", - "once_cell", - "strsim", - "termcolor", - "textwrap", -] - [[package]] name = "clap" version = "4.0.26" @@ -965,26 +968,13 @@ checksum = "2148adefda54e14492fb9bddcc600b4344c5d1a3123bd666dcb939c6f0e0e57e" dependencies = [ "atty", "bitflags", - "clap_derive 4.0.21", - "clap_lex 0.3.0", + "clap_derive", + "clap_lex", "once_cell", "strsim", "termcolor", ] -[[package]] -name = "clap_derive" -version = "3.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" -dependencies = [ - "heck 0.4.0", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.103", -] - [[package]] name = "clap_derive" version = "4.0.21" @@ -998,15 +988,6 @@ dependencies = [ "syn 1.0.103", ] -[[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - [[package]] name = "clap_lex" version = "0.3.0" @@ -1118,12 +1099,11 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.3.2" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ "generic-array", - "rand_core", "subtle", "zeroize", ] @@ -1138,16 +1118,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "ctor" version = "0.1.26" @@ -1244,7 +1214,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" dependencies = [ "cfg-if", - "hashbrown", + "hashbrown 0.12.3", "lock_api", "once_cell", "parking_lot_core 0.9.4", @@ -1322,8 +1292,9 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ - "block-buffer 0.10.3", + "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -1375,6 +1346,27 @@ dependencies = [ "termcolor", ] +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "fixedbitset" version = "0.4.2" @@ -1396,6 +1388,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.25" @@ -1565,6 +1563,16 @@ 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", + "serde", +] + [[package]] name = "heck" version = "0.3.3" @@ -1589,6 +1597,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hex" version = "0.4.3" @@ -1597,21 +1611,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac", - "digest 0.9.0", -] - -[[package]] -name = "html-escape" -version = "0.2.13" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "utf8-width", + "digest 0.10.6", ] [[package]] @@ -1696,14 +1700,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", + "serde", ] [[package]] name = "indoc" -version = "1.0.7" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3" +checksum = "9f2cb48b81b1dc9f39676bf99f5499babfec7cd8fe14307f7b3d747208fb5690" [[package]] name = "instant" @@ -1714,6 +1719,29 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-lifetimes" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.45.0", +] + [[package]] name = "itertools" version = "0.10.5" @@ -1749,15 +1777,15 @@ dependencies = [ [[package]] name = "lalrpop" -version = "0.19.8" +version = "0.19.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30455341b0e18f276fa64540aff54deafb54c589de6aca68659c63dd2d5d823" +checksum = "f34313ec00c2eb5c3c87ca6732ea02dcf3af99c3ff7a8fb622ffb99c9d860a87" dependencies = [ "ascii-canvas", - "atty", "bit-set", "diff", "ena", + "is-terminal", "itertools", "lalrpop-util", "petgraph", @@ -1772,9 +1800,9 @@ dependencies = [ [[package]] name = "lalrpop-util" -version = "0.19.8" +version = "0.19.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf796c978e9b4d983414f4caedc9273aa33ee214c5b887bd55fde84c85d2dc4" +checksum = "e5c1f7869c94d214466c5fd432dfed12c379fd87786768d36455892d46b18edd" dependencies = [ "regex", ] @@ -1784,6 +1812,9 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -1810,6 +1841,12 @@ dependencies = [ "cc", ] +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "lock_api" version = "0.4.9" @@ -1900,7 +1937,7 @@ dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -1998,15 +2035,15 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", ] [[package]] name = "once_cell" -version = "1.16.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "oorandom" @@ -2014,12 +2051,6 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - [[package]] name = "os_str_bytes" version = "6.4.1" @@ -2086,19 +2117,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-sys", -] - -[[package]] -name = "parse-hyperlinks" -version = "0.23.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0181d37c4d5ae35cc8be7cf823c1a933005661da6a08bcb2855aa392c9a54b8e" -dependencies = [ - "html-escape", - "nom", - "percent-encoding", - "thiserror", + "windows-sys 0.42.0", ] [[package]] @@ -2252,6 +2271,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.5" @@ -2277,9 +2302,6 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] [[package]] name = "rawpointer" @@ -2354,9 +2376,9 @@ checksum = "0df32d82cedd1499386877b062ebe8721f806de80b08d183c70184ef17dd1d42" [[package]] name = "rfc6979" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ "crypto-bigint", "hmac", @@ -2413,6 +2435,20 @@ dependencies = [ "semver 1.0.17", ] +[[package]] +name = "rustix" +version = "0.36.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + [[package]] name = "rustversion" version = "1.0.9" @@ -2527,9 +2563,9 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.7" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfc50e8183eeeb6178dcb167ae34a8051d63535023ae38b5d8d12beae193d37b" +checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" dependencies = [ "serde", ] @@ -2567,19 +2603,6 @@ dependencies = [ "syn 1.0.103", ] -[[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.6" @@ -2633,9 +2656,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "smol_str" -version = "0.1.23" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7475118a28b7e3a2e157ce0131ba8c5526ea96e90ee601d9f6bb2e286a35ab44" +checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" dependencies = [ "serde", ] @@ -2650,6 +2673,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "sprs" version = "0.7.1" @@ -2663,9 +2692,9 @@ dependencies = [ [[package]] name = "starknet-crypto" -version = "0.2.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be7d6b2c959fde2a10dbc31d54bdd0307eecb7ef6c05c23a0263e65b57b3e18a" +checksum = "d49eb65d58fa98a164ad2cd4d04775885386b83bdad6060f168a38ede77c9aed" dependencies = [ "crypto-bigint", "hex", @@ -2674,19 +2703,18 @@ dependencies = [ "num-integer", "num-traits 0.2.15", "rfc6979", - "sha2 0.9.9", + "sha2", "starknet-crypto-codegen", "starknet-curve", "starknet-ff", - "thiserror", "zeroize", ] [[package]] name = "starknet-crypto-codegen" -version = "0.1.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6569d70430f0f6edc41f6820d00acf63356e6308046ca01e57eeac22ad258c47" +checksum = "bff08f74f3ac785ac34ac05c68c5bd4df280107ab35df69dbcbde35183d89eba" dependencies = [ "starknet-curve", "starknet-ff", @@ -2695,27 +2723,23 @@ dependencies = [ [[package]] name = "starknet-curve" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84be6079d3060fdbd8b5335574fef3d3783fa2f7ee6474d08ae0c1e4b0a29ba4" +checksum = "fe0dbde7ef14d54c2117bc6d2efb68c2383005f1cd749b277c11df874d07b7af" dependencies = [ "starknet-ff", ] [[package]] name = "starknet-ff" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5874510620214ebeac50915b01d67437d8ca10a6682b1de85b93cd01157b58eb" +checksum = "78d484109da192f3a8cd58f674861c2d5e4b3e1765a466362c6f350ef213dfd1" dependencies = [ "ark-ff 0.3.0", - "bigdecimal", "crypto-bigint", "getrandom", "hex", - "num-bigint", - "serde", - "thiserror", ] [[package]] @@ -2777,6 +2801,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "term" version = "0.7.0" @@ -2832,7 +2862,7 @@ dependencies = [ [[package]] name = "tests" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" dependencies = [ "assert_matches", "cairo-felt", @@ -2857,6 +2887,7 @@ dependencies = [ "itertools", "log", "num-bigint", + "once_cell", "pretty_assertions", "rstest", "salsa", @@ -2864,12 +2895,6 @@ dependencies = [ "test-log", ] -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - [[package]] name = "thiserror" version = "1.0.40" @@ -2890,6 +2915,26 @@ dependencies = [ "syn 2.0.9", ] +[[package]] +name = "thiserror-impl-no-std" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58e6318948b519ba6dc2b442a6d0b904ebfb8d411a3ad3e07843615a72249758" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.103", +] + +[[package]] +name = "thiserror-no-std" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3ad459d94dd517257cc96add8a43190ee620011bb6e6cdc82dafd97dfafafea" +dependencies = [ + "thiserror-impl-no-std", +] + [[package]] name = "thread_local" version = "1.1.4" @@ -3140,12 +3185,6 @@ dependencies = [ "serde", ] -[[package]] -name = "utf8-width" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5190c9442dcdaf0ddd50f37420417d219ae5261bbf5db120d0f9bab996c9cba1" - [[package]] name = "version_check" version = "0.9.4" @@ -3275,47 +3314,80 @@ dependencies = [ "windows_x86_64_msvc", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "wyz" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] [[package]] name = "xshell" diff --git a/Cargo.toml b/Cargo.toml index 107fb5d91..bb3d92a73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ members = [ ] [workspace.package] -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" edition = "2021" repository = "https://github.com/starkware-libs/cairo/" license = "Apache-2.0" @@ -43,8 +43,8 @@ ark-ff = "0.4.0-alpha.7" ark-std = "0.3.0" assert_matches = "1.5" bimap = "0.6.2" -cairo-felt = "0.1.1" -cairo-vm = "0.1.2" +cairo-felt = "0.3.0-rc1" +cairo-vm = "0.3.0-rc1" chrono = "0.4.23" clap = { version = "4.0", features = ["derive"] } colored = "2" @@ -57,16 +57,16 @@ genco = "0.17.0" good_lp = { version = "1.3.2", features = ["minilp"], default-features = false } id-arena = "2.2.1" ignore = "0.4.20" -indexmap = "1.9.1" -indoc = "1.0.7" +indexmap = { version = "1.9.1", features = ["serde"] } +indoc = "2.0.1" itertools = "0.10.3" -lalrpop-util = { version = "0.19.8", features = ["lexer"] } -lazy_static = "1.4.0" +lalrpop-util = { version = "0.19.9", features = ["lexer"] } log = "0.4" lsp = { version = "0.93", package = "lsp-types" } num-bigint = "0.4" num-integer = "0.1" num-traits = "0.2" +once_cell = "1.17.1" path-clean = "0.1.0" pretty_assertions = "1.2.1" proc-macro2 = "1.0" @@ -78,7 +78,7 @@ scarb-metadata = "1.0.1" serde = { version = "1.0.130", features = ["derive"] } serde_json = "1.0" sha3 = "0.10.6" -smol_str = "0.1.23" +smol_str = { version = "0.2.0", features = ["serde"] } syn = { version = "1.0.99", features = ["full", "extra-traits"] } test-case = "2.2.2" test-case-macros = "2.2.2" diff --git a/Makefile b/Makefile index 7969ea043..0ed75752e 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ build: cargo build test: - cargo run --bin cairo-test -- --starknet --path $(SOURCE_FOLDER) + cargo run --bin cairo-test -- --starknet $(SOURCE_FOLDER) format: cargo run --bin cairo-format -- --recursive $(SOURCE_FOLDER) --print-parsing-errors diff --git a/cairo b/cairo index 9c190561c..81c4eb942 160000 --- a/cairo +++ b/cairo @@ -1 +1 @@ -Subproject commit 9c190561ce1e8323665857f1a77082925c817b4c +Subproject commit 81c4eb942dace6849a8067dc43263da99579bef8 diff --git a/src/openzeppelin/security/initializable.cairo b/src/openzeppelin/security/initializable.cairo index 00079d280..8485238bb 100644 --- a/src/openzeppelin/security/initializable.cairo +++ b/src/openzeppelin/security/initializable.cairo @@ -11,7 +11,7 @@ mod Initializable { #[internal] fn initialize() { - assert(!is_initialized(), 'Contract already initialized'); + assert(!is_initialized(), 'Initializable: is initialized'); initialized::write(true); } } diff --git a/src/openzeppelin/tests/test_erc20.cairo b/src/openzeppelin/tests/test_erc20.cairo index 0b4f7f230..04fb5fcf1 100644 --- a/src/openzeppelin/tests/test_erc20.cairo +++ b/src/openzeppelin/tests/test_erc20.cairo @@ -84,7 +84,7 @@ fn test_approve() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('ERC20: approve from 0', ))] +#[should_panic(expected: ('ERC20: approve from 0', ))] fn test_approve_from_zero() { let (owner, supply) = setup(); let spender: ContractAddress = contract_address_const::<2>(); @@ -97,7 +97,7 @@ fn test_approve_from_zero() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('ERC20: approve to 0', ))] +#[should_panic(expected: ('ERC20: approve to 0', ))] fn test_approve_to_zero() { let (owner, supply) = setup(); let spender: ContractAddress = contract_address_const::<0>(); @@ -120,7 +120,7 @@ fn test__approve() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('ERC20: approve from 0', ))] +#[should_panic(expected: ('ERC20: approve from 0', ))] fn test__approve_from_zero() { let owner: ContractAddress = contract_address_const::<0>(); let spender: ContractAddress = contract_address_const::<1>(); @@ -130,7 +130,7 @@ fn test__approve_from_zero() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('ERC20: approve to 0', ))] +#[should_panic(expected: ('ERC20: approve to 0', ))] fn test__approve_to_zero() { let (owner, supply) = setup(); @@ -170,7 +170,7 @@ fn test__transfer() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('u256_sub Overflow', ))] +#[should_panic(expected: ('u256_sub Overflow', ))] fn test__transfer_not_enough_balance() { let (sender, supply) = setup(); @@ -181,7 +181,7 @@ fn test__transfer_not_enough_balance() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('ERC20: transfer from 0', ))] +#[should_panic(expected: ('ERC20: transfer from 0', ))] fn test__transfer_from_zero() { let sender: ContractAddress = contract_address_const::<0>(); let recipient: ContractAddress = contract_address_const::<1>(); @@ -191,7 +191,7 @@ fn test__transfer_from_zero() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('ERC20: transfer to 0', ))] +#[should_panic(expected: ('ERC20: transfer to 0', ))] fn test__transfer_to_zero() { let (sender, supply) = setup(); @@ -245,7 +245,7 @@ fn test_transfer_from_doesnt_consume_infinite_allowance() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('u256_sub Overflow', ))] +#[should_panic(expected: ('u256_sub Overflow', ))] fn test_transfer_from_greater_than_allowance() { let (owner, supply) = setup(); @@ -263,7 +263,7 @@ fn test_transfer_from_greater_than_allowance() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('ERC20: transfer to 0', ))] +#[should_panic(expected: ('ERC20: transfer to 0', ))] fn test_transfer_from_to_zero_address() { let (owner, supply) = setup(); @@ -280,7 +280,7 @@ fn test_transfer_from_to_zero_address() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('u256_sub Overflow', ))] +#[should_panic(expected: ('u256_sub Overflow', ))] fn test_transfer_from_from_zero_address() { let (owner, supply) = setup(); @@ -312,7 +312,7 @@ fn test_increase_allowance() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('ERC20: approve to 0', ))] +#[should_panic(expected: ('ERC20: approve to 0', ))] fn test_increase_allowance_to_zero_address() { let (owner, supply) = setup(); @@ -324,7 +324,7 @@ fn test_increase_allowance_to_zero_address() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('ERC20: approve from 0', ))] +#[should_panic(expected: ('ERC20: approve from 0', ))] fn test_increase_allowance_from_zero_address() { let (owner, supply) = setup(); @@ -355,7 +355,7 @@ fn test_decrease_allowance() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('u256_sub Overflow', ))] +#[should_panic(expected: ('u256_sub Overflow', ))] fn test_decrease_allowance_to_zero_address() { let (owner, supply) = setup(); @@ -367,7 +367,7 @@ fn test_decrease_allowance_to_zero_address() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('u256_sub Overflow', ))] +#[should_panic(expected: ('u256_sub Overflow', ))] fn test_decrease_allowance_from_zero_address() { let (owner, supply) = setup(); @@ -423,7 +423,7 @@ fn test__mint() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('ERC20: mint to 0', ))] +#[should_panic(expected: ('ERC20: mint to 0', ))] fn test__mint_to_zero() { let minter: ContractAddress = contract_address_const::<0>(); let amount: u256 = u256_from_felt252(100); @@ -445,7 +445,7 @@ fn test__burn() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('ERC20: burn from 0', ))] +#[should_panic(expected: ('ERC20: burn from 0', ))] fn test__burn_from_zero() { setup(); let zero_address: ContractAddress = contract_address_const::<0>(); diff --git a/src/openzeppelin/tests/test_initializable.cairo b/src/openzeppelin/tests/test_initializable.cairo index 37e1b6b78..cfd7ec003 100644 --- a/src/openzeppelin/tests/test_initializable.cairo +++ b/src/openzeppelin/tests/test_initializable.cairo @@ -10,7 +10,7 @@ fn test_initialize() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('Contract already initialized', ))] +#[should_panic(expected: ('Initializable: is initialized', ))] fn test_initialize_when_initialized() { Initializable::initialize(); Initializable::initialize(); diff --git a/src/openzeppelin/tests/test_pausable.cairo b/src/openzeppelin/tests/test_pausable.cairo index 598f98be9..6b76e9375 100644 --- a/src/openzeppelin/tests/test_pausable.cairo +++ b/src/openzeppelin/tests/test_pausable.cairo @@ -13,7 +13,7 @@ fn test_pause_when_unpaused() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('Pausable: paused', ))] +#[should_panic(expected: ('Pausable: paused', ))] fn test_pause_when_paused() { MockPausable::pause(); MockPausable::pause(); @@ -21,7 +21,7 @@ fn test_pause_when_paused() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('Pausable: paused', ))] +#[should_panic(expected: ('Pausable: paused', ))] fn test_pause_increment() { MockPausable::pause(); MockPausable::assert_unpaused_and_increment(); @@ -40,7 +40,7 @@ fn test_unpause_when_paused() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('Pausable: not paused', ))] +#[should_panic(expected: ('Pausable: not paused', ))] fn test_unpause_when_unpaused() { assert(!MockPausable::is_paused(), 'Should be unpaused'); MockPausable::unpause(); diff --git a/src/openzeppelin/token/erc20.cairo b/src/openzeppelin/token/erc20.cairo index 8b84c1a1f..f0b63d59d 100644 --- a/src/openzeppelin/token/erc20.cairo +++ b/src/openzeppelin/token/erc20.cairo @@ -14,7 +14,7 @@ trait IERC20 { #[contract] mod ERC20 { - use openzeppelin::token::erc20::IERC20; + use super::IERC20; use starknet::get_caller_address; use starknet::ContractAddress; use starknet::ContractAddressZeroable; From c29b45cf69232c469ac2c90da572149f707a025f Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Fri, 28 Apr 2023 12:57:45 -0400 Subject: [PATCH 046/246] Add `BoundedInt` and internal macros, update cairo (#600) * update cairo * update Cargo * remove path flag * refactor _spend_allowance with BoundedInt * add BoundedInt for u256_max * add internal macros * update cairo * update cairo * update Cargo * remove import * update expected syntax * remove redundant maxu256 fn * update expected syntax * Update src/openzeppelin/tests/test_erc20.cairo Co-authored-by: Hadrien Croubois * use into trait * use address zeroable in mod * set cairo to alpha7 * update Cargo --------- Co-authored-by: Hadrien Croubois --- src/openzeppelin/tests/test_erc165.cairo | 4 ++-- src/openzeppelin/tests/test_erc20.cairo | 18 +++++++----------- src/openzeppelin/token/erc20.cairo | 14 ++++++++++---- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/openzeppelin/tests/test_erc165.cairo b/src/openzeppelin/tests/test_erc165.cairo index 7a4efeac8..c66c1b034 100644 --- a/src/openzeppelin/tests/test_erc165.cairo +++ b/src/openzeppelin/tests/test_erc165.cairo @@ -35,7 +35,7 @@ fn test_register_interface() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('Invalid id', ))] +#[should_panic(expected: ('Invalid id', ))] fn test_register_invalid_interface() { ERC165::register_interface(INVALID_ID); } @@ -51,7 +51,7 @@ fn test_deregister_interface() { #[test] #[available_gas(2000000)] -#[should_panic(expected = ('Invalid id', ))] +#[should_panic(expected: ('Invalid id', ))] fn test_deregister_default_interface() { ERC165::deregister_interface(IERC165_ID); } diff --git a/src/openzeppelin/tests/test_erc20.cairo b/src/openzeppelin/tests/test_erc20.cairo index 04fb5fcf1..5c5615990 100644 --- a/src/openzeppelin/tests/test_erc20.cairo +++ b/src/openzeppelin/tests/test_erc20.cairo @@ -4,6 +4,8 @@ use starknet::ContractAddress; use starknet::testing::set_caller_address; use integer::u256; use integer::u256_from_felt252; +use integer::BoundedInt; +use traits::Into; // // Constants @@ -12,12 +14,6 @@ use integer::u256_from_felt252; const NAME: felt252 = 111; const SYMBOL: felt252 = 222; -fn MAX_U256() -> u256 { - u256 { - low: 0xffffffffffffffffffffffffffffffff_u128, high: 0xffffffffffffffffffffffffffffffff_u128 - } -} - // // Helper functions // @@ -234,13 +230,13 @@ fn test_transfer_from_doesnt_consume_infinite_allowance() { let spender: ContractAddress = contract_address_const::<3>(); let amount: u256 = u256_from_felt252(100); - ERC20::approve(spender, MAX_U256()); + ERC20::approve(spender, BoundedInt::max()); set_caller_address(spender); ERC20::transfer_from(owner, recipient, amount); let spender_allowance: u256 = ERC20::allowance(owner, spender); - assert(spender_allowance == MAX_U256(), 'Allowance should not change'); + assert(spender_allowance == BoundedInt::max(), 'Allowance should not change'); } #[test] @@ -399,12 +395,12 @@ fn test__spend_allowance_unlimited() { let (owner, supply) = setup(); let spender: ContractAddress = contract_address_const::<2>(); - let max_minus_one: u256 = MAX_U256() - u256_from_felt252(1); + let max_minus_one: u256 = BoundedInt::max() - 1.into(); - ERC20::_approve(owner, spender, MAX_U256()); + ERC20::_approve(owner, spender, BoundedInt::max()); ERC20::_spend_allowance(owner, spender, max_minus_one); - assert(ERC20::allowance(owner, spender) == MAX_U256(), 'Allowance should not change'); + assert(ERC20::allowance(owner, spender) == BoundedInt::max(), 'Allowance should not change'); } #[test] diff --git a/src/openzeppelin/token/erc20.cairo b/src/openzeppelin/token/erc20.cairo index f0b63d59d..c53fdbbe4 100644 --- a/src/openzeppelin/token/erc20.cairo +++ b/src/openzeppelin/token/erc20.cairo @@ -19,6 +19,7 @@ mod ERC20 { use starknet::ContractAddress; use starknet::ContractAddressZeroable; use zeroable::Zeroable; + use integer::BoundedInt; struct Storage { _name: felt252, @@ -148,23 +149,27 @@ mod ERC20 { /// Internals /// + #[internal] fn initializer(name_: felt252, symbol_: felt252) { _name::write(name_); _symbol::write(symbol_); } + #[internal] fn _increase_allowance(spender: ContractAddress, added_value: u256) -> bool { let caller = get_caller_address(); _approve(caller, spender, _allowances::read((caller, spender)) + added_value); true } + #[internal] fn _decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { let caller = get_caller_address(); _approve(caller, spender, _allowances::read((caller, spender)) - subtracted_value); true } + #[internal] fn _mint(recipient: ContractAddress, amount: u256) { assert(!recipient.is_zero(), 'ERC20: mint to 0'); _total_supply::write(_total_supply::read() + amount); @@ -172,6 +177,7 @@ mod ERC20 { Transfer(Zeroable::zero(), recipient, amount); } + #[internal] fn _burn(account: ContractAddress, amount: u256) { assert(!account.is_zero(), 'ERC20: burn from 0'); _total_supply::write(_total_supply::read() - amount); @@ -179,6 +185,7 @@ mod ERC20 { Transfer(account, Zeroable::zero(), amount); } + #[internal] fn _approve(owner: ContractAddress, spender: ContractAddress, amount: u256) { assert(!owner.is_zero(), 'ERC20: approve from 0'); assert(!spender.is_zero(), 'ERC20: approve to 0'); @@ -186,6 +193,7 @@ mod ERC20 { Approval(owner, spender, amount); } + #[internal] fn _transfer(sender: ContractAddress, recipient: ContractAddress, amount: u256) { assert(!sender.is_zero(), 'ERC20: transfer from 0'); assert(!recipient.is_zero(), 'ERC20: transfer to 0'); @@ -194,12 +202,10 @@ mod ERC20 { Transfer(sender, recipient, amount); } + #[internal] fn _spend_allowance(owner: ContractAddress, spender: ContractAddress, amount: u256) { let current_allowance = _allowances::read((owner, spender)); - let ONES_MASK = 0xffffffffffffffffffffffffffffffff_u128; - let is_unlimited_allowance = - current_allowance.low == ONES_MASK & current_allowance.high == ONES_MASK; - if !is_unlimited_allowance { + if current_allowance != BoundedInt::max() { _approve(owner, spender, current_allowance - amount); } } From e48a68291d2b878ed683bcbb39cf7a3eb96c20ac Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Thu, 11 May 2023 18:12:21 -0400 Subject: [PATCH 047/246] Migrate ownable (#604) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add ownable * add ownable tests * simplify renounce * fix formatting * add internal macros * Apply suggestions from code review Co-authored-by: Martín Triay * add test_transfer_ownership_from_zero * update cairo to alpha7 * update Cargo * fix expected syntax * update cairo to rc0 * update Cargo * fix import * simplify imports * Apply suggestions from code review Co-authored-by: Eric Nordelo * Update src/openzeppelin/token/erc20.cairo Co-authored-by: Eric Nordelo * fix set_caller_address --------- Co-authored-by: Martín Triay Co-authored-by: Eric Nordelo --- Cargo.lock | 263 +++++++--------------- Cargo.toml | 13 +- cairo | 2 +- src/openzeppelin/access.cairo | 1 + src/openzeppelin/access/ownable.cairo | 52 +++++ src/openzeppelin/lib.cairo | 1 + src/openzeppelin/tests.cairo | 1 + src/openzeppelin/tests/test_ownable.cairo | 90 ++++++++ src/openzeppelin/token/erc20.cairo | 5 +- 9 files changed, 239 insertions(+), 189 deletions(-) create mode 100644 src/openzeppelin/access.cairo create mode 100644 src/openzeppelin/access/ownable.cairo create mode 100644 src/openzeppelin/tests/test_ownable.cairo diff --git a/Cargo.lock b/Cargo.lock index e4ddc69bf..85aa935ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,15 +22,6 @@ dependencies = [ "memchr", ] -[[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 = "anyhow" version = "1.0.70" @@ -310,7 +301,7 @@ dependencies = [ [[package]] name = "cairo-lang-casm" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "cairo-lang-utils", "env_logger", @@ -327,7 +318,7 @@ dependencies = [ [[package]] name = "cairo-lang-compiler" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "anyhow", "cairo-lang-defs", @@ -345,13 +336,14 @@ dependencies = [ "clap", "log", "salsa", + "smol_str", "test-log", "thiserror", ] [[package]] name = "cairo-lang-debug" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "cairo-lang-proc-macros", "cairo-lang-utils", @@ -362,7 +354,7 @@ dependencies = [ [[package]] name = "cairo-lang-defs" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "cairo-lang-debug", "cairo-lang-diagnostics", @@ -383,7 +375,7 @@ dependencies = [ [[package]] name = "cairo-lang-diagnostics" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "cairo-lang-filesystem", "cairo-lang-proc-macros", @@ -398,7 +390,7 @@ dependencies = [ [[package]] name = "cairo-lang-eq-solver" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "cairo-lang-utils", "env_logger", @@ -412,7 +404,7 @@ dependencies = [ [[package]] name = "cairo-lang-filesystem" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "cairo-lang-debug", "cairo-lang-utils", @@ -427,7 +419,7 @@ dependencies = [ [[package]] name = "cairo-lang-formatter" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "anyhow", "cairo-lang-diagnostics", @@ -450,7 +442,7 @@ dependencies = [ [[package]] name = "cairo-lang-language-server" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "anyhow", "cairo-lang-compiler", @@ -482,7 +474,7 @@ dependencies = [ [[package]] name = "cairo-lang-lowering" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -511,7 +503,7 @@ dependencies = [ [[package]] name = "cairo-lang-parser" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "cairo-lang-diagnostics", "cairo-lang-filesystem", @@ -523,16 +515,19 @@ dependencies = [ "env_logger", "itertools", "log", + "num-bigint", + "num-traits 0.2.15", "pretty_assertions", "salsa", "smol_str", "test-case", "test-log", + "unescaper", ] [[package]] name = "cairo-lang-plugins" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -551,12 +546,11 @@ dependencies = [ "smol_str", "test-case", "test-log", - "unescaper", ] [[package]] name = "cairo-lang-proc-macros" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "cairo-lang-debug", "quote", @@ -565,7 +559,7 @@ dependencies = [ [[package]] name = "cairo-lang-project" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "cairo-lang-filesystem", "indoc", @@ -578,7 +572,7 @@ dependencies = [ [[package]] name = "cairo-lang-runner" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "anyhow", "ark-ff 0.4.0-alpha.7", @@ -586,16 +580,22 @@ dependencies = [ "cairo-felt", "cairo-lang-casm", "cairo-lang-compiler", + "cairo-lang-defs", "cairo-lang-diagnostics", + "cairo-lang-filesystem", + "cairo-lang-lowering", + "cairo-lang-semantic", "cairo-lang-sierra", "cairo-lang-sierra-ap-change", "cairo-lang-sierra-gas", "cairo-lang-sierra-generator", "cairo-lang-sierra-to-casm", + "cairo-lang-starknet", "cairo-lang-utils", "cairo-vm", "clap", "itertools", + "keccak", "num-bigint", "num-integer", "num-traits 0.2.15", @@ -607,7 +607,7 @@ dependencies = [ [[package]] name = "cairo-lang-semantic" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "assert_matches", "cairo-lang-debug", @@ -632,12 +632,11 @@ dependencies = [ "smol_str", "test-case", "test-log", - "unescaper", ] [[package]] name = "cairo-lang-sierra" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "assert_matches", "bimap", @@ -665,7 +664,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-ap-change" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", @@ -680,7 +679,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-gas" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", @@ -697,7 +696,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-generator" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -728,7 +727,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-to-casm" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "anyhow", "assert_matches", @@ -753,9 +752,10 @@ dependencies = [ [[package]] name = "cairo-lang-starknet" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "anyhow", + "cairo-felt", "cairo-lang-casm", "cairo-lang-compiler", "cairo-lang-defs", @@ -796,21 +796,25 @@ dependencies = [ [[package]] name = "cairo-lang-syntax" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "cairo-lang-debug", "cairo-lang-filesystem", "cairo-lang-utils", "env_logger", + "num-bigint", + "num-traits 0.2.15", "pretty_assertions", "salsa", "smol_str", "test-log", + "thiserror", + "unescaper", ] [[package]] name = "cairo-lang-syntax-codegen" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "cairo-lang-utils", "env_logger", @@ -822,7 +826,7 @@ dependencies = [ [[package]] name = "cairo-lang-test-runner" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "anyhow", "cairo-felt", @@ -848,15 +852,15 @@ dependencies = [ "colored", "itertools", "num-bigint", + "num-traits 0.2.15", "rayon", "salsa", "thiserror", - "unescaper", ] [[package]] name = "cairo-lang-test-utils" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "cairo-lang-utils", "env_logger", @@ -867,9 +871,8 @@ dependencies = [ [[package]] name = "cairo-lang-utils" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ - "chrono", "env_logger", "indexmap", "itertools", @@ -881,6 +884,7 @@ dependencies = [ "serde_json", "test-case", "test-log", + "time", ] [[package]] @@ -945,21 +949,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chrono" -version = "0.4.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-integer", - "num-traits 0.2.15", - "time", - "wasm-bindgen", - "winapi", -] - [[package]] name = "clap" version = "4.0.26" @@ -997,16 +986,6 @@ dependencies = [ "os_str_bytes", ] -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - [[package]] name = "colored" version = "2.0.0" @@ -1033,12 +1012,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - [[package]] name = "cpufeatures" version = "0.2.5" @@ -1128,50 +1101,6 @@ dependencies = [ "syn 1.0.103", ] -[[package]] -name = "cxx" -version = "1.0.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a41a86530d0fe7f5d9ea779916b7cadd2d4f9add748b99c2c029cbbdfaf453" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06416d667ff3e3ad2df1cd8cd8afae5da26cf9cec4d0825040f88b5ca659a2f0" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 1.0.103", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "820a9a2af1669deeef27cb271f476ffd196a2c4b6731336011e0ba63e2c7cf71" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08a6e2fcc370a089ad3b4aaf54db3b1b4cee38ddabce5896b33eb693275f470" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.103", -] - [[package]] name = "darling" version = "0.14.4" @@ -1530,7 +1459,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] @@ -1630,30 +1559,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" -[[package]] -name = "iana-time-zone" -version = "0.1.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "winapi", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" -dependencies = [ - "cxx", - "cxx-build", -] - [[package]] name = "id-arena" version = "2.2.1" @@ -1832,15 +1737,6 @@ dependencies = [ "libc", ] -[[package]] -name = "link-cplusplus" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" -dependencies = [ - "cc", -] - [[package]] name = "linux-raw-sys" version = "0.1.4" @@ -1936,7 +1832,7 @@ checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.42.0", ] @@ -2039,6 +1935,15 @@ dependencies = [ "libc", ] +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + [[package]] name = "once_cell" version = "1.17.1" @@ -2255,9 +2160,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.53" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" +checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" dependencies = [ "unicode-ident", ] @@ -2519,12 +2424,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "scratch" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" - [[package]] name = "semver" version = "0.11.0" @@ -2578,7 +2477,7 @@ checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" dependencies = [ "proc-macro2", "quote", - "syn 2.0.9", + "syn 2.0.3", ] [[package]] @@ -2780,9 +2679,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.9" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da4a3c17e109f700685ec577c0f85efd9b19bcf15c913985f14dc1ac01775aa" +checksum = "e8234ae35e70582bfa0f1fedffa6daa248e41dd045310b19800c4a36382c8f60" dependencies = [ "proc-macro2", "quote", @@ -2862,7 +2761,7 @@ dependencies = [ [[package]] name = "tests" -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" dependencies = [ "assert_matches", "cairo-felt", @@ -2912,7 +2811,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.9", + "syn 2.0.3", ] [[package]] @@ -2946,13 +2845,31 @@ dependencies = [ [[package]] name = "time" -version = "0.1.44" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" dependencies = [ + "itoa", "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", + "num_threads", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +dependencies = [ + "time-core", ] [[package]] @@ -3161,12 +3078,6 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - [[package]] name = "unicode-xid" version = "0.2.4" @@ -3202,12 +3113,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index bb3d92a73..49e71907f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,10 +3,8 @@ members = [ "cairo/crates/cairo-lang-casm", "cairo/crates/cairo-lang-compiler", - "cairo/crates/cairo-lang-utils", "cairo/crates/cairo-lang-debug", "cairo/crates/cairo-lang-defs", - "cairo/crates/cairo-lang-proc-macros", "cairo/crates/cairo-lang-diagnostics", "cairo/crates/cairo-lang-eq-solver", "cairo/crates/cairo-lang-filesystem", @@ -15,23 +13,25 @@ members = [ "cairo/crates/cairo-lang-lowering", "cairo/crates/cairo-lang-parser", "cairo/crates/cairo-lang-plugins", + "cairo/crates/cairo-lang-proc-macros", "cairo/crates/cairo-lang-project", "cairo/crates/cairo-lang-runner", "cairo/crates/cairo-lang-semantic", + "cairo/crates/cairo-lang-sierra", "cairo/crates/cairo-lang-sierra-ap-change", "cairo/crates/cairo-lang-sierra-gas", "cairo/crates/cairo-lang-sierra-generator", "cairo/crates/cairo-lang-sierra-to-casm", - "cairo/crates/cairo-lang-sierra", "cairo/crates/cairo-lang-starknet", - "cairo/crates/cairo-lang-syntax-codegen", "cairo/crates/cairo-lang-syntax", + "cairo/crates/cairo-lang-syntax-codegen", "cairo/crates/cairo-lang-test-runner", + "cairo/crates/cairo-lang-utils", "cairo/tests", ] [workspace.package] -version = "1.0.0-alpha.7" +version = "1.0.0-rc0" edition = "2021" repository = "https://github.com/starkware-libs/cairo/" license = "Apache-2.0" @@ -45,7 +45,6 @@ assert_matches = "1.5" bimap = "0.6.2" cairo-felt = "0.3.0-rc1" cairo-vm = "0.3.0-rc1" -chrono = "0.4.23" clap = { version = "4.0", features = ["derive"] } colored = "2" const-fnv1a-hash = "1.1.0" @@ -60,6 +59,7 @@ ignore = "0.4.20" indexmap = { version = "1.9.1", features = ["serde"] } indoc = "2.0.1" itertools = "0.10.3" +keccak = "0.1.3" lalrpop-util = { version = "0.19.9", features = ["lexer"] } log = "0.4" lsp = { version = "0.93", package = "lsp-types" } @@ -84,6 +84,7 @@ test-case = "2.2.2" test-case-macros = "2.2.2" test-log = "0.2.11" thiserror = "1.0.32" +time = { version = "0.3.20", features = ["formatting", "macros", "local-offset"] } tokio = { version = "1.18.2", features = ["full", "sync"] } toml = "0.4.2" tower-lsp = "0.17.0" diff --git a/cairo b/cairo index 81c4eb942..05867c82d 160000 --- a/cairo +++ b/cairo @@ -1 +1 @@ -Subproject commit 81c4eb942dace6849a8067dc43263da99579bef8 +Subproject commit 05867c82de42d5ee5cfa953dcca1cb826402f74b diff --git a/src/openzeppelin/access.cairo b/src/openzeppelin/access.cairo new file mode 100644 index 000000000..cadc81b51 --- /dev/null +++ b/src/openzeppelin/access.cairo @@ -0,0 +1 @@ +mod ownable; diff --git a/src/openzeppelin/access/ownable.cairo b/src/openzeppelin/access/ownable.cairo new file mode 100644 index 000000000..69bf5b2b1 --- /dev/null +++ b/src/openzeppelin/access/ownable.cairo @@ -0,0 +1,52 @@ +#[contract] +mod Ownable { + use starknet::ContractAddress; + use starknet::get_caller_address; + use zeroable::Zeroable; + + struct Storage { + _owner: ContractAddress + } + + #[event] + fn OwnershipTransferred(previous_owner: ContractAddress, new_owner: ContractAddress) {} + + #[internal] + fn initializer() { + let caller: ContractAddress = get_caller_address(); + _transfer_ownership(caller); + } + + #[internal] + fn assert_only_owner() { + let owner: ContractAddress = _owner::read(); + let caller: ContractAddress = get_caller_address(); + assert(!caller.is_zero(), 'Caller is the zero address'); + assert(caller == owner, 'Caller is not the owner'); + } + + #[internal] + fn owner() -> ContractAddress { + _owner::read() + } + + #[internal] + fn transfer_ownership(new_owner: ContractAddress) { + assert(!new_owner.is_zero(), 'New owner is the zero address'); + assert_only_owner(); + _transfer_ownership(new_owner); + } + + #[internal] + fn renounce_ownership() { + assert_only_owner(); + _transfer_ownership(Zeroable::zero()); + } + + #[internal] + fn _transfer_ownership(new_owner: ContractAddress) { + let previous_owner: ContractAddress = _owner::read(); + _owner::write(new_owner); + OwnershipTransferred(previous_owner, new_owner); + } +} diff --git a/src/openzeppelin/lib.cairo b/src/openzeppelin/lib.cairo index 14a9204a4..1f55a830f 100644 --- a/src/openzeppelin/lib.cairo +++ b/src/openzeppelin/lib.cairo @@ -1,3 +1,4 @@ +mod access; mod introspection; mod security; mod token; diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index ac9e6b924..df1dcba06 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -1,3 +1,4 @@ +mod test_ownable; mod test_erc165; mod test_erc20; mod test_pausable; diff --git a/src/openzeppelin/tests/test_ownable.cairo b/src/openzeppelin/tests/test_ownable.cairo new file mode 100644 index 000000000..424cc0546 --- /dev/null +++ b/src/openzeppelin/tests/test_ownable.cairo @@ -0,0 +1,90 @@ +use openzeppelin::access::ownable::Ownable; + +use starknet::ContractAddress; +use starknet::contract_address_const; +use starknet::testing; +use zeroable::Zeroable; + +fn ZERO() -> ContractAddress { + contract_address_const::<0>() +} + +fn OWNER() -> ContractAddress { + contract_address_const::<1>() +} + +fn OTHER() -> ContractAddress { + contract_address_const::<2>() +} + +fn setup() { + testing::set_caller_address(OWNER()); + Ownable::initializer(); +} + +#[test] +#[available_gas(2000000)] +fn test_initializer() { + assert(Ownable::owner().is_zero(), 'Should be zero'); + setup(); + assert(Ownable::owner() == OWNER(), 'Owner should be set'); +} + +#[test] +#[available_gas(2000000)] +fn test_transfer_ownership() { + setup(); + Ownable::transfer_ownership(OTHER()); + assert(Ownable::owner() == OTHER(), 'Should transfer ownership'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('New owner is the zero address', ))] +fn test_transfer_ownership_to_zero() { + setup(); + Ownable::transfer_ownership(ZERO()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Caller is the zero address', ))] +fn test_transfer_ownership_from_zero() { + assert(Ownable::owner() == ZERO(), 'Should be zero with no owner'); + Ownable::transfer_ownership(OTHER()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Caller is not the owner', ))] +fn test_transfer_ownership_from_nonowner() { + setup(); + testing::set_caller_address(OTHER()); + Ownable::transfer_ownership(OTHER()); +} + +#[test] +#[available_gas(2000000)] +fn test_renounce_ownership() { + setup(); + Ownable::renounce_ownership(); + assert(Ownable::owner() == ZERO(), 'Should renounce ownership'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Caller is the zero address', ))] +fn test_renounce_ownership_from_zero_address() { + setup(); + testing::set_caller_address(ZERO()); + Ownable::renounce_ownership(); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Caller is not the owner', ))] +fn test_renounce_ownership_from_nonowner() { + setup(); + testing::set_caller_address(OTHER()); + Ownable::renounce_ownership(); +} diff --git a/src/openzeppelin/token/erc20.cairo b/src/openzeppelin/token/erc20.cairo index c53fdbbe4..f2350bebc 100644 --- a/src/openzeppelin/token/erc20.cairo +++ b/src/openzeppelin/token/erc20.cairo @@ -15,11 +15,10 @@ trait IERC20 { #[contract] mod ERC20 { use super::IERC20; - use starknet::get_caller_address; + use integer::BoundedInt; use starknet::ContractAddress; - use starknet::ContractAddressZeroable; + use starknet::get_caller_address; use zeroable::Zeroable; - use integer::BoundedInt; struct Storage { _name: felt252, From 5f86f65b564752ec43aa9cd53b51b85761a24484 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Fri, 19 May 2023 16:46:06 -0400 Subject: [PATCH 048/246] Migrate security/reentrancyguard (#590) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add reentrancyguard * update Cargo * remove mytest * add reentrancy mocks * add utils * finish tests * remove unused func * add comments * clean up code * clean up code * Apply suggestions from code review Co-authored-by: Martín Triay * remove underscore * remove constructor in mock * update interface name * change count_this_recursive to count_external_recursive * move mocks inside tests dir * remove underscore * update cairo to alpha7 * update Cargo * add withdraw_gas * fix formatting * update cairo to rc0 * update Cargo * fix import * simplify imports * remove duplicate import * rename mod * add mock interface * finish tests * update cairo * add new line * Update src/openzeppelin/tests/mocks/reentrancy_mock.cairo Co-authored-by: Eric Nordelo * import dispatchers * fix formatting * Apply suggestions from code review Co-authored-by: Martín Triay * add deploy fn to utils * use deploy from utils --------- Co-authored-by: Martín Triay Co-authored-by: Eric Nordelo --- cairo | 2 +- src/openzeppelin/security.cairo | 1 + .../security/reentrancyguard.cairo | 17 ++++ src/openzeppelin/tests.cairo | 2 + src/openzeppelin/tests/mocks.cairo | 2 + .../mocks/reentrancy_attacker_mock.cairo | 21 +++++ .../tests/mocks/reentrancy_mock.cairo | 93 +++++++++++++++++++ .../tests/test_reentrancyguard.cairo | 92 ++++++++++++++++++ src/openzeppelin/tests/utils.cairo | 14 +++ 9 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 src/openzeppelin/security/reentrancyguard.cairo create mode 100644 src/openzeppelin/tests/mocks/reentrancy_attacker_mock.cairo create mode 100644 src/openzeppelin/tests/mocks/reentrancy_mock.cairo create mode 100644 src/openzeppelin/tests/test_reentrancyguard.cairo create mode 100644 src/openzeppelin/tests/utils.cairo diff --git a/cairo b/cairo index 05867c82d..ee9a0bb1e 160000 --- a/cairo +++ b/cairo @@ -1 +1 @@ -Subproject commit 05867c82de42d5ee5cfa953dcca1cb826402f74b +Subproject commit ee9a0bb1e496c6e5d922c3a5071d85b45f8bc268 diff --git a/src/openzeppelin/security.cairo b/src/openzeppelin/security.cairo index 871349507..45a9c998a 100644 --- a/src/openzeppelin/security.cairo +++ b/src/openzeppelin/security.cairo @@ -1,2 +1,3 @@ +mod reentrancyguard; mod pausable; mod initializable; diff --git a/src/openzeppelin/security/reentrancyguard.cairo b/src/openzeppelin/security/reentrancyguard.cairo new file mode 100644 index 000000000..4aa7375a6 --- /dev/null +++ b/src/openzeppelin/security/reentrancyguard.cairo @@ -0,0 +1,17 @@ +#[contract] +mod ReentrancyGuard { + use starknet::get_caller_address; + + struct Storage { + entered: bool + } + + fn start() { + assert(!entered::read(), 'ReentrancyGuard: reentrant call'); + entered::write(true); + } + + fn end() { + entered::write(false); + } +} diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index df1dcba06..80288f54b 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -1,6 +1,8 @@ +mod test_reentrancyguard; mod test_ownable; mod test_erc165; mod test_erc20; mod test_pausable; mod test_initializable; mod mocks; +mod utils; diff --git a/src/openzeppelin/tests/mocks.cairo b/src/openzeppelin/tests/mocks.cairo index 60df001ef..ca522a93c 100644 --- a/src/openzeppelin/tests/mocks.cairo +++ b/src/openzeppelin/tests/mocks.cairo @@ -1 +1,3 @@ +mod reentrancy_attacker_mock; +mod reentrancy_mock; mod mock_pausable; diff --git a/src/openzeppelin/tests/mocks/reentrancy_attacker_mock.cairo b/src/openzeppelin/tests/mocks/reentrancy_attacker_mock.cairo new file mode 100644 index 000000000..072432d00 --- /dev/null +++ b/src/openzeppelin/tests/mocks/reentrancy_attacker_mock.cairo @@ -0,0 +1,21 @@ +#[abi] +trait IAttacker { + fn call_sender(); +} + +#[contract] +mod Attacker { + // Dispatcher + use openzeppelin::tests::mocks::reentrancy_mock::IReentrancyMockDispatcher; + use openzeppelin::tests::mocks::reentrancy_mock::IReentrancyMockDispatcherTrait; + + // Other + use starknet::ContractAddress; + use starknet::get_caller_address; + + #[external] + fn call_sender() { + let caller: ContractAddress = get_caller_address(); + IReentrancyMockDispatcher { contract_address: caller }.callback(); + } +} diff --git a/src/openzeppelin/tests/mocks/reentrancy_mock.cairo b/src/openzeppelin/tests/mocks/reentrancy_mock.cairo new file mode 100644 index 000000000..a950d27f1 --- /dev/null +++ b/src/openzeppelin/tests/mocks/reentrancy_mock.cairo @@ -0,0 +1,93 @@ +use starknet::ContractAddress; + +#[abi] +trait IReentrancyGuarded { + fn count_external_recursive(n: felt252); +} + +#[abi] +trait IReentrancyMock { + #[view] + fn current_count() -> felt252; + #[external] + fn callback(); + #[external] + fn count_local_recursive(n: felt252); + #[external] + fn count_external_recursive(n: felt252); + #[external] + fn count_and_call(attacker: ContractAddress); + #[external] + fn count(); +} + +#[contract] +mod ReentrancyMock { + // OZ modules + use openzeppelin::security::reentrancyguard::ReentrancyGuard; + + // Dispatchers + use super::IReentrancyGuardedDispatcher; + use super::IReentrancyGuardedDispatcherTrait; + use openzeppelin::tests::mocks::reentrancy_attacker_mock::IAttackerDispatcher; + use openzeppelin::tests::mocks::reentrancy_attacker_mock::IAttackerDispatcherTrait; + + // Other + use option::OptionTrait; + use starknet::ContractAddress; + use starknet::get_caller_address; + use starknet::get_contract_address; + + struct Storage { + counter: felt252 + } + + #[view] + fn current_count() -> felt252 { + counter::read() + } + + #[external] + fn callback() { + ReentrancyGuard::start(); + count(); + ReentrancyGuard::end(); + } + + #[external] + fn count_local_recursive(n: felt252) { + ReentrancyGuard::start(); + gas::withdraw_gas().expect('Out of gas'); + if n != 0 { + count(); + count_local_recursive(n - 1); + } + ReentrancyGuard::end(); + } + + #[external] + fn count_external_recursive(n: felt252) { + ReentrancyGuard::start(); + gas::withdraw_gas().expect('Out of gas'); + if n != 0 { + count(); + let this: ContractAddress = get_contract_address(); + IReentrancyGuardedDispatcher { contract_address: this }.count_external_recursive(n - 1) + } + ReentrancyGuard::end(); + } + + #[external] + fn count_and_call(attacker: ContractAddress) { + ReentrancyGuard::start(); + gas::withdraw_gas().expect('Out of gas'); + count(); + IAttackerDispatcher { contract_address: attacker }.call_sender(); + ReentrancyGuard::end(); + } + + #[external] + fn count() { + counter::write(counter::read() + 1); + } +} diff --git a/src/openzeppelin/tests/test_reentrancyguard.cairo b/src/openzeppelin/tests/test_reentrancyguard.cairo new file mode 100644 index 000000000..174035cb7 --- /dev/null +++ b/src/openzeppelin/tests/test_reentrancyguard.cairo @@ -0,0 +1,92 @@ +use openzeppelin::security::reentrancyguard::ReentrancyGuard; +use openzeppelin::tests::mocks::reentrancy_mock::ReentrancyMock; +use openzeppelin::tests::mocks::reentrancy_mock::IReentrancyMockDispatcher; +use openzeppelin::tests::mocks::reentrancy_mock::IReentrancyMockDispatcherTrait; +use openzeppelin::tests::mocks::reentrancy_attacker_mock::Attacker; +use openzeppelin::tests::utils; + +use array::ArrayTrait; + +fn deploy_mock() -> IReentrancyMockDispatcher { + let calldata = ArrayTrait::new(); + let address = utils::deploy(ReentrancyMock::TEST_CLASS_HASH, calldata); + IReentrancyMockDispatcher { contract_address: address } +} + +// +// ReentrancyGuard direct call tests +// + +#[test] +#[available_gas(2000000)] +fn test_reentrancy_guard_start() { + assert(!ReentrancyGuard::entered::read(), 'Guard should not be active'); + ReentrancyGuard::start(); + assert(ReentrancyGuard::entered::read(), 'Guard should be active'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ReentrancyGuard: reentrant call', ))] +fn test_reentrancy_guard_start_when_started() { + ReentrancyGuard::start(); + ReentrancyGuard::start(); +} + +#[test] +#[available_gas(2000000)] +fn test_reentrancy_guard_end() { + ReentrancyGuard::start(); + ReentrancyGuard::end(); + assert(!ReentrancyGuard::entered::read(), 'Guard should not be active'); +} + +// +// Mock implementation tests +// + +#[test] +#[available_gas(2000000)] +#[should_panic( + expected: ( + 'ReentrancyGuard: reentrant call', + 'ENTRYPOINT_FAILED', + 'ENTRYPOINT_FAILED', + 'ENTRYPOINT_FAILED' + ), +)] +fn test_remote_callback() { + let contract = deploy_mock(); + + // Deploy attacker + let calldata = ArrayTrait::new(); + let attacker_addr = utils::deploy(Attacker::TEST_CLASS_HASH, calldata); + + contract.count_and_call(attacker_addr); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ReentrancyGuard: reentrant call', 'ENTRYPOINT_FAILED'))] +fn test_local_recursion() { + let contract = deploy_mock(); + contract.count_local_recursive(10); +} + +#[test] +#[available_gas(2000000)] +#[should_panic( + expected: ('ReentrancyGuard: reentrant call', 'ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED') +)] +fn test_external_recursion() { + let contract = deploy_mock(); + contract.count_external_recursive(10); +} + +#[test] +#[available_gas(2000000)] +fn test_nonreentrant_function_call() { + let contract = deploy_mock(); + contract.callback(); + assert(contract.current_count() == 1, 'Call should execute'); +} diff --git a/src/openzeppelin/tests/utils.cairo b/src/openzeppelin/tests/utils.cairo new file mode 100644 index 000000000..d3b720cce --- /dev/null +++ b/src/openzeppelin/tests/utils.cairo @@ -0,0 +1,14 @@ +use array::ArrayTrait; +use core::result::ResultTrait; +use option::OptionTrait; +use starknet::class_hash::Felt252TryIntoClassHash; +use starknet::ContractAddress; +use traits::TryInto; + +fn deploy(contract_class_hash: felt252, calldata: Array) -> ContractAddress { + let (address, _) = starknet::deploy_syscall( + contract_class_hash.try_into().unwrap(), 0, calldata.span(), false + ) + .unwrap(); + address +} From 897facb5e0750baf76c1de1d587837da85eb9a6a Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Fri, 19 May 2023 17:37:19 -0400 Subject: [PATCH 049/246] Migrate access control (#605) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add accesscontrol * add accesscontrol tests * remove mock, add impl to contract * fix formatting * remove accesscontrol mock * formatting, remove unused imports * integrate erc165 * fix typo * update cairo to alpha7 * update Cargo * update expected syntax * update expected syntax * update cairo to rc0 * update Cargo * fix import * simplify imports * Apply suggestions from code review Co-authored-by: Martín Triay * fix storage mapping name * rename impl * add warning * fix comment * remove constructor, setup test constructor in setup * fix format * fix import format * add test for granting already granted role * add clarity to revoke role test * add test for revoking already revoked role * add test for renouncing already renounced role * add test for revoked admin role losing privileges * fix test_role_admin_cycle * add target func as comment * add default admin tests * refactor tests, rename actors * add tests for has_role and set_role_admin * tidy up tests * add general doc for events * fix assert_only_role test * remove redundant test * remove unnecessary assertion * remove duplicate test * Update src/openzeppelin/access/accesscontrol.cairo Co-authored-by: Martín Triay * fix imports, define constants locally * Apply suggestions from code review Co-authored-by: Martín Triay * fix test name * bump cairo * fix formatting --------- Co-authored-by: Martín Triay --- src/openzeppelin/access.cairo | 1 + src/openzeppelin/access/accesscontrol.cairo | 151 +++++++++ src/openzeppelin/tests.cairo | 1 + .../tests/test_accesscontrol.cairo | 291 ++++++++++++++++++ 4 files changed, 444 insertions(+) create mode 100644 src/openzeppelin/access/accesscontrol.cairo create mode 100644 src/openzeppelin/tests/test_accesscontrol.cairo diff --git a/src/openzeppelin/access.cairo b/src/openzeppelin/access.cairo index cadc81b51..8f59b4ad1 100644 --- a/src/openzeppelin/access.cairo +++ b/src/openzeppelin/access.cairo @@ -1 +1,2 @@ +mod accesscontrol; mod ownable; diff --git a/src/openzeppelin/access/accesscontrol.cairo b/src/openzeppelin/access/accesscontrol.cairo new file mode 100644 index 000000000..d6bac6fc6 --- /dev/null +++ b/src/openzeppelin/access/accesscontrol.cairo @@ -0,0 +1,151 @@ +use starknet::ContractAddress; + +const DEFAULT_ADMIN_ROLE: felt252 = 0; +const IACCESSCONTROL_ID: u32 = 0x7965db0b_u32; + +#[abi] +trait IAccessControl { + fn has_role(role: felt252, account: ContractAddress) -> bool; + fn get_role_admin(role: felt252) -> felt252; + fn grant_role(role: felt252, account: ContractAddress); + fn revoke_role(role: felt252, account: ContractAddress); + fn renounce_role(role: felt252, account: ContractAddress); +} + +#[contract] +mod AccessControl { + use super::IAccessControl; + use super::DEFAULT_ADMIN_ROLE; + use super::IACCESSCONTROL_ID; + use openzeppelin::introspection::erc165::ERC165; + use starknet::ContractAddress; + use starknet::get_caller_address; + + struct Storage { + role_admin: LegacyMap, + role_members: LegacyMap<(felt252, ContractAddress), bool>, + } + + /// Emitted when `account` is granted `role`. + /// + /// `sender` is the account that originated the contract call, an admin role + /// bearer (except if `_grant_role` is called during initialization from the constructor). + #[event] + fn RoleGranted(role: felt252, account: ContractAddress, sender: ContractAddress) {} + + /// Emitted when `account` is revoked `role`. + /// + /// `sender` is the account that originated the contract call: + /// - If using `revoke_role`, it is the admin role bearer. + /// - If using `renounce_role`, it is the role bearer (i.e. `account`). + #[event] + fn RoleRevoked(role: felt252, account: ContractAddress, sender: ContractAddress) {} + + /// Emitted when `new_admin_role` is set as `role`'s admin role, replacing `previous_admin_role` + /// + /// `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite + /// {RoleAdminChanged} not being emitted signaling this. + #[event] + fn RoleAdminChanged(role: felt252, previous_admin_role: felt252, new_admin_role: felt252) {} + + impl AccessControlImpl of IAccessControl { + fn has_role(role: felt252, account: ContractAddress) -> bool { + role_members::read((role, account)) + } + + fn get_role_admin(role: felt252) -> felt252 { + role_admin::read(role) + } + + fn grant_role(role: felt252, account: ContractAddress) { + let admin = get_role_admin(role); + assert_only_role(admin); + _grant_role(role, account); + } + + fn revoke_role(role: felt252, account: ContractAddress) { + let admin: felt252 = get_role_admin(role); + assert_only_role(admin); + _revoke_role(role, account); + } + + fn renounce_role(role: felt252, account: ContractAddress) { + let caller: ContractAddress = get_caller_address(); + assert(caller == account, 'Can only renounce role for self'); + _revoke_role(role, account); + } + } + + #[view] + fn supports_interface(interface_id: u32) -> bool { + ERC165::supports_interface(interface_id) + } + + #[view] + fn has_role(role: felt252, account: ContractAddress) -> bool { + AccessControlImpl::has_role(role, account) + } + + #[view] + fn get_role_admin(role: felt252) -> felt252 { + AccessControlImpl::get_role_admin(role) + } + + #[external] + fn grant_role(role: felt252, account: ContractAddress) { + AccessControlImpl::grant_role(role, account); + } + + #[external] + fn revoke_role(role: felt252, account: ContractAddress) { + AccessControlImpl::revoke_role(role, account); + } + + #[external] + fn renounce_role(role: felt252, account: ContractAddress) { + AccessControlImpl::renounce_role(role, account); + } + + #[internal] + fn initializer() { + ERC165::register_interface(IACCESSCONTROL_ID); + } + + #[internal] + fn assert_only_role(role: felt252) { + let caller: ContractAddress = get_caller_address(); + let authorized: bool = has_role(role, caller); + assert(authorized, 'Caller is missing role'); + } + + // + // WARNING + // The following internal methods are unprotected and should not be used + // outside of a contract's constructor. + // + + #[internal] + fn _grant_role(role: felt252, account: ContractAddress) { + if !has_role(role, account) { + let caller: ContractAddress = get_caller_address(); + role_members::write((role, account), true); + RoleGranted(role, account, caller); + } + } + + #[internal] + fn _revoke_role(role: felt252, account: ContractAddress) { + if has_role(role, account) { + let caller: ContractAddress = get_caller_address(); + role_members::write((role, account), false); + RoleRevoked(role, account, caller); + } + } + + #[internal] + fn _set_role_admin(role: felt252, admin_role: felt252) { + let previous_admin_role: felt252 = get_role_admin(role); + role_admin::write(role, admin_role); + RoleAdminChanged(role, previous_admin_role, admin_role); + } +} diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index 80288f54b..69490bef7 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -1,3 +1,4 @@ +mod test_accesscontrol; mod test_reentrancyguard; mod test_ownable; mod test_erc165; diff --git a/src/openzeppelin/tests/test_accesscontrol.cairo b/src/openzeppelin/tests/test_accesscontrol.cairo new file mode 100644 index 000000000..16b0a41fa --- /dev/null +++ b/src/openzeppelin/tests/test_accesscontrol.cairo @@ -0,0 +1,291 @@ +use openzeppelin::access::accesscontrol::AccessControl; +use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; +use openzeppelin::access::accesscontrol::IACCESSCONTROL_ID; +use starknet::contract_address_const; +use starknet::ContractAddress; +use starknet::testing; + +const ROLE: felt252 = 41; +const OTHER_ROLE: felt252 = 42; + +fn ADMIN() -> ContractAddress { + contract_address_const::<1>() +} + +fn AUTHORIZED() -> ContractAddress { + contract_address_const::<2>() +} + +fn OTHER() -> ContractAddress { + contract_address_const::<3>() +} + +fn OTHER_ADMIN() -> ContractAddress { + contract_address_const::<4>() +} + +fn setup() { + AccessControl::_grant_role(DEFAULT_ADMIN_ROLE, ADMIN()); + testing::set_caller_address(ADMIN()); +} + +// +// initializer +// + +#[test] +#[available_gas(2000000)] +fn test_initializer() { + AccessControl::initializer(); + assert(AccessControl::supports_interface(IACCESSCONTROL_ID), 'Should support own interface'); +} + +// +// has_role +// + +#[test] +#[available_gas(2000000)] +fn test_has_role() { + setup(); + assert(!AccessControl::has_role(ROLE, AUTHORIZED()), 'should not have role'); + AccessControl::_grant_role(ROLE, AUTHORIZED()); + assert(AccessControl::has_role(ROLE, AUTHORIZED()), 'should have role'); +} + + +// +// assert_only_role +// + +#[test] +#[available_gas(2000000)] +fn test_assert_only_role() { + setup(); + AccessControl::grant_role(ROLE, AUTHORIZED()); + testing::set_caller_address(AUTHORIZED()); + AccessControl::assert_only_role(ROLE); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Caller is missing role', ))] +fn test_assert_only_role_unauthorized() { + setup(); + testing::set_caller_address(OTHER()); + AccessControl::assert_only_role(ROLE); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Caller is missing role', ))] +fn test_assert_only_role_unauthorized_when_authorized_for_another_role() { + setup(); + AccessControl::grant_role(ROLE, AUTHORIZED()); + testing::set_caller_address(AUTHORIZED()); + AccessControl::assert_only_role(OTHER_ROLE); +} + +// +// grant_role +// + +#[test] +#[available_gas(2000000)] +fn test_grant_role() { + setup(); + AccessControl::grant_role(ROLE, AUTHORIZED()); + assert(AccessControl::has_role(ROLE, AUTHORIZED()), 'Role should be granted'); +} + +#[test] +#[available_gas(2000000)] +fn test_grant_role_multiple_times_for_granted_role() { + setup(); + AccessControl::grant_role(ROLE, AUTHORIZED()); + AccessControl::grant_role(ROLE, AUTHORIZED()); + assert(AccessControl::has_role(ROLE, AUTHORIZED()), 'Role should still be granted'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Caller is missing role', ))] +fn test_grant_role_unauthorized() { + setup(); + testing::set_caller_address(AUTHORIZED()); + AccessControl::grant_role(ROLE, AUTHORIZED()); +} + +// +// revoke_role +// + +#[test] +#[available_gas(2000000)] +fn test_revoke_role_for_role_not_granted() { + setup(); + AccessControl::revoke_role(ROLE, AUTHORIZED()); +} + +#[test] +#[available_gas(2000000)] +fn test_revoke_role_for_granted_role() { + setup(); + AccessControl::grant_role(ROLE, AUTHORIZED()); + AccessControl::revoke_role(ROLE, AUTHORIZED()); + assert(!AccessControl::has_role(ROLE, AUTHORIZED()), 'Role should be revoked'); +} + +#[test] +#[available_gas(2000000)] +fn test_revoke_role_multiple_times_for_granted_role() { + setup(); + AccessControl::grant_role(ROLE, AUTHORIZED()); + + AccessControl::revoke_role(ROLE, AUTHORIZED()); + AccessControl::revoke_role(ROLE, AUTHORIZED()); + assert(!AccessControl::has_role(ROLE, AUTHORIZED()), 'Role should still be revoked'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Caller is missing role', ))] +fn test_revoke_role_unauthorized() { + setup(); + + testing::set_caller_address(OTHER()); + AccessControl::revoke_role(ROLE, AUTHORIZED()); +} + +// +// renounce_role +// + +#[test] +#[available_gas(2000000)] +fn test_renounce_role_for_role_not_granted() { + setup(); + testing::set_caller_address(AUTHORIZED()); + + AccessControl::renounce_role(ROLE, AUTHORIZED()); +} + +#[test] +#[available_gas(2000000)] +fn test_renounce_role_for_granted_role() { + setup(); + AccessControl::grant_role(ROLE, AUTHORIZED()); + testing::set_caller_address(AUTHORIZED()); + + AccessControl::renounce_role(ROLE, AUTHORIZED()); + assert(!AccessControl::has_role(ROLE, AUTHORIZED()), 'Role should be renounced'); +} + +#[test] +#[available_gas(2000000)] +fn test_renounce_role_multiple_times_for_granted_role() { + setup(); + AccessControl::grant_role(ROLE, AUTHORIZED()); + testing::set_caller_address(AUTHORIZED()); + + AccessControl::renounce_role(ROLE, AUTHORIZED()); + AccessControl::renounce_role(ROLE, AUTHORIZED()); + assert(!AccessControl::has_role(ROLE, AUTHORIZED()), 'Role should still be renounced'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Can only renounce role for self', ))] +fn test_renounce_role_unauthorized() { + setup(); + AccessControl::grant_role(ROLE, AUTHORIZED()); + + // Admin is unauthorized caller + AccessControl::renounce_role(ROLE, AUTHORIZED()); +} + +// +// _set_role_admin +// + +#[test] +#[available_gas(2000000)] +fn test__set_role_admin() { + setup(); + assert( + AccessControl::get_role_admin(ROLE) == DEFAULT_ADMIN_ROLE, 'ROLE admin default should be 0' + ); + AccessControl::_set_role_admin(ROLE, OTHER_ROLE); + assert(AccessControl::get_role_admin(ROLE) == OTHER_ROLE, 'ROLE admin should be OTHER_ROLE'); +} + +#[test] +#[available_gas(2000000)] +fn test_new_admin_can_grant_roles() { + setup(); + AccessControl::_set_role_admin(ROLE, OTHER_ROLE); + AccessControl::grant_role(OTHER_ROLE, OTHER_ADMIN()); + + testing::set_caller_address(OTHER_ADMIN()); + AccessControl::grant_role(ROLE, AUTHORIZED()); + + assert(AccessControl::has_role(ROLE, AUTHORIZED()), 'AUTHORIZED should have ROLE'); +} + +#[test] +#[available_gas(2000000)] +fn test_new_admin_can_revoke_roles() { + setup(); + AccessControl::_set_role_admin(ROLE, OTHER_ROLE); + AccessControl::grant_role(OTHER_ROLE, OTHER_ADMIN()); + + testing::set_caller_address(OTHER_ADMIN()); + AccessControl::grant_role(ROLE, AUTHORIZED()); + AccessControl::revoke_role(ROLE, AUTHORIZED()); + + assert(!AccessControl::has_role(ROLE, AUTHORIZED()), 'AUTHORIZED should not have ROLE'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Caller is missing role', ))] +fn test_previous_admin_cannot_grant_roles() { + setup(); + AccessControl::_set_role_admin(ROLE, OTHER_ROLE); + + // Caller is ADMIN + AccessControl::grant_role(ROLE, AUTHORIZED()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Caller is missing role', ))] +fn test_previous_admin_cannot_revoke_roles() { + setup(); + AccessControl::_set_role_admin(ROLE, OTHER_ROLE); + + // Caller is ADMIN + AccessControl::revoke_role(ROLE, AUTHORIZED()); +} + +// +// default admin +// + +#[test] +#[available_gas(2000000)] +fn test_other_role_admin_is_the_default_admin_role() { + assert( + AccessControl::get_role_admin(OTHER_ROLE) == DEFAULT_ADMIN_ROLE, + 'Should be DEFAULT_ADMIN_ROLE' + ); +} + +#[test] +#[available_gas(2000000)] +fn test_default_admin_role_is_its_own_admin() { + assert( + AccessControl::get_role_admin(DEFAULT_ADMIN_ROLE) == DEFAULT_ADMIN_ROLE, + 'Should be DEFAULT_ADMIN_ROLE' + ); +} From 372de37a6e9ab3fc2e747baa4d2e8f727f834bc7 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Tue, 23 May 2023 18:41:54 -0400 Subject: [PATCH 050/246] Migrate account (#620) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * continue account implementation * add missing account interface functions * tidy up module * fix validate * bump cairo + account changes * fix __execute__, add serde, rename felt>felt252 * tidy up code * add account tests * complete account implementation * Apply suggestions from code review Co-authored-by: Andrew Fleming Co-authored-by: Hadrien Croubois * apply review suggestions * remove unused import * clarify __execute__ guard * add account tests * add internals * tidy up * add internal macro * add internal macro * add deregister * update erc165 * feat: refactor account and add validate_transaction test * feat: work on span serde * feat: add test for validate * refactor: remove unnecessary imports * refactor: tests * feat: implement test_execute * Update src/openzeppelin/tests/test_account.cairo Co-authored-by: Martín Triay * Update src/openzeppelin/tests/test_account.cairo Co-authored-by: Martín Triay * feat: update from review * feat: format files * feat: SpanSerde compiling version * feat: update tests and interface * feat: format file * feat: add test for multicall * feat: add more tests * test: remove unused function * refactor: update ERC20 interface and module * feat: add impl again * refactor: apply review comments * test: execute out of gas * refactor: is valid signature because of lack of short circuit condicionals * refactor: apply updates from reviews * feat: add SpanSerde implementation * refactor: remove span_to_array * Update src/openzeppelin/tests/test_account.cairo Co-authored-by: Andrew Fleming * Update src/openzeppelin/account.cairo Co-authored-by: Andrew Fleming * test: update from review * feat: splitting account interface and abi * feat: add new account files * remove underscore on mod api methods * remove comment * remove underscore * move erc1271 id to interface * remove unused import * integrate deploy util * add comments for validate dummy params * add initializer * change error msg * add empty sig tests * move test * replace dummy args, fix comments * fix formatting * tidy up code * add erc20 transfer call in execute test * tidy up imports * remove unused const --------- Co-authored-by: Martín Triay Co-authored-by: Hadrien Croubois Co-authored-by: Eric Nordelo --- src/openzeppelin/account.cairo | 6 + src/openzeppelin/account/account.cairo | 233 +++++++++++ src/openzeppelin/account/interface.cairo | 21 + src/openzeppelin/lib.cairo | 1 + src/openzeppelin/tests.cairo | 1 + src/openzeppelin/tests/test_account.cairo | 457 ++++++++++++++++++++++ src/openzeppelin/token/erc20.cairo | 26 +- src/openzeppelin/utils.cairo | 16 + 8 files changed, 759 insertions(+), 2 deletions(-) create mode 100644 src/openzeppelin/account.cairo create mode 100644 src/openzeppelin/account/account.cairo create mode 100644 src/openzeppelin/account/interface.cairo create mode 100644 src/openzeppelin/tests/test_account.cairo diff --git a/src/openzeppelin/account.cairo b/src/openzeppelin/account.cairo new file mode 100644 index 000000000..75f26fdf1 --- /dev/null +++ b/src/openzeppelin/account.cairo @@ -0,0 +1,6 @@ +mod account; +use account::{ + Account, AccountABIDispatcher, AccountABIDispatcherTrait, TRANSACTION_VERSION, QUERY_VERSION +}; + +mod interface; diff --git a/src/openzeppelin/account/account.cairo b/src/openzeppelin/account/account.cairo new file mode 100644 index 000000000..267aa9534 --- /dev/null +++ b/src/openzeppelin/account/account.cairo @@ -0,0 +1,233 @@ +use array::ArrayTrait; +use array::SpanTrait; +use option::OptionTrait; +use serde::Serde; +use serde::deserialize_array_helper; +use serde::serialize_array_helper; +use starknet::ContractAddress; + +use openzeppelin::account::interface::Call; + +const TRANSACTION_VERSION: felt252 = 1; +// 2**128 + TRANSACTION_VERSION +const QUERY_VERSION: felt252 = 340282366920938463463374607431768211457; + +#[abi] +trait AccountABI { + #[external] + fn __execute__(calls: Array) -> Array>; + #[external] + fn __validate__(calls: Array) -> felt252; + #[external] + fn __validate_declare__(class_hash: felt252) -> felt252; + #[external] + fn __validate_deploy__( + class_hash: felt252, contract_address_salt: felt252, _public_key: felt252 + ) -> felt252; + #[external] + fn set_public_key(new_public_key: felt252); + #[view] + fn get_public_key() -> felt252; + #[view] + fn is_valid_signature(message: felt252, signature: Array) -> u32; + #[view] + fn supports_interface(interface_id: u32) -> bool; +} + +#[account_contract] +mod Account { + use array::SpanTrait; + use array::ArrayTrait; + use box::BoxTrait; + use ecdsa::check_ecdsa_signature; + use serde::ArraySerde; + use starknet::get_tx_info; + use starknet::get_caller_address; + use starknet::get_contract_address; + use option::OptionTrait; + use zeroable::Zeroable; + + use openzeppelin::account::interface::ERC1271_VALIDATED; + use openzeppelin::account::interface::IAccount; + use openzeppelin::account::interface::IACCOUNT_ID; + use openzeppelin::introspection::erc165::ERC165; + + use super::Call; + use super::QUERY_VERSION; + use super::SpanSerde; + use super::TRANSACTION_VERSION; + + struct Storage { + public_key: felt252 + } + + impl AccountImpl of IAccount { + fn __execute__(mut calls: Array) -> Array> { + // Avoid calls from other contracts + // https://github.com/OpenZeppelin/cairo-contracts/issues/344 + let sender = get_caller_address(); + assert(sender.is_zero(), 'Account: invalid caller'); + + // Check tx version + let tx_info = get_tx_info().unbox(); + let version = tx_info.version; + if version != TRANSACTION_VERSION { + assert(version == QUERY_VERSION, 'Account: invalid tx version'); + } + + _execute_calls(calls) + } + + fn __validate__(mut calls: Array) -> felt252 { + validate_transaction() + } + + fn __validate_declare__(class_hash: felt252) -> felt252 { + validate_transaction() + } + + fn is_valid_signature(message: felt252, signature: Array) -> u32 { + if _is_valid_signature(message, signature.span()) { + ERC1271_VALIDATED + } else { + 0_u32 + } + } + + fn supports_interface(interface_id: u32) -> bool { + ERC165::supports_interface(interface_id) + } + } + + #[constructor] + fn constructor(_public_key: felt252) { + initializer(_public_key); + } + + // + // Externals + // + + #[external] + fn __execute__(mut calls: Array) -> Array> { + AccountImpl::__execute__(calls) + } + + #[external] + fn __validate__(mut calls: Array) -> felt252 { + AccountImpl::__validate__(calls) + } + + #[external] + fn __validate_declare__(class_hash: felt252) -> felt252 { + AccountImpl::__validate_declare__(class_hash) + } + + #[external] + fn __validate_deploy__( + class_hash: felt252, contract_address_salt: felt252, _public_key: felt252 + ) -> felt252 { + validate_transaction() + } + + #[external] + fn set_public_key(new_public_key: felt252) { + assert_only_self(); + public_key::write(new_public_key); + } + + // + // View + // + + #[view] + fn get_public_key() -> felt252 { + public_key::read() + } + + #[view] + fn is_valid_signature(message: felt252, signature: Array) -> u32 { + AccountImpl::is_valid_signature(message, signature) + } + + #[view] + fn supports_interface(interface_id: u32) -> bool { + AccountImpl::supports_interface(interface_id) + } + + // + // Internals + // + + #[internal] + fn initializer(_public_key: felt252) { + ERC165::register_interface(IACCOUNT_ID); + public_key::write(_public_key); + } + + #[internal] + fn assert_only_self() { + let caller = get_caller_address(); + let self = get_contract_address(); + assert(self == caller, 'Account: unauthorized'); + } + + #[internal] + fn validate_transaction() -> felt252 { + let tx_info = get_tx_info().unbox(); + let tx_hash = tx_info.transaction_hash; + let signature = tx_info.signature; + assert(_is_valid_signature(tx_hash, signature), 'Account: invalid signature'); + starknet::VALIDATED + } + + #[internal] + fn _is_valid_signature(message: felt252, signature: Span) -> bool { + let valid_length = signature.len() == 2_u32; + + if valid_length { + check_ecdsa_signature( + message, public_key::read(), *signature.at(0_u32), *signature.at(1_u32) + ) + } else { + false + } + } + + #[internal] + fn _execute_calls(mut calls: Array) -> Array> { + let mut res = ArrayTrait::new(); + loop { + match calls.pop_front() { + Option::Some(call) => { + let _res = _execute_single_call(call); + res.append(_res); + }, + Option::None(_) => { + break (); + }, + }; + }; + res + } + + #[internal] + fn _execute_single_call(call: Call) -> Span { + let Call{to, selector, calldata } = call; + starknet::call_contract_syscall(to, selector, calldata.span()).unwrap_syscall() + } +} + +impl SpanSerde< + T, impl TSerde: Serde, impl TCopy: Copy, impl TDrop: Drop +> of Serde> { + fn serialize(self: @Span, ref output: Array) { + (*self).len().serialize(ref output); + serialize_array_helper(*self, ref output); + } + fn deserialize(ref serialized: Span) -> Option> { + let length = *serialized.pop_front()?; + let mut arr = ArrayTrait::new(); + Option::Some(deserialize_array_helper(ref serialized, arr, length)?.span()) + } +} diff --git a/src/openzeppelin/account/interface.cairo b/src/openzeppelin/account/interface.cairo new file mode 100644 index 000000000..5c77f797b --- /dev/null +++ b/src/openzeppelin/account/interface.cairo @@ -0,0 +1,21 @@ +use array::ArrayTrait; +use array::SpanTrait; +use starknet::ContractAddress; + +const IACCOUNT_ID: u32 = 0xa66bd575_u32; +const ERC1271_VALIDATED: u32 = 0x1626ba7e_u32; + +#[derive(Serde, Drop)] +struct Call { + to: ContractAddress, + selector: felt252, + calldata: Array +} + +trait IAccount { + fn __execute__(calls: Array) -> Array>; + fn __validate__(calls: Array) -> felt252; + fn __validate_declare__(class_hash: felt252) -> felt252; + fn is_valid_signature(message: felt252, signature: Array) -> u32; + fn supports_interface(interface_id: u32) -> bool; +} diff --git a/src/openzeppelin/lib.cairo b/src/openzeppelin/lib.cairo index 1f55a830f..47d73c605 100644 --- a/src/openzeppelin/lib.cairo +++ b/src/openzeppelin/lib.cairo @@ -1,6 +1,7 @@ mod access; mod introspection; mod security; +mod account; mod token; mod tests; mod utils; diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index 69490bef7..e13d78136 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -2,6 +2,7 @@ mod test_accesscontrol; mod test_reentrancyguard; mod test_ownable; mod test_erc165; +mod test_account; mod test_erc20; mod test_pausable; mod test_initializable; diff --git a/src/openzeppelin/tests/test_account.cairo b/src/openzeppelin/tests/test_account.cairo new file mode 100644 index 000000000..d53ba3259 --- /dev/null +++ b/src/openzeppelin/tests/test_account.cairo @@ -0,0 +1,457 @@ +use array::ArrayTrait; +use core::traits::Into; +use option::OptionTrait; +use serde::Serde; +use starknet::ContractAddress; +use starknet::contract_address_const; +use starknet::testing; + +use openzeppelin::account::Account; +use openzeppelin::account::AccountABIDispatcher; +use openzeppelin::account::AccountABIDispatcherTrait; +use openzeppelin::account::interface::Call; +use openzeppelin::account::interface::ERC1271_VALIDATED; +use openzeppelin::account::interface::IACCOUNT_ID; +use openzeppelin::account::QUERY_VERSION; +use openzeppelin::account::TRANSACTION_VERSION; +use openzeppelin::introspection::erc165::IERC165_ID; +use openzeppelin::tests::utils; +use openzeppelin::token::erc20::ERC20; +use openzeppelin::token::erc20::IERC20Dispatcher; +use openzeppelin::token::erc20::IERC20DispatcherTrait; + +const PUBLIC_KEY: felt252 = 0x333333; +const NEW_PUBKEY: felt252 = 0x789789; +const TRANSFER_SELECTOR: felt252 = 0x83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e; +const SALT: felt252 = 123; + +#[derive(Drop)] +struct SignedTransactionData { + private_key: felt252, + public_key: felt252, + transaction_hash: felt252, + r: felt252, + s: felt252 +} + +fn CLASS_HASH() -> felt252 { + Account::TEST_CLASS_HASH +} +fn ACCOUNT_ADDRESS() -> ContractAddress { + contract_address_const::<0x111111>() +} +fn SIGNED_TX_DATA() -> SignedTransactionData { + SignedTransactionData { + private_key: 1234, + public_key: 883045738439352841478194533192765345509759306772397516907181243450667673002, + transaction_hash: 2717105892474786771566982177444710571376803476229898722748888396642649184538, + r: 3068558690657879390136740086327753007413919701043650133111397282816679110801, + s: 3355728545224320878895493649495491771252432631648740019139167265522817576501 + } +} + +fn setup_dispatcher(data: Option<@SignedTransactionData>) -> AccountABIDispatcher { + // Set the transaction version + testing::set_version(TRANSACTION_VERSION); + + // Deploy the account contract + let mut calldata = ArrayTrait::new(); + + if data.is_some() { + let data = data.unwrap(); + + // Set the signature and transaction hash + let mut signature = ArrayTrait::new(); + signature.append(*data.r); + signature.append(*data.s); + testing::set_signature(signature.span()); + testing::set_transaction_hash(*data.transaction_hash); + + calldata.append(*data.public_key); + } else { + calldata.append(PUBLIC_KEY); + } + + let address = utils::deploy(Account::TEST_CLASS_HASH, calldata); + AccountABIDispatcher { contract_address: address } +} + +fn deploy_erc20(recipient: ContractAddress, initial_supply: u256) -> IERC20Dispatcher { + let name = 0; + let symbol = 0; + let mut calldata = ArrayTrait::::new(); + + calldata.append(name); + calldata.append(symbol); + calldata.append(initial_supply.low.into()); + calldata.append(initial_supply.high.into()); + calldata.append(recipient.into()); + + let address = utils::deploy(ERC20::TEST_CLASS_HASH, calldata); + IERC20Dispatcher { contract_address: address } +} + +#[test] +#[available_gas(2000000)] +fn test_constructor() { + Account::constructor(PUBLIC_KEY); + assert(Account::get_public_key() == PUBLIC_KEY, 'Should return public key'); +} + +#[test] +#[available_gas(2000000)] +fn test_interfaces() { + Account::constructor(PUBLIC_KEY); + + let supports_default_interface = Account::supports_interface(IERC165_ID); + assert(supports_default_interface, 'Should support base interface'); + + let supports_account_interface = Account::supports_interface(IACCOUNT_ID); + assert(supports_account_interface, 'Should support account id'); +} + +#[test] +#[available_gas(2000000)] +fn test_is_valid_signature() { + let data = SIGNED_TX_DATA(); + let message = data.transaction_hash; + + let mut good_signature = ArrayTrait::new(); + good_signature.append(data.r); + good_signature.append(data.s); + + let mut bad_signature = ArrayTrait::new(); + bad_signature.append(0x987); + bad_signature.append(0x564); + + Account::set_public_key(data.public_key); + + let is_valid = Account::is_valid_signature(message, good_signature); + assert(is_valid == ERC1271_VALIDATED, 'Should accept valid signature'); + + let is_valid = Account::is_valid_signature(message, bad_signature); + assert(is_valid == 0_u32, 'Should reject invalid signature'); +} + +#[test] +#[available_gas(2000000)] +fn test_validate_deploy() { + let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + + // `__validate_deploy__` does not directly use the passed arguments. Their + // values are already integrated in the tx hash. The passed arguments in this + // testing context are decoupled from the signature and have no effect on the test. + assert( + account.__validate_deploy__(CLASS_HASH(), SALT, PUBLIC_KEY) == starknet::VALIDATED, + 'Should validate correctly' + ); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +fn test_validate_deploy_invalid_signature_data() { + let mut data = SIGNED_TX_DATA(); + data.transaction_hash += 1; + let account = setup_dispatcher(Option::Some(@data)); + + account.__validate_deploy__(CLASS_HASH(), SALT, PUBLIC_KEY); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +fn test_validate_deploy_invalid_signature_length() { + let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let mut signature = ArrayTrait::new(); + + signature.append(0x1); + testing::set_signature(signature.span()); + + account.__validate_deploy__(CLASS_HASH(), SALT, PUBLIC_KEY); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +fn test_validate_deploy_empty_signature() { + let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let empty_sig = ArrayTrait::new(); + + testing::set_signature(empty_sig.span()); + account.__validate_deploy__(CLASS_HASH(), SALT, PUBLIC_KEY); +} + +#[test] +#[available_gas(2000000)] +fn test_validate_declare() { + let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + + // `__validate_declare__` does not directly use the class_hash argument. Its + // value is already integrated in the tx hash. The class_hash argument in this + // testing context is decoupled from the signature and has no effect on the test. + assert( + account.__validate_declare__(CLASS_HASH()) == starknet::VALIDATED, + 'Should validate correctly' + ); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +fn test_validate_declare_invalid_signature_data() { + let mut data = SIGNED_TX_DATA(); + data.transaction_hash += 1; + let account = setup_dispatcher(Option::Some(@data)); + + account.__validate_declare__(CLASS_HASH()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +fn test_validate_declare_invalid_signature_length() { + let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let mut signature = ArrayTrait::new(); + + signature.append(0x1); + testing::set_signature(signature.span()); + + account.__validate_declare__(CLASS_HASH()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +fn test_validate_declare_empty_signature() { + let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let empty_sig = ArrayTrait::new(); + + testing::set_signature(empty_sig.span()); + + account.__validate_declare__(CLASS_HASH()); +} + +fn test_execute_with_version(version: Option) { + let data = SIGNED_TX_DATA(); + let account = setup_dispatcher(Option::Some(@data)); + let erc20 = deploy_erc20(account.contract_address, 1000); + let recipient = contract_address_const::<0x123>(); + + // Craft call and add to calls array + let mut calldata = ArrayTrait::new(); + let amount: u256 = 200; + calldata.append(recipient.into()); + calldata.append(amount.low.into()); + calldata.append(amount.high.into()); + let call = Call { to: erc20.contract_address, selector: TRANSFER_SELECTOR, calldata: calldata }; + let mut calls = ArrayTrait::new(); + calls.append(call); + + // Handle version for test + if version.is_some() { + testing::set_version(version.unwrap()); + } + + // Execute + let ret = account.__execute__(calls); + + // Assert that the transfer was successful + assert(erc20.balance_of(account.contract_address) == 800, 'Should have remainder'); + assert(erc20.balance_of(recipient) == amount, 'Should have transferred'); + + // Test return value + let mut call_serialized_retval = *ret.at(0); + let call_retval = Serde::::deserialize(ref call_serialized_retval); + assert(call_retval.unwrap(), 'Should have succeeded'); +} + +#[test] +#[available_gas(2000000)] +fn test_execute() { + test_execute_with_version(Option::None(())); +} + +#[test] +#[available_gas(2000000)] +fn test_execute_query_version() { + test_execute_with_version(Option::Some(QUERY_VERSION)); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Account: invalid tx version', 'ENTRYPOINT_FAILED'))] +fn test_execute_invalid_version() { + test_execute_with_version(Option::Some(TRANSACTION_VERSION - 1)); +} + +#[test] +#[available_gas(2000000)] +fn test_validate() { + let calls = ArrayTrait::new(); + let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + + assert(account.__validate__(calls) == starknet::VALIDATED, 'Should validate correctly'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +fn test_validate_invalid() { + let calls = ArrayTrait::new(); + let mut data = SIGNED_TX_DATA(); + data.transaction_hash += 1; + let account = setup_dispatcher(Option::Some(@data)); + + account.__validate__(calls); +} + +#[test] +#[available_gas(2000000)] +fn test_multicall() { + let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let erc20 = deploy_erc20(account.contract_address, 1000); + let recipient1 = contract_address_const::<0x123>(); + let recipient2 = contract_address_const::<0x456>(); + let mut calls = ArrayTrait::new(); + + // Craft call1 + let mut calldata1 = ArrayTrait::new(); + let amount1: u256 = 300; + calldata1.append(recipient1.into()); + calldata1.append(amount1.low.into()); + calldata1.append(amount1.high.into()); + let call1 = Call { + to: erc20.contract_address, selector: TRANSFER_SELECTOR, calldata: calldata1 + }; + + // Craft call2 + let mut calldata2 = ArrayTrait::new(); + let amount2: u256 = 500; + calldata2.append(recipient2.into()); + calldata2.append(amount2.low.into()); + calldata2.append(amount2.high.into()); + let call2 = Call { + to: erc20.contract_address, selector: TRANSFER_SELECTOR, calldata: calldata2 + }; + + // Bundle calls and exeute + calls.append(call1); + calls.append(call2); + let ret = account.__execute__(calls); + + // Assert that the transfers were successful + assert(erc20.balance_of(account.contract_address) == 200, 'Should have remainder'); + assert(erc20.balance_of(recipient1) == 300, 'Should have transferred'); + assert(erc20.balance_of(recipient2) == 500, 'Should have transferred'); + + // Test return value + let mut call1_serialized_retval = *ret.at(0); + let mut call2_serialized_retval = *ret.at(1); + let call1_retval = Serde::::deserialize(ref call1_serialized_retval); + let call2_retval = Serde::::deserialize(ref call2_serialized_retval); + assert(call1_retval.unwrap(), 'Should have succeeded'); + assert(call2_retval.unwrap(), 'Should have succeeded'); +} + +#[test] +#[available_gas(2000000)] +fn test_multicall_zero_calls() { + let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let mut calls = ArrayTrait::new(); + + let ret = account.__execute__(calls); + + // Test return value + assert(ret.len() == 0, 'Should have an empty response'); +} + +#[test] +#[available_gas(2000000)] +fn test_public_key_setter_and_getter() { + testing::set_contract_address(ACCOUNT_ADDRESS()); + testing::set_caller_address(ACCOUNT_ADDRESS()); + Account::set_public_key(NEW_PUBKEY); + + let public_key = Account::get_public_key(); + assert(public_key == NEW_PUBKEY, 'Should update key'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Account: unauthorized', ))] +fn test_public_key_setter_different_account() { + let caller = contract_address_const::<0x123>(); + testing::set_contract_address(ACCOUNT_ADDRESS()); + testing::set_caller_address(caller); + Account::set_public_key(NEW_PUBKEY); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Account: invalid caller', ))] +fn test_account_called_from_contract() { + let calls = ArrayTrait::new(); + let caller = contract_address_const::<0x123>(); + testing::set_contract_address(ACCOUNT_ADDRESS()); + testing::set_caller_address(caller); + Account::__execute__(calls); +} + +// +// Test internals +// + +#[test] +#[available_gas(2000000)] +fn test_initializer() { + Account::initializer(PUBLIC_KEY); + assert(Account::get_public_key() == PUBLIC_KEY, 'Should return public key'); +} + +#[test] +#[available_gas(2000000)] +fn test_assert_only_self_true() { + testing::set_contract_address(ACCOUNT_ADDRESS()); + testing::set_caller_address(ACCOUNT_ADDRESS()); + Account::assert_only_self(); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Account: unauthorized', ))] +fn test_assert_only_self_false() { + testing::set_contract_address(ACCOUNT_ADDRESS()); + let other = contract_address_const::<0x4567>(); + testing::set_caller_address(other); + Account::assert_only_self(); +} + +#[test] +#[available_gas(2000000)] +fn test__is_valid_signature() { + let data = SIGNED_TX_DATA(); + let message = data.transaction_hash; + + let mut good_signature = ArrayTrait::new(); + good_signature.append(data.r); + good_signature.append(data.s); + + let mut bad_signature = ArrayTrait::new(); + bad_signature.append(0x987); + bad_signature.append(0x564); + + let mut invalid_length_signature = ArrayTrait::new(); + invalid_length_signature.append(0x987); + + Account::set_public_key(data.public_key); + + let is_valid = Account::_is_valid_signature(message, good_signature.span()); + assert(is_valid, 'Should accept valid signature'); + + let is_valid = Account::_is_valid_signature(message, bad_signature.span()); + assert(!is_valid, 'Should reject invalid signature'); + + let is_valid = Account::_is_valid_signature(message, invalid_length_signature.span()); + assert(!is_valid, 'Should reject invalid length'); +} diff --git a/src/openzeppelin/token/erc20.cairo b/src/openzeppelin/token/erc20.cairo index f2350bebc..eca9933d7 100644 --- a/src/openzeppelin/token/erc20.cairo +++ b/src/openzeppelin/token/erc20.cairo @@ -1,15 +1,29 @@ use starknet::ContractAddress; +#[abi] trait IERC20 { + #[view] fn name() -> felt252; + #[view] fn symbol() -> felt252; + #[view] fn decimals() -> u8; + #[view] fn total_supply() -> u256; + #[view] fn balance_of(account: ContractAddress) -> u256; + #[view] fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256; + #[external] fn transfer(recipient: ContractAddress, amount: u256) -> bool; + #[external] fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool; + #[external] fn approve(spender: ContractAddress, amount: u256) -> bool; + #[external] + fn increase_allowance(spender: ContractAddress, added_value: u256) -> bool; + #[external] + fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool; } #[contract] @@ -79,6 +93,14 @@ mod ERC20 { _approve(caller, spender, amount); true } + + fn increase_allowance(spender: ContractAddress, added_value: u256) -> bool { + _increase_allowance(spender, added_value) + } + + fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { + _decrease_allowance(spender, subtracted_value) + } } #[constructor] @@ -136,12 +158,12 @@ mod ERC20 { #[external] fn increase_allowance(spender: ContractAddress, added_value: u256) -> bool { - _increase_allowance(spender, added_value) + ERC20::increase_allowance(spender, added_value) } #[external] fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { - _decrease_allowance(spender, subtracted_value) + ERC20::decrease_allowance(spender, subtracted_value) } /// diff --git a/src/openzeppelin/utils.cairo b/src/openzeppelin/utils.cairo index 3709db21f..c8a47ceb8 100644 --- a/src/openzeppelin/utils.cairo +++ b/src/openzeppelin/utils.cairo @@ -1 +1,17 @@ +use array::ArrayTrait; +use array::SpanTrait; +use box::BoxTrait; +use option::OptionTrait; mod constants; + +#[inline(always)] +fn check_gas() { + match gas::withdraw_gas() { + Option::Some(_) => {}, + Option::None(_) => { + let mut data = ArrayTrait::new(); + data.append('Out of gas'); + panic(data); + }, + } +} From d86eb73879c0ae2ef30ab7762ea26235d6c425e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Tue, 30 May 2023 15:36:53 -0300 Subject: [PATCH 051/246] Migrate ERC721 (#619) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * continue account implementation * add missing account interface functions * tidy up module * fix validate * bump cairo + account changes * fix __execute__, add serde, rename felt>felt252 * tidy up code * WIP ERC721 * make format * fix dispatcher call * clean * working on tests * replace match with `is_some()` and `is_none()` for readeability * erc721 tests * use Option.expect * add account tests * check panic reason * rename _owner() to _owner_of() * spacing * complete account implementation * apply recommandation for PR * Apply suggestions from code review Co-authored-by: Andrew Fleming * check low level ownership int * prefix test function with test_ * Apply suggestions from code review Co-authored-by: Andrew Fleming Co-authored-by: Hadrien Croubois * apply review suggestions * remove unused import * clarify __execute__ guard * add account tests * add internals * tidy up * update ERC165 ids to u32 * apply sugestions from code review * Apply suggestions from code review Co-authored-by: Martín Triay * update & expand tests * update lock * add internal macro * add internal macro * add deregister * update erc165 * wip (dispatched issue) * fix abi * start array→span transition * minimise account dependency * add SpanSerde in utils * add dual interfaces * update test message. fix linter * split interfaces from preset module * fix linter * rename metadata id var * add camelCase to traits * fully implement dual interface traits * simplify _owner_of * add constructor and getter tests * add token_uri tests * remove conflictive test * add erc721receiver. add IERC721ABI * add safe_transfer_from tests * add safe_mint tests * Update src/openzeppelin/token/erc721/interface.cairo Co-authored-by: Eric Nordelo * move erc721 abi next to module * address review comments --------- Co-authored-by: Hadrien Croubois Co-authored-by: Andrew Fleming Co-authored-by: Andrew Fleming Co-authored-by: Eric Nordelo --- src/openzeppelin/introspection/erc165.cairo | 1 + src/openzeppelin/tests.cairo | 3 +- src/openzeppelin/tests/mocks.cairo | 1 + .../tests/mocks/erc721_receiver.cairo | 62 ++ src/openzeppelin/tests/test_erc721.cairo | 791 ++++++++++++++++++ src/openzeppelin/token.cairo | 1 + src/openzeppelin/token/erc721.cairo | 3 + src/openzeppelin/token/erc721/erc721.cairo | 462 ++++++++++ src/openzeppelin/token/erc721/interface.cairo | 71 ++ src/openzeppelin/utils.cairo | 1 + src/openzeppelin/utils/serde.cairo | 19 + 11 files changed, 1414 insertions(+), 1 deletion(-) create mode 100644 src/openzeppelin/tests/mocks/erc721_receiver.cairo create mode 100644 src/openzeppelin/tests/test_erc721.cairo create mode 100644 src/openzeppelin/token/erc721.cairo create mode 100644 src/openzeppelin/token/erc721/erc721.cairo create mode 100644 src/openzeppelin/token/erc721/interface.cairo create mode 100644 src/openzeppelin/utils/serde.cairo diff --git a/src/openzeppelin/introspection/erc165.cairo b/src/openzeppelin/introspection/erc165.cairo index 16f13205a..4a3b01778 100644 --- a/src/openzeppelin/introspection/erc165.cairo +++ b/src/openzeppelin/introspection/erc165.cairo @@ -1,6 +1,7 @@ const IERC165_ID: u32 = 0x01ffc9a7_u32; const INVALID_ID: u32 = 0xffffffff_u32; +#[abi] trait IERC165 { fn supports_interface(interface_id: u32) -> bool; } diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index e13d78136..5aa74d96f 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -4,7 +4,8 @@ mod test_ownable; mod test_erc165; mod test_account; mod test_erc20; -mod test_pausable; +mod test_erc721; mod test_initializable; +mod test_pausable; mod mocks; mod utils; diff --git a/src/openzeppelin/tests/mocks.cairo b/src/openzeppelin/tests/mocks.cairo index ca522a93c..b8f645b23 100644 --- a/src/openzeppelin/tests/mocks.cairo +++ b/src/openzeppelin/tests/mocks.cairo @@ -1,3 +1,4 @@ mod reentrancy_attacker_mock; mod reentrancy_mock; +mod erc721_receiver; mod mock_pausable; diff --git a/src/openzeppelin/tests/mocks/erc721_receiver.cairo b/src/openzeppelin/tests/mocks/erc721_receiver.cairo new file mode 100644 index 000000000..c9e848630 --- /dev/null +++ b/src/openzeppelin/tests/mocks/erc721_receiver.cairo @@ -0,0 +1,62 @@ +const SUCCESS: felt252 = 123123; +const FAILURE: felt252 = 456456; + +#[contract] +mod ERC721Receiver { + use openzeppelin::token::erc721::interface::IERC721Receiver; + use openzeppelin::token::erc721::interface::IERC721ReceiverCamel; + use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; + use openzeppelin::introspection::erc165::ERC165; + + use openzeppelin::utils::serde::SpanSerde; + use starknet::ContractAddress; + use array::SpanTrait; + + impl ERC721ReceiverImpl of IERC721Receiver { + fn on_erc721_received( + operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span + ) -> u32 { + if *data.at(0) == super::SUCCESS { + IERC721_RECEIVER_ID + } else { + 0 + } + } + } + + impl ERC721ReceiverCamelImpl of IERC721ReceiverCamel { + fn onERC721Received( + operator: ContractAddress, from: ContractAddress, tokenId: u256, data: Span + ) -> u32 { + ERC721ReceiverImpl::on_erc721_received(operator, from, tokenId, data) + } + } + + #[constructor] + fn constructor() { + ERC165::register_interface(IERC721_RECEIVER_ID); + } + + #[view] + fn supports_interface(interface_id: u32) -> bool { + ERC165::supports_interface(interface_id) + } + + #[external] + fn on_erc721_received( + operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span + ) -> u32 { + ERC721ReceiverImpl::on_erc721_received(operator, from, token_id, data) + } + + #[external] + fn onERC721Received( + operator: ContractAddress, from: ContractAddress, tokenId: u256, data: Span + ) -> u32 { + ERC721ReceiverCamelImpl::onERC721Received(operator, from, tokenId, data) + } +} + + +#[contract] +mod ERC721NonReceiver {} diff --git a/src/openzeppelin/tests/test_erc721.cairo b/src/openzeppelin/tests/test_erc721.cairo new file mode 100644 index 000000000..3b81360ae --- /dev/null +++ b/src/openzeppelin/tests/test_erc721.cairo @@ -0,0 +1,791 @@ +use openzeppelin::introspection::erc165; +use openzeppelin::token::erc721; +use openzeppelin::token::erc721::ERC721; +use openzeppelin::account::Account; + +use openzeppelin::tests::utils; +use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; +use openzeppelin::tests::mocks::erc721_receiver::ERC721NonReceiver; +use openzeppelin::tests::mocks::erc721_receiver::SUCCESS; +use openzeppelin::tests::mocks::erc721_receiver::FAILURE; + +use starknet::contract_address_const; +use starknet::ContractAddress; +use starknet::testing::set_caller_address; +use integer::u256; +use integer::u256_from_felt252; +use array::ArrayTrait; +use traits::Into; +use zeroable::Zeroable; + +const NAME: felt252 = 111; +const SYMBOL: felt252 = 222; +const URI: felt252 = 333; + +fn TOKEN_ID() -> u256 { + 7.into() +} + +fn ZERO() -> ContractAddress { + Zeroable::zero() +} +fn OWNER() -> ContractAddress { + contract_address_const::<10>() +} +fn RECIPIENT() -> ContractAddress { + contract_address_const::<20>() +} +fn SPENDER() -> ContractAddress { + contract_address_const::<30>() +} +fn OPERATOR() -> ContractAddress { + contract_address_const::<40>() +} +fn OTHER() -> ContractAddress { + contract_address_const::<50>() +} + +fn DATA(success: bool) -> Span { + let mut data = ArrayTrait::new(); + if success { + data.append(SUCCESS); + } else { + data.append(FAILURE); + } + data.span() +} + +/// +/// Setup +/// + +fn setup() { + ERC721::initializer(NAME, SYMBOL); + ERC721::_mint(OWNER(), TOKEN_ID()); +} + +fn setup_receiver() -> ContractAddress { + utils::deploy(ERC721Receiver::TEST_CLASS_HASH, ArrayTrait::new()) +} + +fn setup_account() -> ContractAddress { + let mut calldata = ArrayTrait::new(); + let public_key: felt252 = 1234678; + calldata.append(public_key); + utils::deploy(Account::TEST_CLASS_HASH, calldata) +} + +/// +/// Initializers +/// + +#[test] +#[available_gas(2000000)] +fn test_constructor() { + ERC721::constructor(NAME, SYMBOL); + + assert(ERC721::name() == NAME, 'Name should be NAME'); + assert(ERC721::symbol() == SYMBOL, 'Symbol should be SYMBOL'); + assert(ERC721::balance_of(OWNER()) == 0.into(), 'Balance should be zero'); + + assert(ERC721::supports_interface(erc721::interface::IERC721_ID), 'Missing interface ID'); + assert( + ERC721::supports_interface(erc721::interface::IERC721_METADATA_ID), 'missing interface ID' + ); + assert(ERC721::supports_interface(erc165::IERC165_ID), 'missing interface ID'); + assert(!ERC721::supports_interface(erc165::INVALID_ID), 'invalid interface ID'); +} + +#[test] +#[available_gas(2000000)] +fn test_initialize() { + ERC721::initializer(NAME, SYMBOL); + + assert(ERC721::name() == NAME, 'Name should be NAME'); + assert(ERC721::symbol() == SYMBOL, 'Symbol should be SYMBOL'); + assert(ERC721::balance_of(OWNER()) == 0.into(), 'Balance should be zero'); + + assert(ERC721::supports_interface(erc721::interface::IERC721_ID), 'Missing interface ID'); + assert( + ERC721::supports_interface(erc721::interface::IERC721_METADATA_ID), 'missing interface ID' + ); + assert(ERC721::supports_interface(erc165::IERC165_ID), 'missing interface ID'); + assert(!ERC721::supports_interface(erc165::INVALID_ID), 'invalid interface ID'); +} + +/// +/// Getters +/// + +#[test] +#[available_gas(2000000)] +fn test_balance_of() { + setup(); + assert(ERC721::balance_of(OWNER()) == 1.into(), 'Should return balance'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: invalid account', ))] +fn test_balance_of_zero() { + ERC721::balance_of(ZERO()); +} + +#[test] +#[available_gas(2000000)] +fn test_owner_of() { + setup(); + assert(ERC721::owner_of(TOKEN_ID()) == OWNER(), 'Should return owner'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: invalid token ID', ))] +fn test_owner_of_non_minted() { + ERC721::owner_of(u256_from_felt252(7)); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: invalid token ID', ))] +fn test_token_uri_non_minted() { + ERC721::token_uri(u256_from_felt252(7)); +} + +#[test] +#[available_gas(2000000)] +fn test_get_approved() { + setup(); + let spender = SPENDER(); + let token_id = TOKEN_ID(); + + assert(ERC721::get_approved(token_id) == ZERO(), 'Should return non-approval'); + ERC721::_approve(spender, token_id); + assert(ERC721::get_approved(token_id) == spender, 'Should return approval'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: invalid token ID', ))] +fn test_get_approved_nonexistent() { + ERC721::get_approved(u256_from_felt252(7)); +} + +#[test] +#[available_gas(2000000)] +fn test__exists() { + let zero = ZERO(); + let token_id = TOKEN_ID(); + assert(!ERC721::_exists(token_id), 'Token should not exist'); + assert(ERC721::_owners::read(token_id) == zero, 'Invalid owner'); + + ERC721::_mint(RECIPIENT(), token_id); + + assert(ERC721::_exists(token_id), 'Token should exist'); + assert(ERC721::_owners::read(token_id) == RECIPIENT(), 'Invalid owner'); + + ERC721::_burn(token_id); + + assert(!ERC721::_exists(token_id), 'Token should not exist'); + assert(ERC721::_owners::read(token_id) == zero, 'Invalid owner'); +} + +/// +/// approve & _approve +/// + +#[test] +#[available_gas(2000000)] +fn test_approve_from_owner() { + setup(); + + set_caller_address(OWNER()); + ERC721::approve(SPENDER(), TOKEN_ID()); + assert(ERC721::get_approved(TOKEN_ID()) == SPENDER(), 'Spender not approved correctly'); +} + +#[test] +#[available_gas(2000000)] +fn test_approve_from_operator() { + setup(); + + set_caller_address(OWNER()); + ERC721::set_approval_for_all(OPERATOR(), true); + + set_caller_address(OPERATOR()); + ERC721::approve(SPENDER(), TOKEN_ID()); + assert(ERC721::get_approved(TOKEN_ID()) == SPENDER(), 'Spender not approved correctly'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: unauthorized caller', ))] +fn test_approve_from_unauthorized() { + setup(); + + set_caller_address(OTHER()); + ERC721::approve(SPENDER(), TOKEN_ID()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: approval to owner', ))] +fn test_approve_to_owner() { + setup(); + + set_caller_address(OWNER()); + ERC721::approve(OWNER(), TOKEN_ID()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: invalid token ID', ))] +fn test_approve_nonexistent() { + ERC721::approve(SPENDER(), TOKEN_ID()); +} + +#[test] +#[available_gas(2000000)] +fn test__approve() { + setup(); + + ERC721::_approve(SPENDER(), TOKEN_ID()); + assert(ERC721::get_approved(TOKEN_ID()) == SPENDER(), 'Spender not approved correctly'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: approval to owner', ))] +fn test__approve_to_owner() { + setup(); + + ERC721::_approve(OWNER(), TOKEN_ID()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: invalid token ID', ))] +fn test__approve_nonexistent() { + ERC721::_approve(SPENDER(), TOKEN_ID()); +} + +/// +/// set_approval_for_all & _set_approval_for_all +/// + +#[test] +#[available_gas(2000000)] +fn test_set_approval_for_all() { + set_caller_address(OWNER()); + assert(!ERC721::is_approved_for_all(OWNER(), OPERATOR()), 'Invalid default value'); + + ERC721::set_approval_for_all(OPERATOR(), true); + assert(ERC721::is_approved_for_all(OWNER(), OPERATOR()), 'Operator not approved correctly'); + + ERC721::set_approval_for_all(OPERATOR(), false); + assert(!ERC721::is_approved_for_all(OWNER(), OPERATOR()), 'Approval not revoked correctly'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: self approval', ))] +fn test_set_approval_for_all_owner_equal_operator_true() { + set_caller_address(OWNER()); + ERC721::set_approval_for_all(OWNER(), true); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: self approval', ))] +fn test_set_approval_for_all_owner_equal_operator_false() { + set_caller_address(OWNER()); + ERC721::set_approval_for_all(OWNER(), false); +} + +#[test] +#[available_gas(2000000)] +fn test__set_approval_for_all() { + assert(!ERC721::is_approved_for_all(OWNER(), OPERATOR()), 'Invalid default value'); + + ERC721::_set_approval_for_all(OWNER(), OPERATOR(), true); + assert(ERC721::is_approved_for_all(OWNER(), OPERATOR()), 'Operator not approved correctly'); + + ERC721::_set_approval_for_all(OWNER(), OPERATOR(), false); + assert(!ERC721::is_approved_for_all(OWNER(), OPERATOR()), 'Operator not approved correctly'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: self approval', ))] +fn test__set_approval_for_all_owner_equal_operator_true() { + ERC721::_set_approval_for_all(OWNER(), OWNER(), true); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: self approval', ))] +fn test__set_approval_for_all_owner_equal_operator_false() { + ERC721::_set_approval_for_all(OWNER(), OWNER(), false); +} + +/// +/// transfer_from +/// + +#[test] +#[available_gas(2000000)] +fn test_transfer_from_owner() { + setup(); + let token_id = TOKEN_ID(); + let owner = OWNER(); + let recipient = RECIPIENT(); + // set approval to check reset + ERC721::_approve(OTHER(), token_id); + + assert_state_before_transfer(token_id, owner, recipient); + assert(ERC721::get_approved(token_id) == OTHER(), 'Approval not implicitly reset'); + + set_caller_address(owner); + ERC721::transfer_from(owner, recipient, token_id); + + assert_state_after_transfer(token_id, owner, recipient); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: invalid token ID', ))] +fn test_transfer_from_nonexistent() { + ERC721::transfer_from(ZERO(), RECIPIENT(), TOKEN_ID()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: invalid receiver', ))] +fn test_transfer_from_to_zero() { + setup(); + + set_caller_address(OWNER()); + ERC721::transfer_from(OWNER(), ZERO(), TOKEN_ID()); +} + +#[test] +#[available_gas(2000000)] +fn test_transfer_from_to_owner() { + setup(); + + assert(ERC721::owner_of(TOKEN_ID()) == OWNER(), 'Ownership before'); + assert(ERC721::balance_of(OWNER()) == 1.into(), 'Balance of owner before'); + + set_caller_address(OWNER()); + ERC721::transfer_from(OWNER(), OWNER(), TOKEN_ID()); + + assert(ERC721::owner_of(TOKEN_ID()) == OWNER(), 'Ownership after'); + assert(ERC721::balance_of(OWNER()) == 1.into(), 'Balance of owner after'); +} + +#[test] +#[available_gas(2000000)] +fn test_transfer_from_approved() { + setup(); + let token_id = TOKEN_ID(); + let owner = OWNER(); + let recipient = RECIPIENT(); + assert_state_before_transfer(token_id, owner, recipient); + + set_caller_address(owner); + ERC721::approve(OPERATOR(), token_id); + + set_caller_address(OPERATOR()); + ERC721::transfer_from(owner, recipient, token_id); + + assert_state_after_transfer(token_id, owner, recipient); +} + +#[test] +#[available_gas(2000000)] +fn test_transfer_from_approved_for_all() { + setup(); + let token_id = TOKEN_ID(); + let owner = OWNER(); + let recipient = RECIPIENT(); + + assert_state_before_transfer(token_id, owner, recipient); + + set_caller_address(owner); + ERC721::set_approval_for_all(OPERATOR(), true); + + set_caller_address(OPERATOR()); + ERC721::transfer_from(owner, recipient, token_id); + + assert_state_after_transfer(token_id, owner, recipient); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: unauthorized caller', ))] +fn test_transfer_from_unauthorized() { + setup(); + + set_caller_address(OTHER()); + ERC721::transfer_from(OWNER(), RECIPIENT(), TOKEN_ID()); +} + +// +// safe_transfer_from +// + +#[test] +#[available_gas(2000000)] +fn test_safe_transfer_from_to_account() { + setup(); + let account = setup_account(); + let token_id = TOKEN_ID(); + let owner = OWNER(); + + assert_state_before_transfer(token_id, owner, account); + + set_caller_address(owner); + ERC721::safe_transfer_from(owner, account, token_id, DATA(true)); + + assert_state_after_transfer(token_id, owner, account); +} + +#[test] +#[available_gas(2000000)] +fn test_safe_transfer_from_to_receiver() { + setup(); + let receiver = setup_receiver(); + let token_id = TOKEN_ID(); + let owner = OWNER(); + + assert_state_before_transfer(token_id, owner, receiver); + + set_caller_address(owner); + ERC721::safe_transfer_from(owner, receiver, token_id, DATA(true)); + + assert_state_after_transfer(token_id, owner, receiver); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: safe transfer failed', ))] +fn test_safe_transfer_from_to_receiver_failure() { + setup(); + let receiver = setup_receiver(); + let token_id = TOKEN_ID(); + let owner = OWNER(); + + set_caller_address(owner); + ERC721::safe_transfer_from(owner, receiver, token_id, DATA(false)); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_safe_transfer_from_to_non_receiver() { + setup(); + let recipient = utils::deploy(ERC721NonReceiver::TEST_CLASS_HASH, ArrayTrait::new()); + let token_id = TOKEN_ID(); + let owner = OWNER(); + + set_caller_address(owner); + ERC721::safe_transfer_from(owner, recipient, token_id, DATA(true)); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: invalid token ID', ))] +fn test_safe_transfer_from_nonexistent() { + ERC721::safe_transfer_from(ZERO(), RECIPIENT(), TOKEN_ID(), DATA(true)); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: invalid receiver', ))] +fn test_safe_transfer_from_to_zero() { + setup(); + + set_caller_address(OWNER()); + ERC721::safe_transfer_from(OWNER(), ZERO(), TOKEN_ID(), DATA(true)); +} + +#[test] +#[available_gas(2000000)] +fn test_safe_transfer_from_to_owner() { + let token_id = TOKEN_ID(); + let owner = setup_receiver(); + ERC721::initializer(NAME, SYMBOL); + ERC721::_mint(owner, token_id); + + assert(ERC721::owner_of(token_id) == owner, 'Ownership before'); + assert(ERC721::balance_of(owner) == 1.into(), 'Balance of owner before'); + + set_caller_address(owner); + ERC721::safe_transfer_from(owner, owner, token_id, DATA(true)); + + assert(ERC721::owner_of(token_id) == owner, 'Ownership after'); + assert(ERC721::balance_of(owner) == 1.into(), 'Balance of owner after'); +} + +#[test] +#[available_gas(2000000)] +fn test_safe_transfer_from_approved() { + setup(); + let receiver = setup_receiver(); + let token_id = TOKEN_ID(); + let owner = OWNER(); + + assert_state_before_transfer(token_id, owner, receiver); + + set_caller_address(owner); + ERC721::approve(OPERATOR(), token_id); + + set_caller_address(OPERATOR()); + ERC721::safe_transfer_from(owner, receiver, token_id, DATA(true)); + + assert_state_after_transfer(token_id, owner, receiver); +} + +#[test] +#[available_gas(2000000)] +fn test_safe_transfer_from_approved_for_all() { + setup(); + let receiver = setup_receiver(); + let token_id = TOKEN_ID(); + let owner = OWNER(); + + assert_state_before_transfer(token_id, owner, receiver); + + set_caller_address(owner); + ERC721::set_approval_for_all(OPERATOR(), true); + + set_caller_address(OPERATOR()); + ERC721::safe_transfer_from(owner, receiver, token_id, DATA(true)); + + assert_state_after_transfer(token_id, owner, receiver); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: unauthorized caller', ))] +fn test_safe_transfer_from_unauthorized() { + setup(); + set_caller_address(OTHER()); + ERC721::safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID(), DATA(true)); +} + +// +// __transfer +// + +#[test] +#[available_gas(2000000)] +fn test__transfer() { + setup(); + let token_id = TOKEN_ID(); + let owner = OWNER(); + let recipient = RECIPIENT(); + + assert_state_before_transfer(token_id, owner, recipient); + ERC721::_transfer(owner, recipient, token_id); + assert_state_after_transfer(token_id, owner, recipient); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: invalid token ID', ))] +fn test__transfer_nonexistent() { + ERC721::_transfer(ZERO(), RECIPIENT(), TOKEN_ID()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: invalid receiver', ))] +fn test__transfer_to_zero() { + setup(); + + ERC721::_transfer(OWNER(), ZERO(), TOKEN_ID()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: wrong sender', ))] +fn test__transfer_from_invalid_owner() { + setup(); + + ERC721::_transfer(RECIPIENT(), OWNER(), TOKEN_ID()); +} + +/// +/// Mint +/// + +#[test] +#[available_gas(2000000)] +fn test__mint() { + let recipient = RECIPIENT(); + let token_id = TOKEN_ID(); + assert_state_before_mint(recipient); + ERC721::_mint(RECIPIENT(), TOKEN_ID()); + assert_state_after_mint(token_id, recipient); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: invalid receiver', ))] +fn test__mint_to_zero() { + ERC721::_mint(ZERO(), TOKEN_ID()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: token already minted', ))] +fn test__mint_already_exist() { + setup(); + + ERC721::_mint(RECIPIENT(), TOKEN_ID()); +} + +/// +/// _safe_mint +/// + +#[test] +#[available_gas(2000000)] +fn test__safe_mint_to_receiver() { + let recipient = setup_receiver(); + let token_id = TOKEN_ID(); + + assert_state_before_mint(recipient); + ERC721::_safe_mint(recipient, token_id, DATA(true)); + assert_state_after_mint(token_id, recipient); +} + +#[test] +#[available_gas(2000000)] +fn test__safe_mint_to_account() { + let account = setup_account(); + let token_id = TOKEN_ID(); + + assert_state_before_mint(account); + ERC721::_safe_mint(account, token_id, DATA(true)); + assert_state_after_mint(token_id, account); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test__safe_mint_to_non_receiver() { + let recipient = utils::deploy(ERC721NonReceiver::TEST_CLASS_HASH, ArrayTrait::new()); + let token_id = TOKEN_ID(); + + assert_state_before_mint(recipient); + ERC721::_safe_mint(recipient, token_id, DATA(true)); + assert_state_after_mint(token_id, recipient); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: safe mint failed', ))] +fn test__safe_mint_to_receiver_failure() { + let recipient = setup_receiver(); + let token_id = TOKEN_ID(); + + assert_state_before_mint(recipient); + ERC721::_safe_mint(recipient, token_id, DATA(false)); + assert_state_after_mint(token_id, recipient); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: invalid receiver', ))] +fn test__safe_mint_to_zero() { + ERC721::_safe_mint(ZERO(), TOKEN_ID(), DATA(true)); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: token already minted', ))] +fn test__safe_mint_already_exist() { + setup(); + ERC721::_safe_mint(RECIPIENT(), TOKEN_ID(), DATA(true)); +} + +/// +/// Burn +/// + +#[test] +#[available_gas(2000000)] +fn test__burn() { + setup(); + + ERC721::_approve(OTHER(), TOKEN_ID()); + + assert(ERC721::owner_of(TOKEN_ID()) == OWNER(), 'Ownership before'); + assert(ERC721::balance_of(OWNER()) == 1.into(), 'Balance of owner before'); + assert(ERC721::get_approved(TOKEN_ID()) == OTHER(), 'Approval before'); + + ERC721::_burn(TOKEN_ID()); + + assert(ERC721::_owners::read(TOKEN_ID()) == ZERO(), 'Ownership after'); + assert(ERC721::balance_of(OWNER()) == 0.into(), 'Balance of owner after'); + assert(ERC721::_token_approvals::read(TOKEN_ID()) == ZERO(), 'Approval after'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: invalid token ID', ))] +fn test__burn_nonexistent() { + ERC721::_burn(TOKEN_ID()); +} + +/// +/// _set_token_uri +/// + +#[test] +#[available_gas(2000000)] +fn test__set_token_uri() { + setup(); + + assert(ERC721::token_uri(TOKEN_ID()) == 0, 'URI should be 0'); + ERC721::_set_token_uri(TOKEN_ID(), URI); + assert(ERC721::token_uri(TOKEN_ID()) == URI, 'URI should be set'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: invalid token ID', ))] +fn test__set_token_uri_nonexistent() { + ERC721::_set_token_uri(TOKEN_ID(), URI); +} + +// +// Helpers +// + +fn assert_state_before_transfer( + token_id: u256, owner: ContractAddress, recipient: ContractAddress +) { + assert(ERC721::owner_of(token_id) == owner, 'Ownership before'); + assert(ERC721::balance_of(owner) == 1.into(), 'Balance of owner before'); + assert(ERC721::balance_of(recipient) == 0.into(), 'Balance of recipient before'); +} + +fn assert_state_after_transfer(token_id: u256, owner: ContractAddress, recipient: ContractAddress) { + assert(ERC721::owner_of(token_id) == recipient, 'Ownership after'); + assert(ERC721::balance_of(owner) == 0.into(), 'Balance of owner after'); + assert(ERC721::balance_of(recipient) == 1.into(), 'Balance of recipient after'); + assert(ERC721::get_approved(token_id) == ZERO(), 'Approval not implicitly reset'); +} + +fn assert_state_before_mint(recipient: ContractAddress) { + assert(ERC721::balance_of(recipient) == 0.into(), 'Balance of recipient before'); +} + +fn assert_state_after_mint(token_id: u256, recipient: ContractAddress) { + assert(ERC721::owner_of(token_id) == recipient, 'Ownership after'); + assert(ERC721::balance_of(recipient) == 1.into(), 'Balance of recipient after'); + assert(ERC721::get_approved(token_id) == ZERO(), 'Approval implicitly set'); +} diff --git a/src/openzeppelin/token.cairo b/src/openzeppelin/token.cairo index bfe4665e0..f9a848d01 100644 --- a/src/openzeppelin/token.cairo +++ b/src/openzeppelin/token.cairo @@ -1 +1,2 @@ mod erc20; +mod erc721; diff --git a/src/openzeppelin/token/erc721.cairo b/src/openzeppelin/token/erc721.cairo new file mode 100644 index 000000000..2dde68df8 --- /dev/null +++ b/src/openzeppelin/token/erc721.cairo @@ -0,0 +1,3 @@ +mod erc721; +use erc721::ERC721; +mod interface; diff --git a/src/openzeppelin/token/erc721/erc721.cairo b/src/openzeppelin/token/erc721/erc721.cairo new file mode 100644 index 000000000..e22466078 --- /dev/null +++ b/src/openzeppelin/token/erc721/erc721.cairo @@ -0,0 +1,462 @@ +use starknet::ContractAddress; + +#[abi] +trait ERC721ABI { + // case agnostic + #[view] + fn name() -> felt252; + #[view] + fn symbol() -> felt252; + #[external] + fn approve(to: ContractAddress, token_id: u256); + // snake_case + #[view] + fn balance_of(account: ContractAddress) -> u256; + #[view] + fn owner_of(token_id: u256) -> ContractAddress; + #[external] + fn transfer_from(from: ContractAddress, to: ContractAddress, token_id: u256); + #[external] + fn safe_transfer_from( + from: ContractAddress, to: ContractAddress, token_id: u256, data: Span + ); + #[external] + fn set_approval_for_all(operator: ContractAddress, approved: bool); + #[view] + fn get_approved(token_id: u256) -> ContractAddress; + #[view] + fn is_approved_for_all(owner: ContractAddress, operator: ContractAddress) -> bool; + #[view] + fn token_uri(token_id: u256) -> felt252; + // camelCase + #[view] + fn balanceOf(account: ContractAddress) -> u256; + #[view] + fn ownerOf(tokenId: u256) -> ContractAddress; + #[external] + fn transferFrom(from: ContractAddress, to: ContractAddress, tokenId: u256); + #[external] + fn safeTransferFrom( + from: ContractAddress, to: ContractAddress, tokenId: u256, data: Span + ); + #[external] + fn setApprovalForAll(operator: ContractAddress, approved: bool); + #[view] + fn getApproved(tokenId: u256) -> ContractAddress; + #[view] + fn isApprovedForAll(owner: ContractAddress, operator: ContractAddress) -> bool; + #[view] + fn tokenUri(tokenId: u256) -> felt252; +} + +#[contract] +mod ERC721 { + // OZ modules + use openzeppelin::account; + use openzeppelin::introspection::erc165; + use openzeppelin::token::erc721; + + // Dispatchers + use openzeppelin::introspection::erc165::IERC165Dispatcher; + use openzeppelin::introspection::erc165::IERC165DispatcherTrait; + use super::super::interface::IERC721ReceiverABIDispatcher; + use super::super::interface::IERC721ReceiverABIDispatcherTrait; + + // Other + use starknet::ContractAddress; + use starknet::get_caller_address; + use zeroable::Zeroable; + use option::OptionTrait; + use array::SpanTrait; + use traits::Into; + use openzeppelin::utils::serde::SpanSerde; + + struct Storage { + _name: felt252, + _symbol: felt252, + _owners: LegacyMap, + _balances: LegacyMap, + _token_approvals: LegacyMap, + _operator_approvals: LegacyMap<(ContractAddress, ContractAddress), bool>, + _token_uri: LegacyMap, + } + + #[event] + fn Transfer(from: ContractAddress, to: ContractAddress, token_id: u256) {} + + #[event] + fn Approval(owner: ContractAddress, approved: ContractAddress, token_id: u256) {} + + #[event] + fn ApprovalForAll(owner: ContractAddress, operator: ContractAddress, approved: bool) {} + + #[constructor] + fn constructor(name: felt252, symbol: felt252) { + initializer(name, symbol); + } + + impl ERC721Impl of erc721::interface::IERC721 { + fn name() -> felt252 { + _name::read() + } + + fn symbol() -> felt252 { + _symbol::read() + } + + fn token_uri(token_id: u256) -> felt252 { + assert(_exists(token_id), 'ERC721: invalid token ID'); + _token_uri::read(token_id) + } + + fn balance_of(account: ContractAddress) -> u256 { + assert(!account.is_zero(), 'ERC721: invalid account'); + _balances::read(account) + } + + fn owner_of(token_id: u256) -> ContractAddress { + _owner_of(token_id) + } + + fn get_approved(token_id: u256) -> ContractAddress { + assert(_exists(token_id), 'ERC721: invalid token ID'); + _token_approvals::read(token_id) + } + + fn is_approved_for_all(owner: ContractAddress, operator: ContractAddress) -> bool { + _operator_approvals::read((owner, operator)) + } + + fn approve(to: ContractAddress, token_id: u256) { + let owner = _owner_of(token_id); + + let caller = get_caller_address(); + assert( + owner == caller | is_approved_for_all(owner, caller), 'ERC721: unauthorized caller' + ); + _approve(to, token_id); + } + + fn set_approval_for_all(operator: ContractAddress, approved: bool) { + _set_approval_for_all(get_caller_address(), operator, approved) + } + + fn transfer_from(from: ContractAddress, to: ContractAddress, token_id: u256) { + assert( + _is_approved_or_owner(get_caller_address(), token_id), 'ERC721: unauthorized caller' + ); + _transfer(from, to, token_id); + } + + fn safe_transfer_from( + from: ContractAddress, to: ContractAddress, token_id: u256, data: Span + ) { + assert( + _is_approved_or_owner(get_caller_address(), token_id), 'ERC721: unauthorized caller' + ); + _safe_transfer(from, to, token_id, data); + } + } + + impl ERC721CamelImpl of erc721::interface::IERC721Camel { + fn name() -> felt252 { + ERC721Impl::name() + } + + fn symbol() -> felt252 { + ERC721Impl::symbol() + } + + fn tokenUri(tokenId: u256) -> felt252 { + ERC721Impl::token_uri(tokenId) + } + + fn balanceOf(account: ContractAddress) -> u256 { + ERC721Impl::balance_of(account) + } + + fn ownerOf(tokenId: u256) -> ContractAddress { + ERC721Impl::owner_of(tokenId) + } + + fn approve(to: ContractAddress, tokenId: u256) { + ERC721Impl::approve(to, tokenId) + } + + fn getApproved(tokenId: u256) -> ContractAddress { + ERC721Impl::get_approved(tokenId) + } + + fn isApprovedForAll(owner: ContractAddress, operator: ContractAddress) -> bool { + ERC721Impl::is_approved_for_all(owner, operator) + } + + fn setApprovalForAll(operator: ContractAddress, approved: bool) { + ERC721Impl::set_approval_for_all(operator, approved) + } + + fn transferFrom(from: ContractAddress, to: ContractAddress, tokenId: u256) { + ERC721Impl::transfer_from(from, to, tokenId) + } + + fn safeTransferFrom( + from: ContractAddress, to: ContractAddress, tokenId: u256, data: Span + ) { + ERC721Impl::safe_transfer_from(from, to, tokenId, data) + } + } + + // View + + #[view] + fn supports_interface(interface_id: u32) -> bool { + erc165::ERC165::supports_interface(interface_id) + } + + #[view] + fn supportsInterface(interfaceId: u32) -> bool { + erc165::ERC165::supports_interface(interfaceId) + } + + #[view] + fn name() -> felt252 { + ERC721Impl::name() + } + + #[view] + fn symbol() -> felt252 { + ERC721Impl::symbol() + } + + #[view] + fn token_uri(token_id: u256) -> felt252 { + ERC721Impl::token_uri(token_id) + } + + #[view] + fn tokenUri(tokenId: u256) -> felt252 { + ERC721CamelImpl::tokenUri(tokenId) + } + + #[view] + fn balance_of(account: ContractAddress) -> u256 { + ERC721Impl::balance_of(account) + } + + #[view] + fn balanceOf(account: ContractAddress) -> u256 { + ERC721CamelImpl::balanceOf(account) + } + + #[view] + fn owner_of(token_id: u256) -> ContractAddress { + ERC721Impl::owner_of(token_id) + } + + #[view] + fn ownerOf(tokenId: u256) -> ContractAddress { + ERC721CamelImpl::ownerOf(tokenId) + } + + #[view] + fn get_approved(token_id: u256) -> ContractAddress { + ERC721Impl::get_approved(token_id) + } + + #[view] + fn getApproved(tokenId: u256) -> ContractAddress { + ERC721CamelImpl::getApproved(tokenId) + } + + #[view] + fn is_approved_for_all(owner: ContractAddress, operator: ContractAddress) -> bool { + ERC721Impl::is_approved_for_all(owner, operator) + } + + #[view] + fn isApprovedForAll(owner: ContractAddress, operator: ContractAddress) -> bool { + ERC721CamelImpl::isApprovedForAll(owner, operator) + } + + // External + + #[external] + fn approve(to: ContractAddress, token_id: u256) { + ERC721Impl::approve(to, token_id) + } + + #[external] + fn set_approval_for_all(operator: ContractAddress, approved: bool) { + ERC721Impl::set_approval_for_all(operator, approved) + } + + #[external] + fn setApprovalForAll(operator: ContractAddress, approved: bool) { + ERC721CamelImpl::setApprovalForAll(operator, approved) + } + + #[external] + fn transfer_from(from: ContractAddress, to: ContractAddress, token_id: u256) { + ERC721Impl::transfer_from(from, to, token_id) + } + + #[external] + fn transferFrom(from: ContractAddress, to: ContractAddress, tokenId: u256) { + ERC721CamelImpl::transferFrom(from, to, tokenId) + } + + #[external] + fn safe_transfer_from( + from: ContractAddress, to: ContractAddress, token_id: u256, data: Span + ) { + ERC721Impl::safe_transfer_from(from, to, token_id, data) + } + + #[external] + fn safeTransferFrom( + from: ContractAddress, to: ContractAddress, tokenId: u256, data: Span + ) { + ERC721CamelImpl::safeTransferFrom(from, to, tokenId, data) + } + + // Internal + + #[internal] + fn initializer(name_: felt252, symbol_: felt252) { + _name::write(name_); + _symbol::write(symbol_); + erc165::ERC165::register_interface(erc721::interface::IERC721_ID); + erc165::ERC165::register_interface(erc721::interface::IERC721_METADATA_ID); + } + + #[internal] + fn _owner_of(token_id: u256) -> ContractAddress { + let owner = _owners::read(token_id); + match owner.is_zero() { + bool::False(()) => owner, + bool::True(()) => panic_with_felt252('ERC721: invalid token ID') + } + } + + #[internal] + fn _exists(token_id: u256) -> bool { + !_owners::read(token_id).is_zero() + } + + #[internal] + fn _is_approved_or_owner(spender: ContractAddress, token_id: u256) -> bool { + let owner = _owner_of(token_id); + owner == spender | is_approved_for_all(owner, spender) | spender == get_approved(token_id) + } + + #[internal] + fn _approve(to: ContractAddress, token_id: u256) { + let owner = _owner_of(token_id); + assert(owner != to, 'ERC721: approval to owner'); + _token_approvals::write(token_id, to); + Approval(owner, to, token_id); + } + + #[internal] + fn _set_approval_for_all(owner: ContractAddress, operator: ContractAddress, approved: bool) { + assert(owner != operator, 'ERC721: self approval'); + _operator_approvals::write((owner, operator), approved); + ApprovalForAll(owner, operator, approved); + } + + #[internal] + fn _mint(to: ContractAddress, token_id: u256) { + assert(!to.is_zero(), 'ERC721: invalid receiver'); + assert(!_exists(token_id), 'ERC721: token already minted'); + + // Update balances + _balances::write(to, _balances::read(to) + 1.into()); + + // Update token_id owner + _owners::write(token_id, to); + + // Emit event + Transfer(Zeroable::zero(), to, token_id); + } + + #[internal] + fn _transfer(from: ContractAddress, to: ContractAddress, token_id: u256) { + assert(!to.is_zero(), 'ERC721: invalid receiver'); + let owner = _owner_of(token_id); + assert(from == owner, 'ERC721: wrong sender'); + + // Implicit clear approvals, no need to emit an event + _token_approvals::write(token_id, Zeroable::zero()); + + // Update balances + _balances::write(from, _balances::read(from) - 1.into()); + _balances::write(to, _balances::read(to) + 1.into()); + + // Update token_id owner + _owners::write(token_id, to); + + // Emit event + Transfer(from, to, token_id); + } + + #[internal] + fn _burn(token_id: u256) { + let owner = _owner_of(token_id); + + // Implicit clear approvals, no need to emit an event + _token_approvals::write(token_id, Zeroable::zero()); + + // Update balances + _balances::write(owner, _balances::read(owner) - 1.into()); + + // Delete owner + _owners::write(token_id, Zeroable::zero()); + + // Emit event + Transfer(owner, Zeroable::zero(), token_id); + } + + #[internal] + fn _safe_mint(to: ContractAddress, token_id: u256, data: Span) { + _mint(to, token_id); + assert( + _check_on_erc721_received(Zeroable::zero(), to, token_id, data), + 'ERC721: safe mint failed' + ); + } + + #[internal] + fn _safe_transfer( + from: ContractAddress, to: ContractAddress, token_id: u256, data: Span + ) { + _transfer(from, to, token_id); + assert(_check_on_erc721_received(from, to, token_id, data), 'ERC721: safe transfer failed'); + } + + #[internal] + fn _set_token_uri(token_id: u256, token_uri: felt252) { + assert(_exists(token_id), 'ERC721: invalid token ID'); + _token_uri::write(token_id, token_uri) + } + + #[private] + fn _check_on_erc721_received( + from: ContractAddress, to: ContractAddress, token_id: u256, data: Span + ) -> bool { + if (IERC165Dispatcher { + contract_address: to + }.supports_interface(erc721::interface::IERC721_RECEIVER_ID)) { + // todo add casing fallback mechanism + IERC721ReceiverABIDispatcher { + contract_address: to + } + .on_erc721_received( + get_caller_address(), from, token_id, data + ) == erc721::interface::IERC721_RECEIVER_ID + } else { + IERC165Dispatcher { + contract_address: to + }.supports_interface(account::interface::IACCOUNT_ID) + } + } +} diff --git a/src/openzeppelin/token/erc721/interface.cairo b/src/openzeppelin/token/erc721/interface.cairo new file mode 100644 index 000000000..a8b21f60e --- /dev/null +++ b/src/openzeppelin/token/erc721/interface.cairo @@ -0,0 +1,71 @@ +use openzeppelin::utils::serde::SpanSerde; +use starknet::ContractAddress; +use array::SpanTrait; + +const IERC721_ID: u32 = 0x80ac58cd_u32; +const IERC721_METADATA_ID: u32 = 0x5b5e139f_u32; +const IERC721_RECEIVER_ID: u32 = 0x150b7a02_u32; + +#[abi] +trait IERC721 { + fn balance_of(account: ContractAddress) -> u256; + fn owner_of(token_id: u256) -> ContractAddress; + fn transfer_from(from: ContractAddress, to: ContractAddress, token_id: u256); + fn safe_transfer_from( + from: ContractAddress, to: ContractAddress, token_id: u256, data: Span + ); + fn approve(to: ContractAddress, token_id: u256); + fn set_approval_for_all(operator: ContractAddress, approved: bool); + fn get_approved(token_id: u256) -> ContractAddress; + fn is_approved_for_all(owner: ContractAddress, operator: ContractAddress) -> bool; + // IERC721Metadata + fn name() -> felt252; + fn symbol() -> felt252; + fn token_uri(token_id: u256) -> felt252; +} + +#[abi] +trait IERC721Camel { + fn balanceOf(account: ContractAddress) -> u256; + fn ownerOf(tokenId: u256) -> ContractAddress; + fn transferFrom(from: ContractAddress, to: ContractAddress, tokenId: u256); + fn safeTransferFrom( + from: ContractAddress, to: ContractAddress, tokenId: u256, data: Span + ); + fn approve(to: ContractAddress, tokenId: u256); + fn setApprovalForAll(operator: ContractAddress, approved: bool); + fn getApproved(tokenId: u256) -> ContractAddress; + fn isApprovedForAll(owner: ContractAddress, operator: ContractAddress) -> bool; + // IERC721Metadata + fn name() -> felt252; + fn symbol() -> felt252; + fn tokenUri(tokenId: u256) -> felt252; +} + +// +// ERC721Receiver +// + +#[abi] +trait IERC721ReceiverABI { + fn on_erc721_received( + operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span + ) -> u32; + fn onERC721Received( + operator: ContractAddress, from: ContractAddress, tokenId: u256, data: Span + ) -> u32; +} + +#[abi] +trait IERC721Receiver { + fn on_erc721_received( + operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span + ) -> u32; +} + +#[abi] +trait IERC721ReceiverCamel { + fn onERC721Received( + operator: ContractAddress, from: ContractAddress, tokenId: u256, data: Span + ) -> u32; +} diff --git a/src/openzeppelin/utils.cairo b/src/openzeppelin/utils.cairo index c8a47ceb8..8dab269b1 100644 --- a/src/openzeppelin/utils.cairo +++ b/src/openzeppelin/utils.cairo @@ -3,6 +3,7 @@ use array::SpanTrait; use box::BoxTrait; use option::OptionTrait; mod constants; +mod serde; #[inline(always)] fn check_gas() { diff --git a/src/openzeppelin/utils/serde.cairo b/src/openzeppelin/utils/serde.cairo new file mode 100644 index 000000000..68c3720f0 --- /dev/null +++ b/src/openzeppelin/utils/serde.cairo @@ -0,0 +1,19 @@ +use array::ArrayTrait; +use array::SpanTrait; +use serde::Serde; +use serde::serialize_array_helper; +use serde::deserialize_array_helper; + +impl SpanSerde< + T, impl TSerde: Serde, impl TCopy: Copy, impl TDrop: Drop +> of Serde> { + fn serialize(self: @Span, ref output: Array) { + (*self).len().serialize(ref output); + serialize_array_helper(*self, ref output); + } + fn deserialize(ref serialized: Span) -> Option> { + let length = *serialized.pop_front()?; + let mut arr = ArrayTrait::new(); + Option::Some(deserialize_array_helper(ref serialized, arr, length)?.span()) + } +} From 62688e797d20fd3aafc1a86b3b60aa02eab212bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Sun, 2 Jul 2023 19:32:03 -0300 Subject: [PATCH 052/246] Dual interface dispatcher for ERC721 (#623) * add dual interface dispatcher draft * complete dual721 implementation * fix format * add felt252 <> bool casts * simplify Felt252IntoBool * make felt252 into -> try_into * add base test suite * implement most tests * add mocks * add tests for panic cases * fix some tests * fix format * fix more tests * fix tests * apply review feedback * normalize mock names * add token_uri tests * rename non implementing mock * move comment * upgrade to cairo 1.1.1 * Update src/openzeppelin/utils.cairo Co-authored-by: Eric Nordelo * address review comments --------- Co-authored-by: Eric Nordelo --- Cargo.lock | 56 +- Cargo.toml | 2 +- cairo | 2 +- src/openzeppelin/tests.cairo | 1 + src/openzeppelin/tests/mocks.cairo | 4 + .../tests/mocks/camel721_mock.cairo | 80 +++ .../tests/mocks/erc721_panic_mock.cairo | 139 +++++ .../tests/mocks/non_implementing_mock.cairo | 7 + .../tests/mocks/snake721_mock.cairo | 80 +++ src/openzeppelin/tests/test_account.cairo | 2 +- src/openzeppelin/tests/test_dual721.cairo | 525 ++++++++++++++++++ src/openzeppelin/tests/utils.cairo | 7 +- src/openzeppelin/token/erc721.cairo | 1 + src/openzeppelin/token/erc721/dual721.cairo | 198 +++++++ src/openzeppelin/utils.cairo | 43 +- src/openzeppelin/utils/selectors.cairo | 27 + 16 files changed, 1140 insertions(+), 34 deletions(-) create mode 100644 src/openzeppelin/tests/mocks/camel721_mock.cairo create mode 100644 src/openzeppelin/tests/mocks/erc721_panic_mock.cairo create mode 100644 src/openzeppelin/tests/mocks/non_implementing_mock.cairo create mode 100644 src/openzeppelin/tests/mocks/snake721_mock.cairo create mode 100644 src/openzeppelin/tests/test_dual721.cairo create mode 100644 src/openzeppelin/token/erc721/dual721.cairo create mode 100644 src/openzeppelin/utils/selectors.cairo diff --git a/Cargo.lock b/Cargo.lock index 85aa935ae..a5ca15c63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -301,7 +301,7 @@ dependencies = [ [[package]] name = "cairo-lang-casm" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "cairo-lang-utils", "env_logger", @@ -318,7 +318,7 @@ dependencies = [ [[package]] name = "cairo-lang-compiler" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "anyhow", "cairo-lang-defs", @@ -343,7 +343,7 @@ dependencies = [ [[package]] name = "cairo-lang-debug" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "cairo-lang-proc-macros", "cairo-lang-utils", @@ -354,7 +354,7 @@ dependencies = [ [[package]] name = "cairo-lang-defs" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "cairo-lang-debug", "cairo-lang-diagnostics", @@ -375,7 +375,7 @@ dependencies = [ [[package]] name = "cairo-lang-diagnostics" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "cairo-lang-filesystem", "cairo-lang-proc-macros", @@ -390,7 +390,7 @@ dependencies = [ [[package]] name = "cairo-lang-eq-solver" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "cairo-lang-utils", "env_logger", @@ -404,7 +404,7 @@ dependencies = [ [[package]] name = "cairo-lang-filesystem" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "cairo-lang-debug", "cairo-lang-utils", @@ -419,7 +419,7 @@ dependencies = [ [[package]] name = "cairo-lang-formatter" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "anyhow", "cairo-lang-diagnostics", @@ -442,7 +442,7 @@ dependencies = [ [[package]] name = "cairo-lang-language-server" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "anyhow", "cairo-lang-compiler", @@ -474,7 +474,7 @@ dependencies = [ [[package]] name = "cairo-lang-lowering" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -503,7 +503,7 @@ dependencies = [ [[package]] name = "cairo-lang-parser" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "cairo-lang-diagnostics", "cairo-lang-filesystem", @@ -527,7 +527,7 @@ dependencies = [ [[package]] name = "cairo-lang-plugins" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -550,7 +550,7 @@ dependencies = [ [[package]] name = "cairo-lang-proc-macros" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "cairo-lang-debug", "quote", @@ -559,7 +559,7 @@ dependencies = [ [[package]] name = "cairo-lang-project" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "cairo-lang-filesystem", "indoc", @@ -572,7 +572,7 @@ dependencies = [ [[package]] name = "cairo-lang-runner" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "anyhow", "ark-ff 0.4.0-alpha.7", @@ -607,7 +607,7 @@ dependencies = [ [[package]] name = "cairo-lang-semantic" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "assert_matches", "cairo-lang-debug", @@ -636,7 +636,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "assert_matches", "bimap", @@ -664,7 +664,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-ap-change" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", @@ -679,7 +679,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-gas" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", @@ -696,7 +696,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-generator" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -727,7 +727,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-to-casm" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "anyhow", "assert_matches", @@ -752,7 +752,7 @@ dependencies = [ [[package]] name = "cairo-lang-starknet" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "anyhow", "cairo-felt", @@ -796,7 +796,7 @@ dependencies = [ [[package]] name = "cairo-lang-syntax" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "cairo-lang-debug", "cairo-lang-filesystem", @@ -814,7 +814,7 @@ dependencies = [ [[package]] name = "cairo-lang-syntax-codegen" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "cairo-lang-utils", "env_logger", @@ -826,7 +826,7 @@ dependencies = [ [[package]] name = "cairo-lang-test-runner" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "anyhow", "cairo-felt", @@ -860,7 +860,7 @@ dependencies = [ [[package]] name = "cairo-lang-test-utils" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "cairo-lang-utils", "env_logger", @@ -871,7 +871,7 @@ dependencies = [ [[package]] name = "cairo-lang-utils" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "env_logger", "indexmap", @@ -2761,7 +2761,7 @@ dependencies = [ [[package]] name = "tests" -version = "1.0.0-rc0" +version = "1.1.1" dependencies = [ "assert_matches", "cairo-felt", diff --git a/Cargo.toml b/Cargo.toml index 49e71907f..3927b6991 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ members = [ ] [workspace.package] -version = "1.0.0-rc0" +version = "1.1.1" edition = "2021" repository = "https://github.com/starkware-libs/cairo/" license = "Apache-2.0" diff --git a/cairo b/cairo index ee9a0bb1e..c6b003cc4 160000 --- a/cairo +++ b/cairo @@ -1 +1 @@ -Subproject commit ee9a0bb1e496c6e5d922c3a5071d85b45f8bc268 +Subproject commit c6b003cc425907ac5fc846375a48737d5125f5b5 diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index 5aa74d96f..fd9d9d0c0 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -5,6 +5,7 @@ mod test_erc165; mod test_account; mod test_erc20; mod test_erc721; +mod test_dual721; mod test_initializable; mod test_pausable; mod mocks; diff --git a/src/openzeppelin/tests/mocks.cairo b/src/openzeppelin/tests/mocks.cairo index b8f645b23..4ab029428 100644 --- a/src/openzeppelin/tests/mocks.cairo +++ b/src/openzeppelin/tests/mocks.cairo @@ -1,4 +1,8 @@ mod reentrancy_attacker_mock; mod reentrancy_mock; mod erc721_receiver; +mod erc721_panic_mock; mod mock_pausable; +mod snake721_mock; +mod camel721_mock; +mod non_implementing_mock; diff --git a/src/openzeppelin/tests/mocks/camel721_mock.cairo b/src/openzeppelin/tests/mocks/camel721_mock.cairo new file mode 100644 index 000000000..2b2715ec9 --- /dev/null +++ b/src/openzeppelin/tests/mocks/camel721_mock.cairo @@ -0,0 +1,80 @@ +#[contract] +mod CamelERC721Mock { + use starknet::ContractAddress; + use starknet::get_caller_address; + use openzeppelin::token::erc721::ERC721; + use openzeppelin::utils::serde::SpanSerde; + + #[constructor] + fn constructor(name: felt252, symbol: felt252, tokenId: u256, uri: felt252) { + ERC721::initializer(name, symbol); + ERC721::_mint(get_caller_address(), tokenId); + ERC721::_set_token_uri(tokenId, uri); + } + + // View + + #[view] + fn supportsInterface(interfaceId: u32) -> bool { + ERC721::supports_interface(interfaceId) + } + + #[view] + fn name() -> felt252 { + ERC721::name() + } + + #[view] + fn symbol() -> felt252 { + ERC721::symbol() + } + + #[view] + fn tokenUri(tokenId: u256) -> felt252 { + ERC721::tokenUri(tokenId) + } + + #[view] + fn balanceOf(account: ContractAddress) -> u256 { + ERC721::balanceOf(account) + } + + #[view] + fn ownerOf(tokenId: u256) -> ContractAddress { + ERC721::ownerOf(tokenId) + } + + #[view] + fn getApproved(tokenId: u256) -> ContractAddress { + ERC721::getApproved(tokenId) + } + + #[view] + fn isApprovedForAll(owner: ContractAddress, operator: ContractAddress) -> bool { + ERC721::isApprovedForAll(owner, operator) + } + + // External + + #[external] + fn approve(to: ContractAddress, tokenId: u256) { + ERC721::approve(to, tokenId) + } + + #[external] + fn setApprovalForAll(operator: ContractAddress, approved: bool) { + ERC721::setApprovalForAll(operator, approved) + } + + #[external] + fn transferFrom(from: ContractAddress, to: ContractAddress, tokenId: u256) { + ERC721::transferFrom(from, to, tokenId) + } + + #[external] + fn safeTransferFrom( + from: ContractAddress, to: ContractAddress, tokenId: u256, data: Span + ) { + ERC721::safeTransferFrom(from, to, tokenId, data) + } +} diff --git a/src/openzeppelin/tests/mocks/erc721_panic_mock.cairo b/src/openzeppelin/tests/mocks/erc721_panic_mock.cairo new file mode 100644 index 000000000..d7f1702dd --- /dev/null +++ b/src/openzeppelin/tests/mocks/erc721_panic_mock.cairo @@ -0,0 +1,139 @@ +// Although these modules are designed to panic, functions +// still need a valid return value. We chose: +// +// 3 for felt252 +// zero for ContractAddress +// u256 { 3, 3 } for u256 + +#[contract] +mod SnakeERC721PanicMock { + use openzeppelin::utils::serde::SpanSerde; + use starknet::ContractAddress; + use zeroable::Zeroable; + + // + // agnostic + // + + #[view] + fn name() -> felt252 { + panic_with_felt252('Some error'); + 3 + } + + #[view] + fn symbol() -> felt252 { + panic_with_felt252('Some error'); + 3 + } + + #[external] + fn approve(to: ContractAddress, token_id: u256) { + panic_with_felt252('Some error'); + } + + // + // snake + // + + #[view] + fn token_uri(token_id: u256) -> felt252 { + panic_with_felt252('Some error'); + 3 + } + + #[view] + fn balance_of(account: ContractAddress) -> u256 { + panic_with_felt252('Some error'); + u256 { low: 3, high: 3 } + } + + #[view] + fn owner_of(token_id: u256) -> ContractAddress { + panic_with_felt252('Some error'); + Zeroable::zero() + } + + #[view] + fn get_approved(token_id: u256) -> ContractAddress { + panic_with_felt252('Some error'); + Zeroable::zero() + } + + #[view] + fn is_approved_for_all(owner: ContractAddress, operator: ContractAddress) -> bool { + panic_with_felt252('Some error'); + false + } + + #[external] + fn set_approval_for_all(operator: ContractAddress, approved: bool) { + panic_with_felt252('Some error'); + } + + #[external] + fn transfer_from(from: ContractAddress, to: ContractAddress, token_id: u256) { + panic_with_felt252('Some error'); + } + + #[external] + fn safe_transfer_from( + from: ContractAddress, to: ContractAddress, token_id: u256, data: Span + ) { + panic_with_felt252('Some error'); + } +} + +#[contract] +mod CamelERC721PanicMock { + use openzeppelin::utils::serde::SpanSerde; + use starknet::ContractAddress; + use zeroable::Zeroable; + + #[view] + fn tokenUri(tokenId: u256) -> felt252 { + panic_with_felt252('Some error'); + 3 + } + + #[view] + fn balanceOf(account: ContractAddress) -> u256 { + panic_with_felt252('Some error'); + u256 { low: 3, high: 3 } + } + + #[view] + fn ownerOf(tokenId: u256) -> ContractAddress { + panic_with_felt252('Some error'); + Zeroable::zero() + } + + #[view] + fn getApproved(tokenId: u256) -> ContractAddress { + panic_with_felt252('Some error'); + Zeroable::zero() + } + + #[view] + fn isApprovedForAll(owner: ContractAddress, operator: ContractAddress) -> bool { + panic_with_felt252('Some error'); + false + } + + #[external] + fn setApprovalForAll(operator: ContractAddress, approved: bool) { + panic_with_felt252('Some error'); + } + + #[external] + fn transferFrom(from: ContractAddress, to: ContractAddress, tokenId: u256) { + panic_with_felt252('Some error'); + } + + #[external] + fn safeTransferFrom( + from: ContractAddress, to: ContractAddress, tokenId: u256, data: Span + ) { + panic_with_felt252('Some error'); + } +} diff --git a/src/openzeppelin/tests/mocks/non_implementing_mock.cairo b/src/openzeppelin/tests/mocks/non_implementing_mock.cairo new file mode 100644 index 000000000..d614d1da7 --- /dev/null +++ b/src/openzeppelin/tests/mocks/non_implementing_mock.cairo @@ -0,0 +1,7 @@ +#[contract] +mod NonImplementingMock { + #[view] + fn nope() -> bool { + false + } +} diff --git a/src/openzeppelin/tests/mocks/snake721_mock.cairo b/src/openzeppelin/tests/mocks/snake721_mock.cairo new file mode 100644 index 000000000..44f4e91d9 --- /dev/null +++ b/src/openzeppelin/tests/mocks/snake721_mock.cairo @@ -0,0 +1,80 @@ +#[contract] +mod SnakeERC721Mock { + use starknet::ContractAddress; + use starknet::get_caller_address; + use openzeppelin::token::erc721::ERC721; + use openzeppelin::utils::serde::SpanSerde; + + #[constructor] + fn constructor(name: felt252, symbol: felt252, token_id: u256, uri: felt252) { + ERC721::initializer(name, symbol); + ERC721::_mint(get_caller_address(), token_id); + ERC721::_set_token_uri(token_id, uri); + } + + // View + + #[view] + fn supports_interface(interface_id: u32) -> bool { + ERC721::supports_interface(interface_id) + } + + #[view] + fn name() -> felt252 { + ERC721::name() + } + + #[view] + fn symbol() -> felt252 { + ERC721::symbol() + } + + #[view] + fn token_uri(token_id: u256) -> felt252 { + ERC721::token_uri(token_id) + } + + #[view] + fn balance_of(account: ContractAddress) -> u256 { + ERC721::balance_of(account) + } + + #[view] + fn owner_of(token_id: u256) -> ContractAddress { + ERC721::owner_of(token_id) + } + + #[view] + fn get_approved(token_id: u256) -> ContractAddress { + ERC721::get_approved(token_id) + } + + #[view] + fn is_approved_for_all(owner: ContractAddress, operator: ContractAddress) -> bool { + ERC721::is_approved_for_all(owner, operator) + } + + // External + + #[external] + fn approve(to: ContractAddress, token_id: u256) { + ERC721::approve(to, token_id) + } + + #[external] + fn set_approval_for_all(operator: ContractAddress, approved: bool) { + ERC721::set_approval_for_all(operator, approved) + } + + #[external] + fn transfer_from(from: ContractAddress, to: ContractAddress, token_id: u256) { + ERC721::transfer_from(from, to, token_id) + } + + #[external] + fn safe_transfer_from( + from: ContractAddress, to: ContractAddress, token_id: u256, data: Span + ) { + ERC721::safe_transfer_from(from, to, token_id, data) + } +} diff --git a/src/openzeppelin/tests/test_account.cairo b/src/openzeppelin/tests/test_account.cairo index d53ba3259..210e91f50 100644 --- a/src/openzeppelin/tests/test_account.cairo +++ b/src/openzeppelin/tests/test_account.cairo @@ -79,7 +79,7 @@ fn setup_dispatcher(data: Option<@SignedTransactionData>) -> AccountABIDispatche fn deploy_erc20(recipient: ContractAddress, initial_supply: u256) -> IERC20Dispatcher { let name = 0; let symbol = 0; - let mut calldata = ArrayTrait::::new(); + let mut calldata = ArrayTrait::new(); calldata.append(name); calldata.append(symbol); diff --git a/src/openzeppelin/tests/test_dual721.cairo b/src/openzeppelin/tests/test_dual721.cairo new file mode 100644 index 000000000..98d7459f9 --- /dev/null +++ b/src/openzeppelin/tests/test_dual721.cairo @@ -0,0 +1,525 @@ +use traits::Into; +use array::ArrayTrait; +use starknet::ContractAddress; +use starknet::contract_address_const; +use starknet::testing::set_caller_address; +use starknet::testing::set_contract_address; +use openzeppelin::token::erc721::interface::IERC721Dispatcher; +use openzeppelin::token::erc721::interface::IERC721CamelDispatcher; +use openzeppelin::token::erc721::interface::IERC721DispatcherTrait; +use openzeppelin::token::erc721::interface::IERC721CamelDispatcherTrait; +use openzeppelin::token::erc721::dual721::DualCaseERC721Trait; +use openzeppelin::token::erc721::dual721::DualCaseERC721; +use openzeppelin::tests::mocks::snake721_mock::SnakeERC721Mock; +use openzeppelin::tests::mocks::camel721_mock::CamelERC721Mock; +use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; +use openzeppelin::tests::mocks::erc721_receiver::SUCCESS; +use openzeppelin::tests::mocks::erc721_receiver::FAILURE; +use openzeppelin::tests::mocks::erc721_panic_mock::SnakeERC721PanicMock; +use openzeppelin::tests::mocks::erc721_panic_mock::CamelERC721PanicMock; +use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; +use openzeppelin::tests::utils; + +/// +/// Constants +/// + +const NAME: felt252 = 111; +const SYMBOL: felt252 = 222; +const URI: felt252 = 333; + +fn TOKEN_ID() -> u256 { + 7.into() +} +fn OWNER() -> ContractAddress { + contract_address_const::<10>() +} +fn RECIPIENT() -> ContractAddress { + contract_address_const::<20>() +} +fn SPENDER() -> ContractAddress { + contract_address_const::<30>() +} +fn OPERATOR() -> ContractAddress { + contract_address_const::<40>() +} +fn DATA(success: bool) -> Span { + let mut data = ArrayTrait::new(); + if success { + data.append(SUCCESS); + } else { + data.append(FAILURE); + } + data.span() +} + +/// +/// Setup +/// + +fn setup_snake() -> (DualCaseERC721, IERC721Dispatcher) { + let mut calldata = ArrayTrait::new(); + calldata.append(NAME); + calldata.append(SYMBOL); + calldata.append(TOKEN_ID().low.into()); + calldata.append(TOKEN_ID().high.into()); + calldata.append(URI); + set_caller_address(OWNER()); + let target = utils::deploy(SnakeERC721Mock::TEST_CLASS_HASH, calldata); + (DualCaseERC721 { contract_address: target }, IERC721Dispatcher { contract_address: target }) +} + +fn setup_camel() -> (DualCaseERC721, IERC721CamelDispatcher) { + let mut calldata = ArrayTrait::new(); + calldata.append(NAME); + calldata.append(SYMBOL); + calldata.append(TOKEN_ID().low.into()); + calldata.append(TOKEN_ID().high.into()); + calldata.append(URI); + set_caller_address(OWNER()); + let target = utils::deploy(CamelERC721Mock::TEST_CLASS_HASH, calldata); + ( + DualCaseERC721 { + contract_address: target + }, IERC721CamelDispatcher { + contract_address: target + } + ) +} + +fn setup_non_erc721() -> DualCaseERC721 { + let calldata = ArrayTrait::new(); + let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); + DualCaseERC721 { contract_address: target } +} + +fn setup_erc721_panic() -> (DualCaseERC721, DualCaseERC721) { + let snake_target = utils::deploy(SnakeERC721PanicMock::TEST_CLASS_HASH, ArrayTrait::new()); + let camel_target = utils::deploy(CamelERC721PanicMock::TEST_CLASS_HASH, ArrayTrait::new()); + ( + DualCaseERC721 { + contract_address: snake_target + }, DualCaseERC721 { + contract_address: camel_target + } + ) +} + +fn setup_receiver() -> ContractAddress { + utils::deploy(ERC721Receiver::TEST_CLASS_HASH, ArrayTrait::new()) +} + +/// +/// Case agnostic methods +/// + +#[test] +#[available_gas(2000000)] +fn test_dual_name() { + let (snake_dispatcher, _) = setup_snake(); + let (camel_dispatcher, _) = setup_camel(); + assert(snake_dispatcher.name() == NAME, 'Should return name'); + assert(camel_dispatcher.name() == NAME, 'Should return name'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_name() { + let dispatcher = setup_non_erc721(); + dispatcher.name(); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_name_exists_and_panics() { + let (dispatcher, _) = setup_erc721_panic(); + dispatcher.name(); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_symbol() { + let (snake_dispatcher, _) = setup_snake(); + let (camel_dispatcher, _) = setup_camel(); + assert(snake_dispatcher.symbol() == SYMBOL, 'Should return symbol'); + assert(camel_dispatcher.symbol() == SYMBOL, 'Should return symbol'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_symbol() { + let dispatcher = setup_non_erc721(); + dispatcher.symbol(); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_symbol_exists_and_panics() { + let (dispatcher, _) = setup_erc721_panic(); + dispatcher.symbol(); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_approve() { + let (snake_dispatcher, snake_target) = setup_snake(); + set_contract_address(OWNER()); + snake_dispatcher.approve(SPENDER(), TOKEN_ID()); + assert(snake_target.get_approved(TOKEN_ID()) == SPENDER(), 'Spender not approved correctly'); + + let (camel_dispatcher, camel_target) = setup_camel(); + set_contract_address(OWNER()); + camel_dispatcher.approve(SPENDER(), TOKEN_ID()); + assert(camel_target.getApproved(TOKEN_ID()) == SPENDER(), 'Spender not approved correctly'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_approve() { + let dispatcher = setup_non_erc721(); + dispatcher.approve(SPENDER(), TOKEN_ID()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_approve_exists_and_panics() { + let (dispatcher, _) = setup_erc721_panic(); + dispatcher.approve(SPENDER(), TOKEN_ID()); +} + +/// +/// snake_case target +/// + +#[test] +#[available_gas(2000000)] +fn test_dual_balance_of() { + let (dispatcher, _) = setup_snake(); + assert(dispatcher.balance_of(OWNER()) == 1, 'Should return balance'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_balance_of() { + let dispatcher = setup_non_erc721(); + dispatcher.balance_of(OWNER()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_balance_of_exists_and_panics() { + let (dispatcher, _) = setup_erc721_panic(); + dispatcher.balance_of(OWNER()); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_owner_of() { + let (dispatcher, target) = setup_snake(); + assert(dispatcher.owner_of(TOKEN_ID()) == OWNER(), 'Should return owner'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_owner_of() { + let dispatcher = setup_non_erc721(); + dispatcher.owner_of(TOKEN_ID()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_owner_of_exists_and_panics() { + let (dispatcher, _) = setup_erc721_panic(); + dispatcher.owner_of(TOKEN_ID()); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_transfer_from() { + let (dispatcher, target) = setup_snake(); + dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID()); + assert(target.owner_of(TOKEN_ID()) == RECIPIENT(), 'Should transfer token'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_transfer_from() { + let dispatcher = setup_non_erc721(); + dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_transfer_from_exists_and_panics() { + let (dispatcher, _) = setup_erc721_panic(); + dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID()); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_safe_transfer_from() { + let (dispatcher, target) = setup_snake(); + let receiver = setup_receiver(); + dispatcher.safe_transfer_from(OWNER(), receiver, TOKEN_ID(), DATA(true)); + assert(target.owner_of(TOKEN_ID()) == receiver, 'Should transfer token'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_safe_transfer_from() { + let dispatcher = setup_non_erc721(); + dispatcher.safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID(), DATA(true)); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_safe_transfer_from_exists_and_panics() { + let (dispatcher, _) = setup_erc721_panic(); + dispatcher.safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID(), DATA(true)); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_get_approved() { + let (dispatcher, target) = setup_snake(); + set_contract_address(OWNER()); + target.approve(SPENDER(), TOKEN_ID()); + assert(dispatcher.get_approved(TOKEN_ID()) == SPENDER(), 'Should return approval'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_get_approved() { + let dispatcher = setup_non_erc721(); + dispatcher.get_approved(TOKEN_ID()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_get_approved_exists_and_panics() { + let (dispatcher, _) = setup_erc721_panic(); + dispatcher.get_approved(TOKEN_ID()); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_set_approval_for_all() { + let (dispatcher, target) = setup_snake(); + set_contract_address(OWNER()); + dispatcher.set_approval_for_all(OPERATOR(), true); + assert(target.is_approved_for_all(OWNER(), OPERATOR()), 'Operator not approved correctly'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_set_approval_for_all() { + let dispatcher = setup_non_erc721(); + dispatcher.set_approval_for_all(OPERATOR(), true); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_set_approval_for_all_exists_and_panics() { + let (dispatcher, _) = setup_erc721_panic(); + dispatcher.set_approval_for_all(OPERATOR(), true); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_is_approved_for_all() { + let (dispatcher, target) = setup_snake(); + set_contract_address(OWNER()); + target.set_approval_for_all(OPERATOR(), true); + assert(dispatcher.is_approved_for_all(OWNER(), OPERATOR()), 'Operator not approved correctly'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_is_approved_for_all() { + let dispatcher = setup_non_erc721(); + dispatcher.is_approved_for_all(OWNER(), OPERATOR()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_is_approved_for_all_exists_and_panics() { + let (dispatcher, _) = setup_erc721_panic(); + dispatcher.is_approved_for_all(OWNER(), OPERATOR()); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_token_uri() { + let (dispatcher, target) = setup_snake(); + assert(dispatcher.token_uri(TOKEN_ID()) == URI, 'Should return URI'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_token_uri() { + let dispatcher = setup_non_erc721(); + dispatcher.token_uri(TOKEN_ID()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_token_uri_exists_and_panics() { + let (dispatcher, _) = setup_erc721_panic(); + dispatcher.token_uri(TOKEN_ID()); +} + +/// +/// camelCase target +/// + +#[test] +#[available_gas(2000000)] +fn test_dual_balanceOf() { + let (dispatcher, _) = setup_camel(); + assert(dispatcher.balance_of(OWNER()) == 1, 'Should return balance'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_balanceOf_exists_and_panics() { + let (_, dispatcher) = setup_erc721_panic(); + dispatcher.balance_of(OWNER()); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_ownerOf() { + let (dispatcher, target) = setup_camel(); + assert(dispatcher.owner_of(TOKEN_ID()) == OWNER(), 'Should return owner'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_ownerOf_exists_and_panics() { + let (_, dispatcher) = setup_erc721_panic(); + dispatcher.owner_of(TOKEN_ID()); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_transferFrom() { + let (dispatcher, target) = setup_camel(); + set_contract_address(OWNER()); + dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID()); + assert(target.ownerOf(TOKEN_ID()) == RECIPIENT(), 'Should transfer token'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_transferFrom_exists_and_panics() { + let (_, dispatcher) = setup_erc721_panic(); + dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID()); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_safeTransferFrom() { + let (dispatcher, target) = setup_camel(); + let receiver = setup_receiver(); + dispatcher.safe_transfer_from(OWNER(), receiver, TOKEN_ID(), DATA(true)); + assert(target.ownerOf(TOKEN_ID()) == receiver, 'Should transfer token'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_safeTransferFrom_exists_and_panics() { + let (_, dispatcher) = setup_erc721_panic(); + dispatcher.safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID(), DATA(true)); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_getApproved() { + let (dispatcher, target) = setup_camel(); + set_contract_address(OWNER()); + target.approve(SPENDER(), TOKEN_ID()); + assert(dispatcher.get_approved(TOKEN_ID()) == SPENDER(), 'Should return approval'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_getApproved_exists_and_panics() { + let (_, dispatcher) = setup_erc721_panic(); + dispatcher.get_approved(TOKEN_ID()); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_setApprovalForAll() { + let (dispatcher, target) = setup_camel(); + set_contract_address(OWNER()); + dispatcher.set_approval_for_all(OPERATOR(), true); + assert(target.isApprovedForAll(OWNER(), OPERATOR()), 'Operator not approved correctly'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_setApprovalForAll_exists_and_panics() { + let (_, dispatcher) = setup_erc721_panic(); + dispatcher.set_approval_for_all(OPERATOR(), true); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_isApprovedForAll() { + let (dispatcher, target) = setup_camel(); + set_contract_address(OWNER()); + target.setApprovalForAll(OPERATOR(), true); + assert(dispatcher.is_approved_for_all(OWNER(), OPERATOR()), 'Operator not approved correctly'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_isApprovedForAll_exists_and_panics() { + let (_, dispatcher) = setup_erc721_panic(); + dispatcher.is_approved_for_all(OWNER(), OPERATOR()); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_tokenUri() { + let (dispatcher, target) = setup_camel(); + assert(dispatcher.token_uri(TOKEN_ID()) == URI, 'Should return URI'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_tokenUri_exists_and_panics() { + let (_, dispatcher) = setup_erc721_panic(); + dispatcher.token_uri(TOKEN_ID()); +} diff --git a/src/openzeppelin/tests/utils.cairo b/src/openzeppelin/tests/utils.cairo index d3b720cce..e0eaea264 100644 --- a/src/openzeppelin/tests/utils.cairo +++ b/src/openzeppelin/tests/utils.cairo @@ -1,9 +1,12 @@ -use array::ArrayTrait; use core::result::ResultTrait; use option::OptionTrait; +use array::ArrayTrait; +use traits::TryInto; +use traits::Into; + +use openzeppelin::utils::BoolIntoFelt252; use starknet::class_hash::Felt252TryIntoClassHash; use starknet::ContractAddress; -use traits::TryInto; fn deploy(contract_class_hash: felt252, calldata: Array) -> ContractAddress { let (address, _) = starknet::deploy_syscall( diff --git a/src/openzeppelin/token/erc721.cairo b/src/openzeppelin/token/erc721.cairo index 2dde68df8..9b6c34399 100644 --- a/src/openzeppelin/token/erc721.cairo +++ b/src/openzeppelin/token/erc721.cairo @@ -1,3 +1,4 @@ mod erc721; use erc721::ERC721; mod interface; +mod dual721; diff --git a/src/openzeppelin/token/erc721/dual721.cairo b/src/openzeppelin/token/erc721/dual721.cairo new file mode 100644 index 000000000..e71b08874 --- /dev/null +++ b/src/openzeppelin/token/erc721/dual721.cairo @@ -0,0 +1,198 @@ +use core::result::ResultTrait; +use traits::Into; +use traits::TryInto; +use array::SpanTrait; +use array::ArrayTrait; +use option::OptionTrait; +use starknet::ContractAddress; +use starknet::SyscallResultTrait; +use starknet::call_contract_syscall; +use starknet::Felt252TryIntoContractAddress; +use openzeppelin::utils::try_selector_with_fallback; +use openzeppelin::utils::Felt252TryIntoBool; +use openzeppelin::utils::BoolIntoFelt252; +use openzeppelin::utils::selectors; + +#[derive(Copy, Drop)] +struct DualCaseERC721 { + contract_address: ContractAddress +} + +trait DualCaseERC721Trait { + fn name(self: @DualCaseERC721) -> felt252; + fn symbol(self: @DualCaseERC721) -> felt252; + fn token_uri(self: @DualCaseERC721, token_id: u256) -> felt252; + fn balance_of(self: @DualCaseERC721, account: ContractAddress) -> u256; + fn owner_of(self: @DualCaseERC721, token_id: u256) -> ContractAddress; + fn get_approved(self: @DualCaseERC721, token_id: u256) -> ContractAddress; + fn approve(self: @DualCaseERC721, to: ContractAddress, token_id: u256); + fn set_approval_for_all(self: @DualCaseERC721, operator: ContractAddress, approved: bool); + fn transfer_from( + self: @DualCaseERC721, from: ContractAddress, to: ContractAddress, token_id: u256 + ); + fn is_approved_for_all( + self: @DualCaseERC721, owner: ContractAddress, operator: ContractAddress + ) -> bool; + fn safe_transfer_from( + self: @DualCaseERC721, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span + ); +} + +impl DualCaseERC721Impl of DualCaseERC721Trait { + fn name(self: @DualCaseERC721) -> felt252 { + *call_contract_syscall(*self.contract_address, selectors::name, ArrayTrait::new().span()) + .unwrap_syscall() + .at(0) + } + + fn symbol(self: @DualCaseERC721) -> felt252 { + *call_contract_syscall(*self.contract_address, selectors::symbol, ArrayTrait::new().span()) + .unwrap_syscall() + .at(0) + } + + fn token_uri(self: @DualCaseERC721, token_id: u256) -> felt252 { + let mut args = ArrayTrait::new(); + args.append(token_id.low.into()); + args.append(token_id.high.into()); + + *try_selector_with_fallback( + *self.contract_address, selectors::token_uri, selectors::tokenUri, args.span() + ) + .unwrap_syscall() + .at(0) + } + + fn balance_of(self: @DualCaseERC721, account: ContractAddress) -> u256 { + let mut args = ArrayTrait::new(); + args.append(account.into()); + + let res = try_selector_with_fallback( + *self.contract_address, selectors::balance_of, selectors::balanceOf, args.span() + ) + .unwrap_syscall(); + + u256 { low: (*res.at(0)).try_into().unwrap(), high: (*res.at(1)).try_into().unwrap(), } + } + + fn owner_of(self: @DualCaseERC721, token_id: u256) -> ContractAddress { + let mut args = ArrayTrait::new(); + args.append(token_id.low.into()); + args.append(token_id.high.into()); + + (*try_selector_with_fallback( + *self.contract_address, selectors::owner_of, selectors::ownerOf, args.span() + ) + .unwrap_syscall() + .at(0)) + .try_into() + .unwrap() + } + + fn get_approved(self: @DualCaseERC721, token_id: u256) -> ContractAddress { + let mut args = ArrayTrait::new(); + args.append(token_id.low.into()); + args.append(token_id.high.into()); + + (*try_selector_with_fallback( + *self.contract_address, selectors::get_approved, selectors::getApproved, args.span() + ) + .unwrap_syscall() + .at(0)) + .try_into() + .unwrap() + } + + fn is_approved_for_all( + self: @DualCaseERC721, owner: ContractAddress, operator: ContractAddress + ) -> bool { + let mut args = ArrayTrait::new(); + args.append(owner.into()); + args.append(operator.into()); + + (*try_selector_with_fallback( + *self.contract_address, + selectors::is_approved_for_all, + selectors::isApprovedForAll, + args.span() + ) + .unwrap_syscall() + .at(0)) + .try_into() + .unwrap() + } + + fn approve(self: @DualCaseERC721, to: ContractAddress, token_id: u256) { + let mut args = ArrayTrait::new(); + args.append(to.into()); + args.append(token_id.low.into()); + args.append(token_id.high.into()); + call_contract_syscall(*self.contract_address, selectors::approve, args.span()) + .unwrap_syscall(); + } + + fn set_approval_for_all(self: @DualCaseERC721, operator: ContractAddress, approved: bool) { + let mut args = ArrayTrait::new(); + args.append(operator.into()); + args.append(approved.into()); + + try_selector_with_fallback( + *self.contract_address, + selectors::set_approval_for_all, + selectors::setApprovalForAll, + args.span() + ) + .unwrap_syscall(); + } + + fn transfer_from( + self: @DualCaseERC721, from: ContractAddress, to: ContractAddress, token_id: u256 + ) { + let mut args = ArrayTrait::new(); + args.append(from.into()); + args.append(to.into()); + args.append(token_id.low.into()); + args.append(token_id.high.into()); + + try_selector_with_fallback( + *self.contract_address, selectors::transfer_from, selectors::transferFrom, args.span() + ) + .unwrap_syscall(); + } + + fn safe_transfer_from( + self: @DualCaseERC721, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + mut data: Span + ) { + let mut args = ArrayTrait::new(); + args.append(from.into()); + args.append(to.into()); + args.append(token_id.low.into()); + args.append(token_id.high.into()); + args.append(data.len().into()); + + loop { + match data.pop_front() { + Option::Some(x) => args.append(*x), + Option::None(_) => { + break (); + } + }; + }; + + try_selector_with_fallback( + *self.contract_address, + selectors::safe_transfer_from, + selectors::safeTransferFrom, + args.span() + ) + .unwrap_syscall(); + } +} diff --git a/src/openzeppelin/utils.cairo b/src/openzeppelin/utils.cairo index 8dab269b1..433693466 100644 --- a/src/openzeppelin/utils.cairo +++ b/src/openzeppelin/utils.cairo @@ -1,10 +1,51 @@ +use starknet::call_contract_syscall; +use starknet::ContractAddress; +use starknet::SyscallResult; +use option::OptionTrait; use array::ArrayTrait; use array::SpanTrait; use box::BoxTrait; -use option::OptionTrait; mod constants; +mod selectors; mod serde; +fn try_selector_with_fallback( + target: ContractAddress, snake_selector: felt252, camel_selector: felt252, args: Span +) -> SyscallResult> { + match call_contract_syscall(target, snake_selector, args) { + Result::Ok(ret) => Result::Ok(ret), + Result::Err(errors) => { + if *errors.at(0) == 'ENTRYPOINT_NOT_FOUND' { + return call_contract_syscall(target, camel_selector, args); + } else { + Result::Err(errors) + } + } + } +} + +impl BoolIntoFelt252 of Into { + fn into(self: bool) -> felt252 { + if self { + return 1; + } else { + return 0; + } + } +} + +impl Felt252TryIntoBool of TryInto { + fn try_into(self: felt252) -> Option { + if self == 0 { + Option::Some(false) + } else if self == 1 { + Option::Some(true) + } else { + Option::None(()) + } + } +} + #[inline(always)] fn check_gas() { match gas::withdraw_gas() { diff --git a/src/openzeppelin/utils/selectors.cairo b/src/openzeppelin/utils/selectors.cairo new file mode 100644 index 000000000..839bd0c12 --- /dev/null +++ b/src/openzeppelin/utils/selectors.cairo @@ -0,0 +1,27 @@ +// +// ERC721 Selectors +// + +const name: felt252 = 0x361458367e696363fbcc70777d07ebbd2394e89fd0adcaf147faccd1d294d60; +const symbol: felt252 = 0x216b05c387bab9ac31918a3e61672f4618601f3c598a2f3f2710f37053e1ea4; +const token_uri: felt252 = 0x226ad7e84c1fe08eb4c525ed93cccadf9517670341304571e66f7c4f95cbe54; +const tokenUri: felt252 = 0x362dec5b8b67ab667ad08e83a2c3ba1db7fdb4ab8dc3a33c057c4fddec8d3de; +const balance_of: felt252 = 0x35a73cd311a05d46deda634c5ee045db92f811b4e74bca4437fcb5302b7af33; +const balanceOf: felt252 = 0x2e4263afad30923c891518314c3c95dbe830a16874e8abc5777a9a20b54c76e; +const owner_of: felt252 = 0x3552df12bdc6089cf963c40c4cf56fbfd4bd14680c244d1c5494c2790f1ea5c; +const ownerOf: felt252 = 0x2962ba17806af798afa6eaf4aa8c93a9fb60a3e305045b6eea33435086cae9; +const get_approved: felt252 = 0x309065f1424d76d4a4ace2ff671391d59536e0297409434908d38673290a749; +const getApproved: felt252 = 0xb180e2fe9f14914416216da76338ac0beb980443725c802af615f8431fdb1e; +const is_approved_for_all: felt252 = + 0x2aa3ea196f9b8a4f65613b67fcf185e69d8faa9601a3382871d15b3060e30dd; +const isApprovedForAll: felt252 = 0x21cdf9aedfed41bc4485ae779fda471feca12075d9127a0fc70ac6b3b3d9c30; +const approve: felt252 = 0x219209e083275171774dab1df80982e9df2096516f06319c5c6d71ae0a8480c; +const set_approval_for_all: felt252 = + 0xd86ca3d41635e20c180181046b11abcf19e1bdef3dcaa4c180300ccca1813f; +const setApprovalForAll: felt252 = + 0x2d4c8ea4c8fb9f571d1f6f9b7692fff8e5ceaf73b1df98e7da8c1109b39ae9a; +const transfer_from: felt252 = 0x3704ffe8fba161be0e994951751a5033b1462b918ff785c0a636be718dfdb68; +const transferFrom: felt252 = 0x41b033f4a31df8067c24d1e9b550a2ce75fd4a29e1147af9752174f0e6cb20; +const safe_transfer_from: felt252 = + 0x16f0218b33b5cf273196787d7cf139a9ad13d58e6674dcdce722b3bf8389863; +const safeTransferFrom: felt252 = 0x19d59d013d4aa1a8b1ce4c8299086f070733b453c02d0dc46e735edc04d6444; From ba69328ba0e22ca77dc678b7a97886880727de54 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Mon, 3 Jul 2023 00:32:56 +0200 Subject: [PATCH 053/246] Replace ERC-165 with SRC-5 (Update Interface Ids) (#637) * feat: add src5 module * refactor: interface ids and account standard interface * refactor: naming convention * refactor: separate interfaces * feat: apply last SNIP update * fix: account interface signature * feat: apply updates from review * fix: accesscontrol interface id * Update src/openzeppelin/account/account.cairo Co-authored-by: Andrew Fleming * feat: apply update from review --------- Co-authored-by: Andrew Fleming --- src/openzeppelin/access/accesscontrol.cairo | 11 ++-- src/openzeppelin/account/account.cairo | 28 ++++----- src/openzeppelin/account/interface.cairo | 4 +- src/openzeppelin/introspection.cairo | 2 +- src/openzeppelin/introspection/erc165.cairo | 42 -------------- src/openzeppelin/introspection/src5.cairo | 40 +++++++++++++ src/openzeppelin/tests.cairo | 2 +- .../tests/mocks/erc721_receiver.cairo | 16 +++--- src/openzeppelin/tests/test_account.cairo | 6 +- src/openzeppelin/tests/test_erc165.cairo | 57 ------------------- src/openzeppelin/tests/test_erc721.cairo | 8 +-- src/openzeppelin/tests/test_src5.cairo | 42 ++++++++++++++ src/openzeppelin/token/erc721/erc721.cairo | 28 ++++----- src/openzeppelin/token/erc721/interface.cairo | 22 ++++--- src/openzeppelin/utils/constants.cairo | 31 ++++------ 15 files changed, 155 insertions(+), 184 deletions(-) delete mode 100644 src/openzeppelin/introspection/erc165.cairo create mode 100644 src/openzeppelin/introspection/src5.cairo delete mode 100644 src/openzeppelin/tests/test_erc165.cairo create mode 100644 src/openzeppelin/tests/test_src5.cairo diff --git a/src/openzeppelin/access/accesscontrol.cairo b/src/openzeppelin/access/accesscontrol.cairo index d6bac6fc6..43b483585 100644 --- a/src/openzeppelin/access/accesscontrol.cairo +++ b/src/openzeppelin/access/accesscontrol.cairo @@ -1,7 +1,8 @@ use starknet::ContractAddress; const DEFAULT_ADMIN_ROLE: felt252 = 0; -const IACCESSCONTROL_ID: u32 = 0x7965db0b_u32; +const IACCESSCONTROL_ID: felt252 = + 0x23700be02858dbe2ac4dc9c9f66d0b6b0ed81ec7f970ca6844500a56ff61751; #[abi] trait IAccessControl { @@ -17,7 +18,7 @@ mod AccessControl { use super::IAccessControl; use super::DEFAULT_ADMIN_ROLE; use super::IACCESSCONTROL_ID; - use openzeppelin::introspection::erc165::ERC165; + use openzeppelin::introspection::src5::SRC5; use starknet::ContractAddress; use starknet::get_caller_address; @@ -77,8 +78,8 @@ mod AccessControl { } #[view] - fn supports_interface(interface_id: u32) -> bool { - ERC165::supports_interface(interface_id) + fn supports_interface(interface_id: felt252) -> bool { + SRC5::supports_interface(interface_id) } #[view] @@ -108,7 +109,7 @@ mod AccessControl { #[internal] fn initializer() { - ERC165::register_interface(IACCESSCONTROL_ID); + SRC5::register_interface(IACCESSCONTROL_ID); } #[internal] diff --git a/src/openzeppelin/account/account.cairo b/src/openzeppelin/account/account.cairo index 267aa9534..4061052b0 100644 --- a/src/openzeppelin/account/account.cairo +++ b/src/openzeppelin/account/account.cairo @@ -31,7 +31,7 @@ trait AccountABI { #[view] fn is_valid_signature(message: felt252, signature: Array) -> u32; #[view] - fn supports_interface(interface_id: u32) -> bool; + fn supports_interface(interface_id: felt252) -> bool; } #[account_contract] @@ -40,17 +40,17 @@ mod Account { use array::ArrayTrait; use box::BoxTrait; use ecdsa::check_ecdsa_signature; + use option::OptionTrait; use serde::ArraySerde; use starknet::get_tx_info; use starknet::get_caller_address; use starknet::get_contract_address; - use option::OptionTrait; use zeroable::Zeroable; - use openzeppelin::account::interface::ERC1271_VALIDATED; use openzeppelin::account::interface::IAccount; use openzeppelin::account::interface::IACCOUNT_ID; - use openzeppelin::introspection::erc165::ERC165; + use openzeppelin::account::interface::ERC1271_VALIDATED; + use openzeppelin::introspection::src5::SRC5; use super::Call; use super::QUERY_VERSION; @@ -61,6 +61,11 @@ mod Account { public_key: felt252 } + #[constructor] + fn constructor(_public_key: felt252) { + initializer(_public_key); + } + impl AccountImpl of IAccount { fn __execute__(mut calls: Array) -> Array> { // Avoid calls from other contracts @@ -90,20 +95,15 @@ mod Account { if _is_valid_signature(message, signature.span()) { ERC1271_VALIDATED } else { - 0_u32 + 0 } } - fn supports_interface(interface_id: u32) -> bool { - ERC165::supports_interface(interface_id) + fn supports_interface(interface_id: felt252) -> bool { + SRC5::supports_interface(interface_id) } } - #[constructor] - fn constructor(_public_key: felt252) { - initializer(_public_key); - } - // // Externals // @@ -151,7 +151,7 @@ mod Account { } #[view] - fn supports_interface(interface_id: u32) -> bool { + fn supports_interface(interface_id: felt252) -> bool { AccountImpl::supports_interface(interface_id) } @@ -161,7 +161,7 @@ mod Account { #[internal] fn initializer(_public_key: felt252) { - ERC165::register_interface(IACCOUNT_ID); + SRC5::register_interface(IACCOUNT_ID); public_key::write(_public_key); } diff --git a/src/openzeppelin/account/interface.cairo b/src/openzeppelin/account/interface.cairo index 5c77f797b..6f30f12d7 100644 --- a/src/openzeppelin/account/interface.cairo +++ b/src/openzeppelin/account/interface.cairo @@ -2,7 +2,7 @@ use array::ArrayTrait; use array::SpanTrait; use starknet::ContractAddress; -const IACCOUNT_ID: u32 = 0xa66bd575_u32; +const IACCOUNT_ID: felt252 = 0x36c738c1c375b993078fe6b517d477e5a3c9b104e40c04662c4bdd3e2f5fa4a; const ERC1271_VALIDATED: u32 = 0x1626ba7e_u32; #[derive(Serde, Drop)] @@ -17,5 +17,5 @@ trait IAccount { fn __validate__(calls: Array) -> felt252; fn __validate_declare__(class_hash: felt252) -> felt252; fn is_valid_signature(message: felt252, signature: Array) -> u32; - fn supports_interface(interface_id: u32) -> bool; + fn supports_interface(interface_id: felt252) -> bool; } diff --git a/src/openzeppelin/introspection.cairo b/src/openzeppelin/introspection.cairo index 4391460f4..2d1a93a9f 100644 --- a/src/openzeppelin/introspection.cairo +++ b/src/openzeppelin/introspection.cairo @@ -1 +1 @@ -mod erc165; +mod src5; diff --git a/src/openzeppelin/introspection/erc165.cairo b/src/openzeppelin/introspection/erc165.cairo deleted file mode 100644 index 4a3b01778..000000000 --- a/src/openzeppelin/introspection/erc165.cairo +++ /dev/null @@ -1,42 +0,0 @@ -const IERC165_ID: u32 = 0x01ffc9a7_u32; -const INVALID_ID: u32 = 0xffffffff_u32; - -#[abi] -trait IERC165 { - fn supports_interface(interface_id: u32) -> bool; -} - -#[contract] -mod ERC165 { - use openzeppelin::introspection::erc165; - - struct Storage { - supported_interfaces: LegacyMap - } - - impl ERC165 of erc165::IERC165 { - fn supports_interface(interface_id: u32) -> bool { - if interface_id == erc165::IERC165_ID { - return true; - } - supported_interfaces::read(interface_id) - } - } - - #[view] - fn supports_interface(interface_id: u32) -> bool { - ERC165::supports_interface(interface_id) - } - - #[internal] - fn register_interface(interface_id: u32) { - assert(interface_id != erc165::INVALID_ID, 'Invalid id'); - supported_interfaces::write(interface_id, true); - } - - #[internal] - fn deregister_interface(interface_id: u32) { - assert(interface_id != erc165::IERC165_ID, 'Invalid id'); - supported_interfaces::write(interface_id, false); - } -} diff --git a/src/openzeppelin/introspection/src5.cairo b/src/openzeppelin/introspection/src5.cairo new file mode 100644 index 000000000..424a6faf1 --- /dev/null +++ b/src/openzeppelin/introspection/src5.cairo @@ -0,0 +1,40 @@ +const ISRC5_ID: felt252 = 0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055; + +#[abi] +trait ISRC5 { + fn supports_interface(interface_id: felt252) -> bool; +} + +#[contract] +mod SRC5 { + use openzeppelin::introspection::src5; + + struct Storage { + supported_interfaces: LegacyMap + } + + impl SRC5Impl of src5::ISRC5 { + fn supports_interface(interface_id: felt252) -> bool { + if interface_id == src5::ISRC5_ID { + return true; + } + supported_interfaces::read(interface_id) + } + } + + #[view] + fn supports_interface(interface_id: felt252) -> bool { + SRC5Impl::supports_interface(interface_id) + } + + #[internal] + fn register_interface(interface_id: felt252) { + supported_interfaces::write(interface_id, true); + } + + #[internal] + fn deregister_interface(interface_id: felt252) { + assert(interface_id != src5::ISRC5_ID, 'SRC5: invalid id'); + supported_interfaces::write(interface_id, false); + } +} diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index fd9d9d0c0..4ecdf57dc 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -1,7 +1,7 @@ mod test_accesscontrol; mod test_reentrancyguard; mod test_ownable; -mod test_erc165; +mod test_src5; mod test_account; mod test_erc20; mod test_erc721; diff --git a/src/openzeppelin/tests/mocks/erc721_receiver.cairo b/src/openzeppelin/tests/mocks/erc721_receiver.cairo index c9e848630..4bb3556bf 100644 --- a/src/openzeppelin/tests/mocks/erc721_receiver.cairo +++ b/src/openzeppelin/tests/mocks/erc721_receiver.cairo @@ -6,7 +6,7 @@ mod ERC721Receiver { use openzeppelin::token::erc721::interface::IERC721Receiver; use openzeppelin::token::erc721::interface::IERC721ReceiverCamel; use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; - use openzeppelin::introspection::erc165::ERC165; + use openzeppelin::introspection::src5::SRC5; use openzeppelin::utils::serde::SpanSerde; use starknet::ContractAddress; @@ -15,7 +15,7 @@ mod ERC721Receiver { impl ERC721ReceiverImpl of IERC721Receiver { fn on_erc721_received( operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span - ) -> u32 { + ) -> felt252 { if *data.at(0) == super::SUCCESS { IERC721_RECEIVER_ID } else { @@ -27,32 +27,32 @@ mod ERC721Receiver { impl ERC721ReceiverCamelImpl of IERC721ReceiverCamel { fn onERC721Received( operator: ContractAddress, from: ContractAddress, tokenId: u256, data: Span - ) -> u32 { + ) -> felt252 { ERC721ReceiverImpl::on_erc721_received(operator, from, tokenId, data) } } #[constructor] fn constructor() { - ERC165::register_interface(IERC721_RECEIVER_ID); + SRC5::register_interface(IERC721_RECEIVER_ID); } #[view] - fn supports_interface(interface_id: u32) -> bool { - ERC165::supports_interface(interface_id) + fn supports_interface(interface_id: felt252) -> bool { + SRC5::supports_interface(interface_id) } #[external] fn on_erc721_received( operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span - ) -> u32 { + ) -> felt252 { ERC721ReceiverImpl::on_erc721_received(operator, from, token_id, data) } #[external] fn onERC721Received( operator: ContractAddress, from: ContractAddress, tokenId: u256, data: Span - ) -> u32 { + ) -> felt252 { ERC721ReceiverCamelImpl::onERC721Received(operator, from, tokenId, data) } } diff --git a/src/openzeppelin/tests/test_account.cairo b/src/openzeppelin/tests/test_account.cairo index 210e91f50..5a833327f 100644 --- a/src/openzeppelin/tests/test_account.cairo +++ b/src/openzeppelin/tests/test_account.cairo @@ -14,7 +14,7 @@ use openzeppelin::account::interface::ERC1271_VALIDATED; use openzeppelin::account::interface::IACCOUNT_ID; use openzeppelin::account::QUERY_VERSION; use openzeppelin::account::TRANSACTION_VERSION; -use openzeppelin::introspection::erc165::IERC165_ID; +use openzeppelin::introspection::src5::ISRC5_ID; use openzeppelin::tests::utils; use openzeppelin::token::erc20::ERC20; use openzeppelin::token::erc20::IERC20Dispatcher; @@ -103,7 +103,7 @@ fn test_constructor() { fn test_interfaces() { Account::constructor(PUBLIC_KEY); - let supports_default_interface = Account::supports_interface(IERC165_ID); + let supports_default_interface = Account::supports_interface(ISRC5_ID); assert(supports_default_interface, 'Should support base interface'); let supports_account_interface = Account::supports_interface(IACCOUNT_ID); @@ -130,7 +130,7 @@ fn test_is_valid_signature() { assert(is_valid == ERC1271_VALIDATED, 'Should accept valid signature'); let is_valid = Account::is_valid_signature(message, bad_signature); - assert(is_valid == 0_u32, 'Should reject invalid signature'); + assert(is_valid == 0, 'Should reject invalid signature'); } #[test] diff --git a/src/openzeppelin/tests/test_erc165.cairo b/src/openzeppelin/tests/test_erc165.cairo deleted file mode 100644 index c66c1b034..000000000 --- a/src/openzeppelin/tests/test_erc165.cairo +++ /dev/null @@ -1,57 +0,0 @@ -use openzeppelin::introspection::erc165::ERC165; -use openzeppelin::introspection::erc165::IERC165_ID; -use openzeppelin::introspection::erc165::INVALID_ID; - -const OTHER_ID: u32 = 0x12345678_u32; - -#[test] -#[available_gas(2000000)] -fn test_default_behavior() { - let supports_default_interface: bool = ERC165::supports_interface(IERC165_ID); - assert(supports_default_interface, 'Should support base interface'); -} - -#[test] -#[available_gas(2000000)] -fn test_not_registered_interface() { - let supports_unregistered_interface: bool = ERC165::supports_interface(OTHER_ID); - assert(!supports_unregistered_interface, 'Should not support unregistered'); -} - -#[test] -#[available_gas(2000000)] -fn test_supports_invalid_interface() { - let supports_invalid_interface: bool = ERC165::supports_interface(INVALID_ID); - assert(!supports_invalid_interface, 'Should not support invalid id'); -} - -#[test] -#[available_gas(2000000)] -fn test_register_interface() { - ERC165::register_interface(OTHER_ID); - let supports_new_interface: bool = ERC165::supports_interface(OTHER_ID); - assert(supports_new_interface, 'Should support new interface'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('Invalid id', ))] -fn test_register_invalid_interface() { - ERC165::register_interface(INVALID_ID); -} - -#[test] -#[available_gas(2000000)] -fn test_deregister_interface() { - ERC165::register_interface(OTHER_ID); - ERC165::deregister_interface(OTHER_ID); - let supports_old_interface: bool = ERC165::supports_interface(OTHER_ID); - assert(!supports_old_interface, 'Should not support interface'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('Invalid id', ))] -fn test_deregister_default_interface() { - ERC165::deregister_interface(IERC165_ID); -} diff --git a/src/openzeppelin/tests/test_erc721.cairo b/src/openzeppelin/tests/test_erc721.cairo index 3b81360ae..d37a495a8 100644 --- a/src/openzeppelin/tests/test_erc721.cairo +++ b/src/openzeppelin/tests/test_erc721.cairo @@ -1,4 +1,4 @@ -use openzeppelin::introspection::erc165; +use openzeppelin::introspection::src5; use openzeppelin::token::erc721; use openzeppelin::token::erc721::ERC721; use openzeppelin::account::Account; @@ -92,8 +92,7 @@ fn test_constructor() { assert( ERC721::supports_interface(erc721::interface::IERC721_METADATA_ID), 'missing interface ID' ); - assert(ERC721::supports_interface(erc165::IERC165_ID), 'missing interface ID'); - assert(!ERC721::supports_interface(erc165::INVALID_ID), 'invalid interface ID'); + assert(ERC721::supports_interface(src5::ISRC5_ID), 'missing interface ID'); } #[test] @@ -109,8 +108,7 @@ fn test_initialize() { assert( ERC721::supports_interface(erc721::interface::IERC721_METADATA_ID), 'missing interface ID' ); - assert(ERC721::supports_interface(erc165::IERC165_ID), 'missing interface ID'); - assert(!ERC721::supports_interface(erc165::INVALID_ID), 'invalid interface ID'); + assert(ERC721::supports_interface(src5::ISRC5_ID), 'missing interface ID'); } /// diff --git a/src/openzeppelin/tests/test_src5.cairo b/src/openzeppelin/tests/test_src5.cairo new file mode 100644 index 000000000..9b43bf894 --- /dev/null +++ b/src/openzeppelin/tests/test_src5.cairo @@ -0,0 +1,42 @@ +use openzeppelin::introspection::src5::SRC5; +use openzeppelin::introspection::src5::ISRC5_ID; + +const OTHER_ID: felt252 = 0x12345678; + +#[test] +#[available_gas(2000000)] +fn test_default_behavior() { + let supports_default_interface: bool = SRC5::supports_interface(ISRC5_ID); + assert(supports_default_interface, 'Should support base interface'); +} + +#[test] +#[available_gas(2000000)] +fn test_not_registered_interface() { + let supports_unregistered_interface: bool = SRC5::supports_interface(OTHER_ID); + assert(!supports_unregistered_interface, 'Should not support unregistered'); +} + +#[test] +#[available_gas(2000000)] +fn test_register_interface() { + SRC5::register_interface(OTHER_ID); + let supports_new_interface: bool = SRC5::supports_interface(OTHER_ID); + assert(supports_new_interface, 'Should support new interface'); +} + +#[test] +#[available_gas(2000000)] +fn test_deregister_interface() { + SRC5::register_interface(OTHER_ID); + SRC5::deregister_interface(OTHER_ID); + let supports_old_interface: bool = SRC5::supports_interface(OTHER_ID); + assert(!supports_old_interface, 'Should not support interface'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('SRC5: invalid id', ))] +fn test_deregister_default_interface() { + SRC5::deregister_interface(ISRC5_ID); +} diff --git a/src/openzeppelin/token/erc721/erc721.cairo b/src/openzeppelin/token/erc721/erc721.cairo index e22466078..7c1345447 100644 --- a/src/openzeppelin/token/erc721/erc721.cairo +++ b/src/openzeppelin/token/erc721/erc721.cairo @@ -53,14 +53,14 @@ trait ERC721ABI { mod ERC721 { // OZ modules use openzeppelin::account; - use openzeppelin::introspection::erc165; + use openzeppelin::introspection::src5; use openzeppelin::token::erc721; // Dispatchers - use openzeppelin::introspection::erc165::IERC165Dispatcher; - use openzeppelin::introspection::erc165::IERC165DispatcherTrait; - use super::super::interface::IERC721ReceiverABIDispatcher; - use super::super::interface::IERC721ReceiverABIDispatcherTrait; + use openzeppelin::introspection::src5::ISRC5Dispatcher; + use openzeppelin::introspection::src5::ISRC5DispatcherTrait; + use super::super::interface::ERC721ReceiverABIDispatcher; + use super::super::interface::ERC721ReceiverABIDispatcherTrait; // Other use starknet::ContractAddress; @@ -209,13 +209,13 @@ mod ERC721 { // View #[view] - fn supports_interface(interface_id: u32) -> bool { - erc165::ERC165::supports_interface(interface_id) + fn supports_interface(interface_id: felt252) -> bool { + src5::SRC5::supports_interface(interface_id) } #[view] - fn supportsInterface(interfaceId: u32) -> bool { - erc165::ERC165::supports_interface(interfaceId) + fn supportsInterface(interfaceId: felt252) -> bool { + src5::SRC5::supports_interface(interfaceId) } #[view] @@ -325,8 +325,8 @@ mod ERC721 { fn initializer(name_: felt252, symbol_: felt252) { _name::write(name_); _symbol::write(symbol_); - erc165::ERC165::register_interface(erc721::interface::IERC721_ID); - erc165::ERC165::register_interface(erc721::interface::IERC721_METADATA_ID); + src5::SRC5::register_interface(erc721::interface::IERC721_ID); + src5::SRC5::register_interface(erc721::interface::IERC721_METADATA_ID); } #[internal] @@ -443,18 +443,18 @@ mod ERC721 { fn _check_on_erc721_received( from: ContractAddress, to: ContractAddress, token_id: u256, data: Span ) -> bool { - if (IERC165Dispatcher { + if (ISRC5Dispatcher { contract_address: to }.supports_interface(erc721::interface::IERC721_RECEIVER_ID)) { // todo add casing fallback mechanism - IERC721ReceiverABIDispatcher { + ERC721ReceiverABIDispatcher { contract_address: to } .on_erc721_received( get_caller_address(), from, token_id, data ) == erc721::interface::IERC721_RECEIVER_ID } else { - IERC165Dispatcher { + ISRC5Dispatcher { contract_address: to }.supports_interface(account::interface::IACCOUNT_ID) } diff --git a/src/openzeppelin/token/erc721/interface.cairo b/src/openzeppelin/token/erc721/interface.cairo index a8b21f60e..78283dbde 100644 --- a/src/openzeppelin/token/erc721/interface.cairo +++ b/src/openzeppelin/token/erc721/interface.cairo @@ -2,11 +2,12 @@ use openzeppelin::utils::serde::SpanSerde; use starknet::ContractAddress; use array::SpanTrait; -const IERC721_ID: u32 = 0x80ac58cd_u32; -const IERC721_METADATA_ID: u32 = 0x5b5e139f_u32; -const IERC721_RECEIVER_ID: u32 = 0x150b7a02_u32; +const IERC721_ID: felt252 = 0x33eb2f84c309543403fd69f0d0f363781ef06ef6faeb0131ff16ea3175bd943; +const IERC721_METADATA_ID: felt252 = + 0x6069a70848f907fa57668ba1875164eb4dcee693952468581406d131081bbd; +const IERC721_RECEIVER_ID: felt252 = + 0x3a0dff5f70d80458ad14ae37bb182a728e3c8cdda0402a5daa86620bdf910bc; -#[abi] trait IERC721 { fn balance_of(account: ContractAddress) -> u256; fn owner_of(token_id: u256) -> ContractAddress; @@ -24,7 +25,6 @@ trait IERC721 { fn token_uri(token_id: u256) -> felt252; } -#[abi] trait IERC721Camel { fn balanceOf(account: ContractAddress) -> u256; fn ownerOf(tokenId: u256) -> ContractAddress; @@ -47,25 +47,23 @@ trait IERC721Camel { // #[abi] -trait IERC721ReceiverABI { +trait ERC721ReceiverABI { fn on_erc721_received( operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span - ) -> u32; + ) -> felt252; fn onERC721Received( operator: ContractAddress, from: ContractAddress, tokenId: u256, data: Span - ) -> u32; + ) -> felt252; } -#[abi] trait IERC721Receiver { fn on_erc721_received( operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span - ) -> u32; + ) -> felt252; } -#[abi] trait IERC721ReceiverCamel { fn onERC721Received( operator: ContractAddress, from: ContractAddress, tokenId: u256, data: Span - ) -> u32; + ) -> felt252; } diff --git a/src/openzeppelin/utils/constants.cairo b/src/openzeppelin/utils/constants.cairo index d7346bf55..7ab63d0da 100644 --- a/src/openzeppelin/utils/constants.cairo +++ b/src/openzeppelin/utils/constants.cairo @@ -2,34 +2,25 @@ // Interface ids // -// ERC165 -// See: https://eips.ethereum.org/EIPS/eip-165 -const IERC165_ID: u32 = 0x01ffc9a7_u32; -const INVALID_ID: u32 = 0xffffffff_u32; +// SRC5 +// See: https://github.com/starknet-io/SNIPs/pull/24 +const ISRC5_ID: felt252 = 0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055; // Account -// See: https://github.com/OpenZeppelin/cairo-contracts/pull/449#discussion_r966242914 -const IACCOUNT_ID: u32 = 0xa66bd575_u32; +const IACCOUNT_ID: felt252 = 0x36c738c1c375b993078fe6b517d477e5a3c9b104e40c04662c4bdd3e2f5fa4a; // ERC721 // See: https://eips.ethereum.org/EIPS/eip-721 -const IERC721_ID: u32 = 0x80ac58cd_u32; -const IERC721_RECEIVER_ID: u32 = 0x150b7a02_u32; -const IERC721_METADATA_ID: u32 = 0x5b5e139f_u32; -const IERC721_ENUMERABLE_ID: u32 = 0x780e9d63_u32; - -// ERC1155 -// See: https://eips.ethereum.org/EIPS/eip-1155 -const IERC1155_ID: u32 = 0xd9b67a26_u32; -const IERC1155_METADATA_ID: u32 = 0x0e89341c_u32; -const IERC1155_RECEIVER_ID: u32 = 0x4e2312e0_u32; -const ON_ERC1155_RECEIVED_SELECTOR: u32 = 0xf23a6e61_u32; -const ON_ERC1155_BATCH_RECEIVED_SELECTOR: u32 = 0xbc197c81_u32; +const IERC721_ID: felt252 = 0x33eb2f84c309543403fd69f0d0f363781ef06ef6faeb0131ff16ea3175bd943; +const IERC721_METADATA_ID: felt252 = + 0x6069a70848f907fa57668ba1875164eb4dcee693952468581406d131081bbd; +const IERC721_RECEIVER_ID: felt252 = + 0x3a0dff5f70d80458ad14ae37bb182a728e3c8cdda0402a5daa86620bdf910bc; // AccessControl -// Calculated from XOR of all function selectors in IAccessControl. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/IAccessControl.sol -const IACCESSCONTROL_ID: u32 = 0x7965db0b_u32; +const IACCESSCONTROL_ID: felt252 = + 0x23700be02858dbe2ac4dc9c9f66d0b6b0ed81ec7f970ca6844500a56ff61751; // // Roles From 8534ec6904d488507b8c062ee76cdeb82fde3203 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Mon, 3 Jul 2023 10:21:47 -0400 Subject: [PATCH 054/246] Fix conflicts from the dual721 and src5 merge (#641) * add abi to interfaces * change u32 to felt --- src/openzeppelin/tests/mocks/camel721_mock.cairo | 2 +- src/openzeppelin/tests/mocks/snake721_mock.cairo | 2 +- src/openzeppelin/token/erc721/interface.cairo | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/openzeppelin/tests/mocks/camel721_mock.cairo b/src/openzeppelin/tests/mocks/camel721_mock.cairo index 2b2715ec9..ef0d95ed2 100644 --- a/src/openzeppelin/tests/mocks/camel721_mock.cairo +++ b/src/openzeppelin/tests/mocks/camel721_mock.cairo @@ -15,7 +15,7 @@ mod CamelERC721Mock { // View #[view] - fn supportsInterface(interfaceId: u32) -> bool { + fn supportsInterface(interfaceId: felt252) -> bool { ERC721::supports_interface(interfaceId) } diff --git a/src/openzeppelin/tests/mocks/snake721_mock.cairo b/src/openzeppelin/tests/mocks/snake721_mock.cairo index 44f4e91d9..eca3d70cb 100644 --- a/src/openzeppelin/tests/mocks/snake721_mock.cairo +++ b/src/openzeppelin/tests/mocks/snake721_mock.cairo @@ -15,7 +15,7 @@ mod SnakeERC721Mock { // View #[view] - fn supports_interface(interface_id: u32) -> bool { + fn supports_interface(interface_id: felt252) -> bool { ERC721::supports_interface(interface_id) } diff --git a/src/openzeppelin/token/erc721/interface.cairo b/src/openzeppelin/token/erc721/interface.cairo index 78283dbde..53292dd94 100644 --- a/src/openzeppelin/token/erc721/interface.cairo +++ b/src/openzeppelin/token/erc721/interface.cairo @@ -8,6 +8,7 @@ const IERC721_METADATA_ID: felt252 = const IERC721_RECEIVER_ID: felt252 = 0x3a0dff5f70d80458ad14ae37bb182a728e3c8cdda0402a5daa86620bdf910bc; +#[abi] trait IERC721 { fn balance_of(account: ContractAddress) -> u256; fn owner_of(token_id: u256) -> ContractAddress; @@ -25,6 +26,7 @@ trait IERC721 { fn token_uri(token_id: u256) -> felt252; } +#[abi] trait IERC721Camel { fn balanceOf(account: ContractAddress) -> u256; fn ownerOf(tokenId: u256) -> ContractAddress; From bc90bd5bcf5894963249bb099aff8c35a9eb5955 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Mon, 3 Jul 2023 12:32:43 -0400 Subject: [PATCH 055/246] Add camel support for ownable (#625) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add camel support * add camel tests * nest ownable into dir * move interface * add dual ownable * add const selectors * add try_selector_with_fallback and bool impl * nest access tests in test dirs * move accesscontrol and ownable test mods * move tests * move tests * add mocks * add dual ownable tests * add panic traits * tidy up tests * fix formatting * remove comments * fix hierarchy * remove import comments * remove comment * Apply suggestions from code review Co-authored-by: Martín Triay * remove unused import * fix camel dispatcher * normalize assert error msg * fix formatting * remove redundant assertion * change selector casing * remove unused import * fix comments, add ref for ignored tests * fix comments * remove panic trait * bump to cairo v1.1.1 * update Cargo * remove ignore from tests * Apply suggestions from code review Co-authored-by: Eric Nordelo * remove selectors from constants * add selectors to selectors.cairo * remove selector bindings * fix comment * remove/fix comments * fix conflicts * Update src/openzeppelin/utils/selectors.cairo Co-authored-by: Eric Nordelo --------- Co-authored-by: Martín Triay Co-authored-by: Eric Nordelo --- src/openzeppelin/access/ownable.cairo | 55 +---- .../access/ownable/dual_ownable.cairo | 60 ++++++ .../access/ownable/interface.cairo | 21 ++ src/openzeppelin/access/ownable/ownable.cairo | 93 ++++++++ src/openzeppelin/tests.cairo | 3 +- src/openzeppelin/tests/access.cairo | 3 + .../{ => access}/test_accesscontrol.cairo | 0 .../tests/access/test_dual_ownable.cairo | 198 ++++++++++++++++++ .../tests/{ => access}/test_ownable.cairo | 72 ++++++- src/openzeppelin/tests/mocks.cairo | 1 + .../tests/mocks/dual_ownable_mocks.cairo | 93 ++++++++ src/openzeppelin/utils/selectors.cairo | 14 +- 12 files changed, 558 insertions(+), 55 deletions(-) create mode 100644 src/openzeppelin/access/ownable/dual_ownable.cairo create mode 100644 src/openzeppelin/access/ownable/interface.cairo create mode 100644 src/openzeppelin/access/ownable/ownable.cairo create mode 100644 src/openzeppelin/tests/access.cairo rename src/openzeppelin/tests/{ => access}/test_accesscontrol.cairo (100%) create mode 100644 src/openzeppelin/tests/access/test_dual_ownable.cairo rename src/openzeppelin/tests/{ => access}/test_ownable.cairo (57%) create mode 100644 src/openzeppelin/tests/mocks/dual_ownable_mocks.cairo diff --git a/src/openzeppelin/access/ownable.cairo b/src/openzeppelin/access/ownable.cairo index 69bf5b2b1..7b8e812b9 100644 --- a/src/openzeppelin/access/ownable.cairo +++ b/src/openzeppelin/access/ownable.cairo @@ -1,52 +1,5 @@ -#[contract] -mod Ownable { - use starknet::ContractAddress; - use starknet::get_caller_address; - use zeroable::Zeroable; +mod ownable; +use ownable::Ownable; - struct Storage { - _owner: ContractAddress - } - - #[event] - fn OwnershipTransferred(previous_owner: ContractAddress, new_owner: ContractAddress) {} - - #[internal] - fn initializer() { - let caller: ContractAddress = get_caller_address(); - _transfer_ownership(caller); - } - - #[internal] - fn assert_only_owner() { - let owner: ContractAddress = _owner::read(); - let caller: ContractAddress = get_caller_address(); - assert(!caller.is_zero(), 'Caller is the zero address'); - assert(caller == owner, 'Caller is not the owner'); - } - - #[internal] - fn owner() -> ContractAddress { - _owner::read() - } - - #[internal] - fn transfer_ownership(new_owner: ContractAddress) { - assert(!new_owner.is_zero(), 'New owner is the zero address'); - assert_only_owner(); - _transfer_ownership(new_owner); - } - - #[internal] - fn renounce_ownership() { - assert_only_owner(); - _transfer_ownership(Zeroable::zero()); - } - - #[internal] - fn _transfer_ownership(new_owner: ContractAddress) { - let previous_owner: ContractAddress = _owner::read(); - _owner::write(new_owner); - OwnershipTransferred(previous_owner, new_owner); - } -} +mod interface; +mod dual_ownable; diff --git a/src/openzeppelin/access/ownable/dual_ownable.cairo b/src/openzeppelin/access/ownable/dual_ownable.cairo new file mode 100644 index 000000000..840c371f5 --- /dev/null +++ b/src/openzeppelin/access/ownable/dual_ownable.cairo @@ -0,0 +1,60 @@ +use array::SpanTrait; +use array::ArrayTrait; +use core::result::ResultTrait; +use option::OptionTrait; +use starknet::ContractAddress; +use starknet::Felt252TryIntoContractAddress; +use starknet::SyscallResultTrait; +use starknet::call_contract_syscall; +use traits::Into; +use traits::TryInto; + +use openzeppelin::utils::try_selector_with_fallback; +use openzeppelin::utils::Felt252TryIntoBool; +use openzeppelin::utils::selectors; + +#[derive(Copy, Drop)] +struct DualCaseOwnable { + contract_address: ContractAddress +} + +trait DualCaseOwnableTrait { + fn owner(self: @DualCaseOwnable) -> ContractAddress; + fn transfer_ownership(self: @DualCaseOwnable, new_owner: ContractAddress); + fn renounce_ownership(self: @DualCaseOwnable); +} + +impl DualCaseOwnableImpl of DualCaseOwnableTrait { + fn owner(self: @DualCaseOwnable) -> ContractAddress { + (*call_contract_syscall(*self.contract_address, selectors::owner, ArrayTrait::new().span()) + .unwrap_syscall() + .at(0)) + .try_into() + .unwrap() + } + + fn transfer_ownership(self: @DualCaseOwnable, new_owner: ContractAddress) { + let mut args = ArrayTrait::new(); + args.append(new_owner.into()); + + try_selector_with_fallback( + *self.contract_address, + selectors::transfer_ownership, + selectors::transferOwnership, + args.span() + ) + .unwrap_syscall(); + } + + fn renounce_ownership(self: @DualCaseOwnable) { + let mut args = ArrayTrait::new(); + + try_selector_with_fallback( + *self.contract_address, + selectors::renounce_ownership, + selectors::renounceOwnership, + args.span() + ) + .unwrap_syscall(); + } +} diff --git a/src/openzeppelin/access/ownable/interface.cairo b/src/openzeppelin/access/ownable/interface.cairo new file mode 100644 index 000000000..41cd27a69 --- /dev/null +++ b/src/openzeppelin/access/ownable/interface.cairo @@ -0,0 +1,21 @@ +use starknet::ContractAddress; + +#[abi] +trait IOwnable { + #[view] + fn owner() -> ContractAddress; + #[external] + fn transfer_ownership(new_owner: ContractAddress); + #[external] + fn renounce_ownership(); +} + +#[abi] +trait IOwnableCamel { + #[view] + fn owner() -> ContractAddress; + #[external] + fn transferOwnership(newOwner: ContractAddress); + #[external] + fn renounceOwnership(); +} diff --git a/src/openzeppelin/access/ownable/ownable.cairo b/src/openzeppelin/access/ownable/ownable.cairo new file mode 100644 index 000000000..1027d4ca7 --- /dev/null +++ b/src/openzeppelin/access/ownable/ownable.cairo @@ -0,0 +1,93 @@ +#[contract] +mod Ownable { + use openzeppelin::access::ownable::interface; + use starknet::ContractAddress; + use starknet::get_caller_address; + use zeroable::Zeroable; + + struct Storage { + _owner: ContractAddress + } + + #[event] + fn OwnershipTransferred(previous_owner: ContractAddress, new_owner: ContractAddress) {} + + impl OwnableImpl of interface::IOwnable { + fn owner() -> ContractAddress { + _owner::read() + } + + fn transfer_ownership(new_owner: ContractAddress) { + assert(!new_owner.is_zero(), 'New owner is the zero address'); + assert_only_owner(); + _transfer_ownership(new_owner); + } + + fn renounce_ownership() { + assert_only_owner(); + _transfer_ownership(Zeroable::zero()); + } + } + + impl OwnableCamelImpl of interface::IOwnableCamel { + fn owner() -> ContractAddress { + OwnableImpl::owner() + } + + fn transferOwnership(newOwner: ContractAddress) { + OwnableImpl::transfer_ownership(newOwner); + } + + fn renounceOwnership() { + OwnableImpl::renounce_ownership(); + } + } + + #[view] + fn owner() -> ContractAddress { + OwnableImpl::owner() + } + + #[external] + fn transfer_ownership(new_owner: ContractAddress) { + OwnableImpl::transfer_ownership(new_owner); + } + + #[external] + fn transferOwnership(newOwner: ContractAddress) { + OwnableCamelImpl::transferOwnership(newOwner); + } + + #[external] + fn renounce_ownership() { + OwnableImpl::renounce_ownership(); + } + + #[external] + fn renounceOwnership() { + OwnableCamelImpl::renounceOwnership(); + } + + // Internals + + #[internal] + fn initializer() { + let caller: ContractAddress = get_caller_address(); + _transfer_ownership(caller); + } + + #[internal] + fn assert_only_owner() { + let owner: ContractAddress = _owner::read(); + let caller: ContractAddress = get_caller_address(); + assert(!caller.is_zero(), 'Caller is the zero address'); + assert(caller == owner, 'Caller is not the owner'); + } + + #[internal] + fn _transfer_ownership(new_owner: ContractAddress) { + let previous_owner: ContractAddress = _owner::read(); + _owner::write(new_owner); + OwnershipTransferred(previous_owner, new_owner); + } +} diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index 4ecdf57dc..e50d9f319 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -1,6 +1,5 @@ -mod test_accesscontrol; +mod access; mod test_reentrancyguard; -mod test_ownable; mod test_src5; mod test_account; mod test_erc20; diff --git a/src/openzeppelin/tests/access.cairo b/src/openzeppelin/tests/access.cairo new file mode 100644 index 000000000..f5857e98d --- /dev/null +++ b/src/openzeppelin/tests/access.cairo @@ -0,0 +1,3 @@ +mod test_accesscontrol; +mod test_ownable; +mod test_dual_ownable; diff --git a/src/openzeppelin/tests/test_accesscontrol.cairo b/src/openzeppelin/tests/access/test_accesscontrol.cairo similarity index 100% rename from src/openzeppelin/tests/test_accesscontrol.cairo rename to src/openzeppelin/tests/access/test_accesscontrol.cairo diff --git a/src/openzeppelin/tests/access/test_dual_ownable.cairo b/src/openzeppelin/tests/access/test_dual_ownable.cairo new file mode 100644 index 000000000..dca5595f7 --- /dev/null +++ b/src/openzeppelin/tests/access/test_dual_ownable.cairo @@ -0,0 +1,198 @@ +use array::ArrayTrait; +use starknet::ContractAddress; +use starknet::contract_address_const; +use starknet::testing::set_caller_address; +use starknet::testing::set_contract_address; +use zeroable::Zeroable; + +use openzeppelin::access::ownable::interface::IOwnableDispatcher; +use openzeppelin::access::ownable::interface::IOwnableCamelDispatcher; +use openzeppelin::access::ownable::interface::IOwnableDispatcherTrait; +use openzeppelin::access::ownable::interface::IOwnableCamelDispatcherTrait; +use openzeppelin::access::ownable::dual_ownable::DualCaseOwnableTrait; +use openzeppelin::access::ownable::dual_ownable::DualCaseOwnable; +use openzeppelin::tests::mocks::dual_ownable_mocks::SnakeOwnableMock; +use openzeppelin::tests::mocks::dual_ownable_mocks::CamelOwnableMock; +use openzeppelin::tests::mocks::dual_ownable_mocks::SnakeOwnablePanicMock; +use openzeppelin::tests::mocks::dual_ownable_mocks::CamelOwnablePanicMock; +use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; +use openzeppelin::tests::utils; + +/// +/// Constants +/// + +fn OWNER() -> ContractAddress { + contract_address_const::<10>() +} + +fn NEW_OWNER() -> ContractAddress { + contract_address_const::<20>() +} + +/// +/// Setup +/// + +fn setup_snake() -> (DualCaseOwnable, IOwnableDispatcher) { + let mut calldata = ArrayTrait::new(); + set_caller_address(OWNER()); + let target = utils::deploy(SnakeOwnableMock::TEST_CLASS_HASH, calldata); + (DualCaseOwnable { contract_address: target }, IOwnableDispatcher { contract_address: target }) +} + +fn setup_camel() -> (DualCaseOwnable, IOwnableCamelDispatcher) { + let mut calldata = ArrayTrait::new(); + set_caller_address(OWNER()); + let target = utils::deploy(CamelOwnableMock::TEST_CLASS_HASH, calldata); + ( + DualCaseOwnable { + contract_address: target + }, IOwnableCamelDispatcher { + contract_address: target + } + ) +} + +fn setup_non_ownable() -> DualCaseOwnable { + let calldata = ArrayTrait::new(); + set_caller_address(OWNER()); + let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); + DualCaseOwnable { contract_address: target } +} + +fn setup_ownable_panic() -> (DualCaseOwnable, DualCaseOwnable) { + set_caller_address(OWNER()); + let snake_target = utils::deploy(SnakeOwnablePanicMock::TEST_CLASS_HASH, ArrayTrait::new()); + let camel_target = utils::deploy(CamelOwnablePanicMock::TEST_CLASS_HASH, ArrayTrait::new()); + ( + DualCaseOwnable { + contract_address: snake_target + }, DualCaseOwnable { + contract_address: camel_target + } + ) +} + +/// +/// Case agnostic methods +/// + +#[test] +#[available_gas(2000000)] +fn test_dual_owner() { + let (snake_dispatcher, _) = setup_snake(); + let (camel_dispatcher, _) = setup_camel(); + assert(snake_dispatcher.owner() == OWNER(), 'Should return OWNER'); + assert(camel_dispatcher.owner() == OWNER(), 'Should return OWNER'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_owner() { + let dispatcher = setup_non_ownable(); + dispatcher.owner(); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_owner_exists_and_panics() { + let (dispatcher, _) = setup_ownable_panic(); + dispatcher.owner(); +} + +/// +/// snake_case target +/// + +#[test] +#[available_gas(2000000)] +fn test_dual_transfer_ownership() { + let (dispatcher, target) = setup_snake(); + set_contract_address(OWNER()); + dispatcher.transfer_ownership(NEW_OWNER()); + assert(target.owner() == NEW_OWNER(), 'Should be new owner'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_transfer_ownership() { + let dispatcher = setup_non_ownable(); + dispatcher.transfer_ownership(NEW_OWNER()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_transfer_ownership_exists_and_panics() { + let (dispatcher, _) = setup_ownable_panic(); + dispatcher.transfer_ownership(NEW_OWNER()); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_renounce_ownership() { + let (dispatcher, target) = setup_snake(); + set_contract_address(OWNER()); + dispatcher.renounce_ownership(); + assert(target.owner().is_zero(), 'Should be zero'); +} + + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_renounce_ownership() { + let dispatcher = setup_non_ownable(); + dispatcher.renounce_ownership(); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_renounce_ownership_exists_and_panics() { + let (dispatcher, _) = setup_ownable_panic(); + dispatcher.renounce_ownership(); +} + +/// +/// camelCase target +/// + +#[test] +#[available_gas(2000000)] +fn test_dual_transferOwnership() { + let (dispatcher, target) = setup_camel(); + set_contract_address(OWNER()); + dispatcher.transfer_ownership(NEW_OWNER()); + assert(target.owner() == NEW_OWNER(), 'Should be new owner'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_transferOwnership_exists_and_panics() { + let (_, dispatcher) = setup_ownable_panic(); + dispatcher.transfer_ownership(NEW_OWNER()); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_renounceOwnership() { + let (dispatcher, target) = setup_camel(); + set_contract_address(OWNER()); + dispatcher.renounce_ownership(); + assert(target.owner().is_zero(), 'Should be zero'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_renounceOwnership_exists_and_panics() { + let (_, dispatcher) = setup_ownable_panic(); + dispatcher.renounce_ownership(); +} + diff --git a/src/openzeppelin/tests/test_ownable.cairo b/src/openzeppelin/tests/access/test_ownable.cairo similarity index 57% rename from src/openzeppelin/tests/test_ownable.cairo rename to src/openzeppelin/tests/access/test_ownable.cairo index 424cc0546..99a717ff6 100644 --- a/src/openzeppelin/tests/test_ownable.cairo +++ b/src/openzeppelin/tests/access/test_ownable.cairo @@ -22,6 +22,10 @@ fn setup() { Ownable::initializer(); } +/// +/// initializer +/// + #[test] #[available_gas(2000000)] fn test_initializer() { @@ -30,6 +34,10 @@ fn test_initializer() { assert(Ownable::owner() == OWNER(), 'Owner should be set'); } +/// +/// transfer_ownership & transferOwnership +/// + #[test] #[available_gas(2000000)] fn test_transfer_ownership() { @@ -50,7 +58,6 @@ fn test_transfer_ownership_to_zero() { #[available_gas(2000000)] #[should_panic(expected: ('Caller is the zero address', ))] fn test_transfer_ownership_from_zero() { - assert(Ownable::owner() == ZERO(), 'Should be zero with no owner'); Ownable::transfer_ownership(OTHER()); } @@ -63,6 +70,42 @@ fn test_transfer_ownership_from_nonowner() { Ownable::transfer_ownership(OTHER()); } +#[test] +#[available_gas(2000000)] +fn test_transferOwnership() { + setup(); + Ownable::transferOwnership(OTHER()); + assert(Ownable::owner() == OTHER(), 'Should transfer ownership'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('New owner is the zero address', ))] +fn test_transferOwnership_to_zero() { + setup(); + Ownable::transferOwnership(ZERO()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Caller is the zero address', ))] +fn test_transferOwnership_from_zero() { + Ownable::transferOwnership(OTHER()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Caller is not the owner', ))] +fn test_transferOwnership_from_nonowner() { + setup(); + testing::set_caller_address(OTHER()); + Ownable::transferOwnership(OTHER()); +} + +/// +/// renounce_ownership & renounceOwnership +/// + #[test] #[available_gas(2000000)] fn test_renounce_ownership() { @@ -88,3 +131,30 @@ fn test_renounce_ownership_from_nonowner() { testing::set_caller_address(OTHER()); Ownable::renounce_ownership(); } + +#[test] +#[available_gas(2000000)] +fn test_renounceOwnership() { + setup(); + Ownable::renounceOwnership(); + assert(Ownable::owner() == ZERO(), 'Should renounce ownership'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Caller is the zero address', ))] +fn test_renounceOwnership_from_zero_address() { + setup(); + testing::set_caller_address(ZERO()); + Ownable::renounceOwnership(); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Caller is not the owner', ))] +fn test_renounceOwnership_from_nonowner() { + setup(); + testing::set_caller_address(OTHER()); + Ownable::renounceOwnership(); +} + diff --git a/src/openzeppelin/tests/mocks.cairo b/src/openzeppelin/tests/mocks.cairo index 4ab029428..0f36b9086 100644 --- a/src/openzeppelin/tests/mocks.cairo +++ b/src/openzeppelin/tests/mocks.cairo @@ -3,6 +3,7 @@ mod reentrancy_mock; mod erc721_receiver; mod erc721_panic_mock; mod mock_pausable; +mod dual_ownable_mocks; mod snake721_mock; mod camel721_mock; mod non_implementing_mock; diff --git a/src/openzeppelin/tests/mocks/dual_ownable_mocks.cairo b/src/openzeppelin/tests/mocks/dual_ownable_mocks.cairo new file mode 100644 index 000000000..f84bc195e --- /dev/null +++ b/src/openzeppelin/tests/mocks/dual_ownable_mocks.cairo @@ -0,0 +1,93 @@ +#[contract] +mod SnakeOwnableMock { + use starknet::ContractAddress; + use openzeppelin::access::ownable::Ownable; + + #[constructor] + fn constructor() { + Ownable::initializer(); + } + + #[view] + fn owner() -> ContractAddress { + Ownable::owner() + } + + #[external] + fn transfer_ownership(new_owner: ContractAddress) { + Ownable::transfer_ownership(new_owner); + } + + #[external] + fn renounce_ownership() { + Ownable::renounce_ownership(); + } +} + +#[contract] +mod CamelOwnableMock { + use starknet::ContractAddress; + use openzeppelin::access::ownable::Ownable; + + #[constructor] + fn constructor() { + Ownable::initializer(); + } + + #[view] + fn owner() -> ContractAddress { + Ownable::owner() + } + + #[external] + fn transferOwnership(newOwner: ContractAddress) { + Ownable::transferOwnership(newOwner); + } + + #[external] + fn renounceOwnership() { + Ownable::renounceOwnership(); + } +} + +#[contract] +mod SnakeOwnablePanicMock { + use starknet::ContractAddress; + + #[view] + fn owner() -> ContractAddress { + panic_with_felt252('Some error'); + Zeroable::zero() + } + + #[external] + fn transfer_ownership(new_owner: ContractAddress) { + panic_with_felt252('Some error'); + } + + #[external] + fn renounce_ownership() { + panic_with_felt252('Some error'); + } +} + +#[contract] +mod CamelOwnablePanicMock { + use starknet::ContractAddress; + + #[view] + fn owner() -> ContractAddress { + panic_with_felt252('Some error'); + Zeroable::zero() + } + + #[external] + fn transfer_ownership(new_owner: ContractAddress) { + panic_with_felt252('Some error'); + } + + #[external] + fn renounce_ownership() { + panic_with_felt252('Some error'); + } +} diff --git a/src/openzeppelin/utils/selectors.cairo b/src/openzeppelin/utils/selectors.cairo index 839bd0c12..8f56ff9d5 100644 --- a/src/openzeppelin/utils/selectors.cairo +++ b/src/openzeppelin/utils/selectors.cairo @@ -1,5 +1,17 @@ // -// ERC721 Selectors +// Ownable +// + +const owner: felt252 = 0x2016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0; +const transfer_ownership: felt252 = + 0x2a3bb1eaa05b77c4b0eeee0116a3177c6d62319dd7149ae148185d9e09de74a; +const transferOwnership: felt252 = + 0x14a390f291e2e1f29874769efdef47ddad94d76f77ff516fad206a385e8995f; +const renounce_ownership: felt252 = 0x52580a92c73f4428f1a260c5d768ef462b25955307de00f99957df119865d; +const renounceOwnership: felt252 = 0xd5d33d590e6660853069b37a2aea67c6fdaa0268626bc760350b590490feb5; + +// +// ERC721 // const name: felt252 = 0x361458367e696363fbcc70777d07ebbd2394e89fd0adcaf147faccd1d294d60; From c8fa9f945811e6c7a7b73382c589b7b154e37fa2 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Wed, 5 Jul 2023 15:53:58 -0400 Subject: [PATCH 056/246] Add camel support for access control (#626) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add camel support * add camel tests * move accesscontrol into dir * separate interface * add try_selector_with_fallback and bool impl * add const selectors * add dual accesscontrol * move access tests * add panic trait * add accesscontrol mocks * add dual accesscontrol tests * fix formatting * Apply suggestions from code review Co-authored-by: Eric Nordelo * add comment to supportsInterface * add tests for supports_interface * remove comments * alphabetize use clauses * remove util panic trait * remove unused imports * remove unused imports * remove unused import * update selector casings * Apply suggestions from code review Co-authored-by: Martín Triay * fix casing on target calls * add space to comments * remove comments, add ref with ignored tests * remove unused imports * remove selectors from constants * add selectors to selectors mod * fix formatting * reorder selectors * fix comment * update interface id * replace erc165 with src5 * fix conflicts * replace u32 with felt * add abi to interfaces * remove ignore from tests * fix formatting * fix comment * remove camel supportsInterface * fix comment consistency * Update src/openzeppelin/tests/access/test_dual_accesscontrol.cairo Co-authored-by: Eric Nordelo * add append_serde * change append to append_serde * remove unused import * remove bindings --------- Co-authored-by: Eric Nordelo Co-authored-by: Martín Triay --- src/openzeppelin/access/accesscontrol.cairo | 155 +-------- .../access/accesscontrol/accesscontrol.cairo | 194 ++++++++++++ .../accesscontrol/dual_accesscontrol.cairo | 86 +++++ .../access/accesscontrol/interface.cairo | 32 ++ src/openzeppelin/tests/access.cairo | 1 + .../tests/access/test_accesscontrol.cairo | 136 +++++++- .../access/test_dual_accesscontrol.cairo | 297 ++++++++++++++++++ src/openzeppelin/tests/mocks.cairo | 3 + .../mocks/accesscontrol_panic_mock.cairo | 69 ++++ .../mocks/camel_accesscontrol_mock.cairo | 39 +++ .../mocks/snake_accesscontrol_mock.cairo | 39 +++ src/openzeppelin/utils/selectors.cairo | 15 + src/openzeppelin/utils/serde.cairo | 10 + 13 files changed, 921 insertions(+), 155 deletions(-) create mode 100644 src/openzeppelin/access/accesscontrol/accesscontrol.cairo create mode 100644 src/openzeppelin/access/accesscontrol/dual_accesscontrol.cairo create mode 100644 src/openzeppelin/access/accesscontrol/interface.cairo create mode 100644 src/openzeppelin/tests/access/test_dual_accesscontrol.cairo create mode 100644 src/openzeppelin/tests/mocks/accesscontrol_panic_mock.cairo create mode 100644 src/openzeppelin/tests/mocks/camel_accesscontrol_mock.cairo create mode 100644 src/openzeppelin/tests/mocks/snake_accesscontrol_mock.cairo diff --git a/src/openzeppelin/access/accesscontrol.cairo b/src/openzeppelin/access/accesscontrol.cairo index 43b483585..025142e68 100644 --- a/src/openzeppelin/access/accesscontrol.cairo +++ b/src/openzeppelin/access/accesscontrol.cairo @@ -1,152 +1,7 @@ -use starknet::ContractAddress; +mod accesscontrol; +use accesscontrol::AccessControl; -const DEFAULT_ADMIN_ROLE: felt252 = 0; -const IACCESSCONTROL_ID: felt252 = - 0x23700be02858dbe2ac4dc9c9f66d0b6b0ed81ec7f970ca6844500a56ff61751; - -#[abi] -trait IAccessControl { - fn has_role(role: felt252, account: ContractAddress) -> bool; - fn get_role_admin(role: felt252) -> felt252; - fn grant_role(role: felt252, account: ContractAddress); - fn revoke_role(role: felt252, account: ContractAddress); - fn renounce_role(role: felt252, account: ContractAddress); -} - -#[contract] -mod AccessControl { - use super::IAccessControl; - use super::DEFAULT_ADMIN_ROLE; - use super::IACCESSCONTROL_ID; - use openzeppelin::introspection::src5::SRC5; - use starknet::ContractAddress; - use starknet::get_caller_address; - - struct Storage { - role_admin: LegacyMap, - role_members: LegacyMap<(felt252, ContractAddress), bool>, - } - - /// Emitted when `account` is granted `role`. - /// - /// `sender` is the account that originated the contract call, an admin role - /// bearer (except if `_grant_role` is called during initialization from the constructor). - #[event] - fn RoleGranted(role: felt252, account: ContractAddress, sender: ContractAddress) {} - - /// Emitted when `account` is revoked `role`. - /// - /// `sender` is the account that originated the contract call: - /// - If using `revoke_role`, it is the admin role bearer. - /// - If using `renounce_role`, it is the role bearer (i.e. `account`). - #[event] - fn RoleRevoked(role: felt252, account: ContractAddress, sender: ContractAddress) {} - - /// Emitted when `new_admin_role` is set as `role`'s admin role, replacing `previous_admin_role` - /// - /// `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite - /// {RoleAdminChanged} not being emitted signaling this. - #[event] - fn RoleAdminChanged(role: felt252, previous_admin_role: felt252, new_admin_role: felt252) {} - - impl AccessControlImpl of IAccessControl { - fn has_role(role: felt252, account: ContractAddress) -> bool { - role_members::read((role, account)) - } - - fn get_role_admin(role: felt252) -> felt252 { - role_admin::read(role) - } - - fn grant_role(role: felt252, account: ContractAddress) { - let admin = get_role_admin(role); - assert_only_role(admin); - _grant_role(role, account); - } - - fn revoke_role(role: felt252, account: ContractAddress) { - let admin: felt252 = get_role_admin(role); - assert_only_role(admin); - _revoke_role(role, account); - } - - fn renounce_role(role: felt252, account: ContractAddress) { - let caller: ContractAddress = get_caller_address(); - assert(caller == account, 'Can only renounce role for self'); - _revoke_role(role, account); - } - } +mod dual_accesscontrol; +mod interface; - #[view] - fn supports_interface(interface_id: felt252) -> bool { - SRC5::supports_interface(interface_id) - } - - #[view] - fn has_role(role: felt252, account: ContractAddress) -> bool { - AccessControlImpl::has_role(role, account) - } - - #[view] - fn get_role_admin(role: felt252) -> felt252 { - AccessControlImpl::get_role_admin(role) - } - - #[external] - fn grant_role(role: felt252, account: ContractAddress) { - AccessControlImpl::grant_role(role, account); - } - - #[external] - fn revoke_role(role: felt252, account: ContractAddress) { - AccessControlImpl::revoke_role(role, account); - } - - #[external] - fn renounce_role(role: felt252, account: ContractAddress) { - AccessControlImpl::renounce_role(role, account); - } - - #[internal] - fn initializer() { - SRC5::register_interface(IACCESSCONTROL_ID); - } - - #[internal] - fn assert_only_role(role: felt252) { - let caller: ContractAddress = get_caller_address(); - let authorized: bool = has_role(role, caller); - assert(authorized, 'Caller is missing role'); - } - - // - // WARNING - // The following internal methods are unprotected and should not be used - // outside of a contract's constructor. - // - - #[internal] - fn _grant_role(role: felt252, account: ContractAddress) { - if !has_role(role, account) { - let caller: ContractAddress = get_caller_address(); - role_members::write((role, account), true); - RoleGranted(role, account, caller); - } - } - - #[internal] - fn _revoke_role(role: felt252, account: ContractAddress) { - if has_role(role, account) { - let caller: ContractAddress = get_caller_address(); - role_members::write((role, account), false); - RoleRevoked(role, account, caller); - } - } - - #[internal] - fn _set_role_admin(role: felt252, admin_role: felt252) { - let previous_admin_role: felt252 = get_role_admin(role); - role_admin::write(role, admin_role); - RoleAdminChanged(role, previous_admin_role, admin_role); - } -} +const DEFAULT_ADMIN_ROLE: felt252 = 0; diff --git a/src/openzeppelin/access/accesscontrol/accesscontrol.cairo b/src/openzeppelin/access/accesscontrol/accesscontrol.cairo new file mode 100644 index 000000000..af4e9ce04 --- /dev/null +++ b/src/openzeppelin/access/accesscontrol/accesscontrol.cairo @@ -0,0 +1,194 @@ +#[contract] +mod AccessControl { + use openzeppelin::access::accesscontrol::interface; + use openzeppelin::introspection::src5::SRC5; + use starknet::ContractAddress; + use starknet::get_caller_address; + + struct Storage { + role_admin: LegacyMap, + role_members: LegacyMap<(felt252, ContractAddress), bool>, + } + + /// Emitted when `account` is granted `role`. + /// + /// `sender` is the account that originated the contract call, an admin role + /// bearer (except if `_grant_role` is called during initialization from the constructor). + #[event] + fn RoleGranted(role: felt252, account: ContractAddress, sender: ContractAddress) {} + + /// Emitted when `account` is revoked `role`. + /// + /// `sender` is the account that originated the contract call: + /// - If using `revoke_role`, it is the admin role bearer. + /// - If using `renounce_role`, it is the role bearer (i.e. `account`). + #[event] + fn RoleRevoked(role: felt252, account: ContractAddress, sender: ContractAddress) {} + + /// Emitted when `new_admin_role` is set as `role`'s admin role, replacing `previous_admin_role` + /// + /// `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite + /// {RoleAdminChanged} not being emitted signaling this. + #[event] + fn RoleAdminChanged(role: felt252, previous_admin_role: felt252, new_admin_role: felt252) {} + + impl AccessControlImpl of interface::IAccessControl { + fn has_role(role: felt252, account: ContractAddress) -> bool { + role_members::read((role, account)) + } + + fn get_role_admin(role: felt252) -> felt252 { + role_admin::read(role) + } + + fn grant_role(role: felt252, account: ContractAddress) { + let admin = get_role_admin(role); + assert_only_role(admin); + _grant_role(role, account); + } + + fn revoke_role(role: felt252, account: ContractAddress) { + let admin: felt252 = get_role_admin(role); + assert_only_role(admin); + _revoke_role(role, account); + } + + fn renounce_role(role: felt252, account: ContractAddress) { + let caller: ContractAddress = get_caller_address(); + assert(caller == account, 'Can only renounce role for self'); + _revoke_role(role, account); + } + } + + impl AccessControlCamelImpl of interface::IAccessControlCamel { + fn hasRole(role: felt252, account: ContractAddress) -> bool { + AccessControlImpl::has_role(role, account) + } + + fn getRoleAdmin(role: felt252) -> felt252 { + AccessControlImpl::get_role_admin(role) + } + + fn grantRole(role: felt252, account: ContractAddress) { + AccessControlImpl::grant_role(role, account); + } + + fn revokeRole(role: felt252, account: ContractAddress) { + AccessControlImpl::revoke_role(role, account); + } + + fn renounceRole(role: felt252, account: ContractAddress) { + AccessControlImpl::renounce_role(role, account); + } + } + + // + // View + // + + #[view] + fn supports_interface(interface_id: felt252) -> bool { + SRC5::supports_interface(interface_id) + } + + #[view] + fn has_role(role: felt252, account: ContractAddress) -> bool { + AccessControlImpl::has_role(role, account) + } + + #[view] + fn hasRole(role: felt252, account: ContractAddress) -> bool { + AccessControlCamelImpl::hasRole(role, account) + } + + #[view] + fn get_role_admin(role: felt252) -> felt252 { + AccessControlImpl::get_role_admin(role) + } + + #[view] + fn getRoleAdmin(role: felt252) -> felt252 { + AccessControlCamelImpl::getRoleAdmin(role) + } + + // + // External + // + + #[external] + fn grant_role(role: felt252, account: ContractAddress) { + AccessControlImpl::grant_role(role, account); + } + + #[external] + fn grantRole(role: felt252, account: ContractAddress) { + AccessControlCamelImpl::grantRole(role, account); + } + + #[external] + fn revoke_role(role: felt252, account: ContractAddress) { + AccessControlImpl::revoke_role(role, account); + } + + #[external] + fn revokeRole(role: felt252, account: ContractAddress) { + AccessControlCamelImpl::revokeRole(role, account); + } + + #[external] + fn renounce_role(role: felt252, account: ContractAddress) { + AccessControlImpl::renounce_role(role, account); + } + + #[external] + fn renounceRole(role: felt252, account: ContractAddress) { + AccessControlCamelImpl::renounceRole(role, account); + } + + // + // Internals + // + + #[internal] + fn initializer() { + SRC5::register_interface(interface::IACCESSCONTROL_ID); + } + + #[internal] + fn assert_only_role(role: felt252) { + let caller: ContractAddress = get_caller_address(); + let authorized: bool = has_role(role, caller); + assert(authorized, 'Caller is missing role'); + } + + // + // WARNING + // The following internal methods are unprotected and should not be used + // outside of a contract's constructor. + // + + #[internal] + fn _grant_role(role: felt252, account: ContractAddress) { + if !has_role(role, account) { + let caller: ContractAddress = get_caller_address(); + role_members::write((role, account), true); + RoleGranted(role, account, caller); + } + } + + #[internal] + fn _revoke_role(role: felt252, account: ContractAddress) { + if has_role(role, account) { + let caller: ContractAddress = get_caller_address(); + role_members::write((role, account), false); + RoleRevoked(role, account, caller); + } + } + + #[internal] + fn _set_role_admin(role: felt252, admin_role: felt252) { + let previous_admin_role: felt252 = get_role_admin(role); + role_admin::write(role, admin_role); + RoleAdminChanged(role, previous_admin_role, admin_role); + } +} diff --git a/src/openzeppelin/access/accesscontrol/dual_accesscontrol.cairo b/src/openzeppelin/access/accesscontrol/dual_accesscontrol.cairo new file mode 100644 index 000000000..3e9c0151a --- /dev/null +++ b/src/openzeppelin/access/accesscontrol/dual_accesscontrol.cairo @@ -0,0 +1,86 @@ +use array::ArrayTrait; +use array::SpanTrait; +use core::result::ResultTrait; +use option::OptionTrait; +use starknet::ContractAddress; +use starknet::Felt252TryIntoContractAddress; +use starknet::SyscallResultTrait; +use traits::TryInto; + +use openzeppelin::utils::Felt252TryIntoBool; +use openzeppelin::utils::selectors; +use openzeppelin::utils::serde::SerializedAppend; +use openzeppelin::utils::try_selector_with_fallback; + +#[derive(Copy, Drop)] +struct DualCaseAccessControl { + contract_address: ContractAddress +} + +trait DualCaseAccessControlTrait { + fn has_role(self: @DualCaseAccessControl, role: felt252, account: ContractAddress) -> bool; + fn get_role_admin(self: @DualCaseAccessControl, role: felt252) -> felt252; + fn grant_role(self: @DualCaseAccessControl, role: felt252, account: ContractAddress); + fn revoke_role(self: @DualCaseAccessControl, role: felt252, account: ContractAddress); + fn renounce_role(self: @DualCaseAccessControl, role: felt252, account: ContractAddress); +} + +impl DualCaseAccessControlImpl of DualCaseAccessControlTrait { + fn has_role(self: @DualCaseAccessControl, role: felt252, account: ContractAddress) -> bool { + let mut args = ArrayTrait::new(); + args.append_serde(role); + args.append_serde(account); + + (*try_selector_with_fallback( + *self.contract_address, selectors::has_role, selectors::hasRole, args.span() + ) + .unwrap_syscall() + .at(0)) + .try_into() + .unwrap() + } + + fn get_role_admin(self: @DualCaseAccessControl, role: felt252) -> felt252 { + let mut args = ArrayTrait::new(); + args.append_serde(role); + + *try_selector_with_fallback( + *self.contract_address, selectors::get_role_admin, selectors::getRoleAdmin, args.span() + ) + .unwrap_syscall() + .at(0) + } + + fn grant_role(self: @DualCaseAccessControl, role: felt252, account: ContractAddress) { + let mut args = ArrayTrait::new(); + args.append_serde(role); + args.append_serde(account); + + try_selector_with_fallback( + *self.contract_address, selectors::grant_role, selectors::grantRole, args.span() + ) + .unwrap_syscall(); + } + + fn revoke_role(self: @DualCaseAccessControl, role: felt252, account: ContractAddress) { + let mut args = ArrayTrait::new(); + args.append_serde(role); + args.append_serde(account); + + try_selector_with_fallback( + *self.contract_address, selectors::revoke_role, selectors::revokeRole, args.span() + ) + .unwrap_syscall(); + } + + fn renounce_role(self: @DualCaseAccessControl, role: felt252, account: ContractAddress) { + let mut args = ArrayTrait::new(); + args.append_serde(role); + args.append_serde(account); + + try_selector_with_fallback( + *self.contract_address, selectors::renounce_role, selectors::renounceRole, args.span() + ) + .unwrap_syscall(); + } +} diff --git a/src/openzeppelin/access/accesscontrol/interface.cairo b/src/openzeppelin/access/accesscontrol/interface.cairo new file mode 100644 index 000000000..9f333dc0f --- /dev/null +++ b/src/openzeppelin/access/accesscontrol/interface.cairo @@ -0,0 +1,32 @@ +use starknet::ContractAddress; + +const IACCESSCONTROL_ID: felt252 = + 0x23700be02858dbe2ac4dc9c9f66d0b6b0ed81ec7f970ca6844500a56ff61751; + +#[abi] +trait IAccessControl { + #[view] + fn has_role(role: felt252, account: ContractAddress) -> bool; + #[view] + fn get_role_admin(role: felt252) -> felt252; + #[external] + fn grant_role(role: felt252, account: ContractAddress); + #[external] + fn revoke_role(role: felt252, account: ContractAddress); + #[external] + fn renounce_role(role: felt252, account: ContractAddress); +} + +#[abi] +trait IAccessControlCamel { + #[view] + fn hasRole(role: felt252, account: ContractAddress) -> bool; + #[view] + fn getRoleAdmin(role: felt252) -> felt252; + #[external] + fn grantRole(role: felt252, account: ContractAddress); + #[external] + fn revokeRole(role: felt252, account: ContractAddress); + #[external] + fn renounceRole(role: felt252, account: ContractAddress); +} diff --git a/src/openzeppelin/tests/access.cairo b/src/openzeppelin/tests/access.cairo index f5857e98d..149c62b59 100644 --- a/src/openzeppelin/tests/access.cairo +++ b/src/openzeppelin/tests/access.cairo @@ -1,3 +1,4 @@ mod test_accesscontrol; +mod test_dual_accesscontrol; mod test_ownable; mod test_dual_ownable; diff --git a/src/openzeppelin/tests/access/test_accesscontrol.cairo b/src/openzeppelin/tests/access/test_accesscontrol.cairo index 16b0a41fa..241f01553 100644 --- a/src/openzeppelin/tests/access/test_accesscontrol.cairo +++ b/src/openzeppelin/tests/access/test_accesscontrol.cairo @@ -1,6 +1,6 @@ use openzeppelin::access::accesscontrol::AccessControl; use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; -use openzeppelin::access::accesscontrol::IACCESSCONTROL_ID; +use openzeppelin::access::accesscontrol::interface::IACCESSCONTROL_ID; use starknet::contract_address_const; use starknet::ContractAddress; use starknet::testing; @@ -41,7 +41,18 @@ fn test_initializer() { } // -// has_role +// supports_interface +// + +#[test] +#[available_gas(2000000)] +fn test_supports_interface() { + AccessControl::initializer(); + assert(AccessControl::supports_interface(IACCESSCONTROL_ID), 'Should support own interface'); +} + +// +// has_role & hasRole // #[test] @@ -53,6 +64,15 @@ fn test_has_role() { assert(AccessControl::has_role(ROLE, AUTHORIZED()), 'should have role'); } +#[test] +#[available_gas(2000000)] +fn test_hasRole() { + setup(); + assert(!AccessControl::hasRole(ROLE, AUTHORIZED()), 'should not have role'); + AccessControl::_grant_role(ROLE, AUTHORIZED()); + assert(AccessControl::hasRole(ROLE, AUTHORIZED()), 'should have role'); +} + // // assert_only_role @@ -87,7 +107,7 @@ fn test_assert_only_role_unauthorized_when_authorized_for_another_role() { } // -// grant_role +// grant_role & grantRole // #[test] @@ -116,8 +136,34 @@ fn test_grant_role_unauthorized() { AccessControl::grant_role(ROLE, AUTHORIZED()); } +#[test] +#[available_gas(2000000)] +fn test_grantRole() { + setup(); + AccessControl::grantRole(ROLE, AUTHORIZED()); + assert(AccessControl::hasRole(ROLE, AUTHORIZED()), 'Role should be granted'); +} + +#[test] +#[available_gas(2000000)] +fn test_grantRole_multiple_times_for_granted_role() { + setup(); + AccessControl::grantRole(ROLE, AUTHORIZED()); + AccessControl::grantRole(ROLE, AUTHORIZED()); + assert(AccessControl::hasRole(ROLE, AUTHORIZED()), 'Role should still be granted'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Caller is missing role', ))] +fn test_grantRole_unauthorized() { + setup(); + testing::set_caller_address(AUTHORIZED()); + AccessControl::grantRole(ROLE, AUTHORIZED()); +} + // -// revoke_role +// revoke_role & revokeRole // #[test] @@ -157,8 +203,45 @@ fn test_revoke_role_unauthorized() { AccessControl::revoke_role(ROLE, AUTHORIZED()); } +#[test] +#[available_gas(2000000)] +fn test_revokeRole_for_role_not_granted() { + setup(); + AccessControl::revokeRole(ROLE, AUTHORIZED()); +} + +#[test] +#[available_gas(2000000)] +fn test_revokeRole_for_granted_role() { + setup(); + AccessControl::grantRole(ROLE, AUTHORIZED()); + AccessControl::revokeRole(ROLE, AUTHORIZED()); + assert(!AccessControl::hasRole(ROLE, AUTHORIZED()), 'Role should be revoked'); +} + +#[test] +#[available_gas(2000000)] +fn test_revokeRole_multiple_times_for_granted_role() { + setup(); + AccessControl::grantRole(ROLE, AUTHORIZED()); + + AccessControl::revokeRole(ROLE, AUTHORIZED()); + AccessControl::revokeRole(ROLE, AUTHORIZED()); + assert(!AccessControl::hasRole(ROLE, AUTHORIZED()), 'Role should still be revoked'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Caller is missing role', ))] +fn test_revokeRole_unauthorized() { + setup(); + + testing::set_caller_address(OTHER()); + AccessControl::revokeRole(ROLE, AUTHORIZED()); +} + // -// renounce_role +// renounce_role & renounceRole // #[test] @@ -204,6 +287,49 @@ fn test_renounce_role_unauthorized() { AccessControl::renounce_role(ROLE, AUTHORIZED()); } +#[test] +#[available_gas(2000000)] +fn test_renounceRole_for_role_not_granted() { + setup(); + testing::set_caller_address(AUTHORIZED()); + + AccessControl::renounceRole(ROLE, AUTHORIZED()); +} + +#[test] +#[available_gas(2000000)] +fn test_renounceRole_for_granted_role() { + setup(); + AccessControl::grantRole(ROLE, AUTHORIZED()); + testing::set_caller_address(AUTHORIZED()); + + AccessControl::renounceRole(ROLE, AUTHORIZED()); + assert(!AccessControl::hasRole(ROLE, AUTHORIZED()), 'Role should be renounced'); +} + +#[test] +#[available_gas(2000000)] +fn test_renounceRole_multiple_times_for_granted_role() { + setup(); + AccessControl::grantRole(ROLE, AUTHORIZED()); + testing::set_caller_address(AUTHORIZED()); + + AccessControl::renounceRole(ROLE, AUTHORIZED()); + AccessControl::renounceRole(ROLE, AUTHORIZED()); + assert(!AccessControl::hasRole(ROLE, AUTHORIZED()), 'Role should still be renounced'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Can only renounce role for self', ))] +fn test_renounceRole_unauthorized() { + setup(); + AccessControl::grantRole(ROLE, AUTHORIZED()); + + // Admin is unauthorized caller + AccessControl::renounceRole(ROLE, AUTHORIZED()); +} + // // _set_role_admin // diff --git a/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo b/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo new file mode 100644 index 000000000..19ae46a4f --- /dev/null +++ b/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo @@ -0,0 +1,297 @@ +use array::ArrayTrait; +use starknet::ContractAddress; +use starknet::contract_address_const; +use starknet::testing::set_contract_address; + +use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; +use openzeppelin::access::accesscontrol::interface::IAccessControlDispatcher; +use openzeppelin::access::accesscontrol::interface::IAccessControlCamelDispatcher; +use openzeppelin::access::accesscontrol::interface::IAccessControlDispatcherTrait; +use openzeppelin::access::accesscontrol::interface::IAccessControlCamelDispatcherTrait; +use openzeppelin::access::accesscontrol::dual_accesscontrol::DualCaseAccessControlTrait; +use openzeppelin::access::accesscontrol::dual_accesscontrol::DualCaseAccessControl; +use openzeppelin::tests::mocks::snake_accesscontrol_mock::SnakeAccessControlMock; +use openzeppelin::tests::mocks::camel_accesscontrol_mock::CamelAccessControlMock; +use openzeppelin::tests::mocks::accesscontrol_panic_mock::SnakeAccessControlPanicMock; +use openzeppelin::tests::mocks::accesscontrol_panic_mock::CamelAccessControlPanicMock; +use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; +use openzeppelin::tests::utils; +use openzeppelin::utils::serde::SerializedAppend; + +/// +/// Constants +/// + +const ROLE: felt252 = 41; + +fn ADMIN() -> ContractAddress { + contract_address_const::<10>() +} + +fn AUTHORIZED() -> ContractAddress { + contract_address_const::<20>() +} + +/// +/// Setup +/// + +fn setup_snake() -> (DualCaseAccessControl, IAccessControlDispatcher) { + let mut calldata = ArrayTrait::new(); + calldata.append_serde(ADMIN()); + let target = utils::deploy(SnakeAccessControlMock::TEST_CLASS_HASH, calldata); + ( + DualCaseAccessControl { + contract_address: target + }, IAccessControlDispatcher { + contract_address: target + } + ) +} + +fn setup_camel() -> (DualCaseAccessControl, IAccessControlCamelDispatcher) { + let mut calldata = ArrayTrait::new(); + calldata.append_serde(ADMIN()); + let target = utils::deploy(CamelAccessControlMock::TEST_CLASS_HASH, calldata); + ( + DualCaseAccessControl { + contract_address: target + }, IAccessControlCamelDispatcher { + contract_address: target + } + ) +} + +fn setup_non_accesscontrol() -> DualCaseAccessControl { + let calldata = ArrayTrait::new(); + let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); + DualCaseAccessControl { contract_address: target } +} + +fn setup_accesscontrol_panic() -> (DualCaseAccessControl, DualCaseAccessControl) { + let snake_target = utils::deploy( + SnakeAccessControlPanicMock::TEST_CLASS_HASH, ArrayTrait::new() + ); + let camel_target = utils::deploy( + CamelAccessControlPanicMock::TEST_CLASS_HASH, ArrayTrait::new() + ); + ( + DualCaseAccessControl { + contract_address: snake_target + }, DualCaseAccessControl { + contract_address: camel_target + } + ) +} + +/// +/// snake_case target +/// + +#[test] +#[available_gas(2000000)] +fn test_dual_has_role() { + let (dispatcher, _) = setup_snake(); + assert(dispatcher.has_role(DEFAULT_ADMIN_ROLE, ADMIN()), 'Should have role'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_has_role() { + let dispatcher = setup_non_accesscontrol(); + dispatcher.has_role(DEFAULT_ADMIN_ROLE, ADMIN()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_has_role_exists_and_panics() { + let (dispatcher, _) = setup_accesscontrol_panic(); + dispatcher.has_role(DEFAULT_ADMIN_ROLE, ADMIN()); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_get_role_admin() { + let (dispatcher, _) = setup_snake(); + assert(dispatcher.get_role_admin(ROLE) == DEFAULT_ADMIN_ROLE, 'Should get admin'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_get_role_admin() { + let dispatcher = setup_non_accesscontrol(); + dispatcher.get_role_admin(ROLE); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_get_role_admin_exists_and_panics() { + let (dispatcher, _) = setup_accesscontrol_panic(); + dispatcher.get_role_admin(ROLE); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_grant_role() { + let (dispatcher, target) = setup_snake(); + set_contract_address(ADMIN()); + dispatcher.grant_role(ROLE, AUTHORIZED()); + assert(target.has_role(ROLE, AUTHORIZED()), 'Should grant role'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_grant_role() { + let dispatcher = setup_non_accesscontrol(); + dispatcher.grant_role(ROLE, AUTHORIZED()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_grant_role_exists_and_panics() { + let (dispatcher, _) = setup_accesscontrol_panic(); + dispatcher.grant_role(ROLE, AUTHORIZED()); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_revoke_role() { + let (dispatcher, target) = setup_snake(); + set_contract_address(ADMIN()); + dispatcher.revoke_role(ROLE, AUTHORIZED()); + assert(!target.has_role(ROLE, AUTHORIZED()), 'Should revoke role'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_revoke_role() { + let dispatcher = setup_non_accesscontrol(); + dispatcher.revoke_role(ROLE, AUTHORIZED()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_revoke_role_exists_and_panics() { + let (dispatcher, _) = setup_accesscontrol_panic(); + dispatcher.revoke_role(ROLE, AUTHORIZED()); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_renounce_role() { + let (dispatcher, target) = setup_snake(); + set_contract_address(ADMIN()); + dispatcher.renounce_role(DEFAULT_ADMIN_ROLE, ADMIN()); + assert(!target.has_role(DEFAULT_ADMIN_ROLE, ADMIN()), 'Should renounce role'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_renounce_role() { + let dispatcher = setup_non_accesscontrol(); + dispatcher.renounce_role(DEFAULT_ADMIN_ROLE, ADMIN()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_renounce_role_exists_and_panics() { + let (dispatcher, _) = setup_accesscontrol_panic(); + dispatcher.renounce_role(DEFAULT_ADMIN_ROLE, ADMIN()); +} + +/// +/// camelCase target +/// + +#[test] +#[available_gas(2000000)] +fn test_dual_hasRole() { + let (dispatcher, _) = setup_camel(); + assert(dispatcher.has_role(DEFAULT_ADMIN_ROLE, ADMIN()), 'Should have role'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_hasRole_exists_and_panics() { + let (_, dispatcher) = setup_accesscontrol_panic(); + dispatcher.has_role(DEFAULT_ADMIN_ROLE, ADMIN()); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_getRoleAdmin() { + let (dispatcher, _) = setup_camel(); + assert(dispatcher.get_role_admin(ROLE) == DEFAULT_ADMIN_ROLE, 'Should get admin'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_getRoleAdmin_exists_and_panics() { + let (_, dispatcher) = setup_accesscontrol_panic(); + dispatcher.get_role_admin(ROLE); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_grantRole() { + let (dispatcher, target) = setup_camel(); + set_contract_address(ADMIN()); + dispatcher.grant_role(ROLE, AUTHORIZED()); + assert(target.hasRole(ROLE, AUTHORIZED()), 'Should grant role'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_grantRole_exists_and_panics() { + let (_, dispatcher) = setup_accesscontrol_panic(); + dispatcher.grant_role(ROLE, AUTHORIZED()); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_revokeRole() { + let (dispatcher, target) = setup_camel(); + set_contract_address(ADMIN()); + dispatcher.grant_role(ROLE, AUTHORIZED()); + dispatcher.revoke_role(ROLE, AUTHORIZED()); + assert(!target.hasRole(ROLE, AUTHORIZED()), 'Should revoke role'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_revokeRole_exists_and_panics() { + let (_, dispatcher) = setup_accesscontrol_panic(); + dispatcher.revoke_role(ROLE, AUTHORIZED()); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_renounceRole() { + let (dispatcher, target) = setup_camel(); + set_contract_address(ADMIN()); + dispatcher.renounce_role(DEFAULT_ADMIN_ROLE, ADMIN()); + assert(!target.hasRole(DEFAULT_ADMIN_ROLE, ADMIN()), 'Should renounce role'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_renounceRole_exists_and_panics() { + let (_, dispatcher) = setup_accesscontrol_panic(); + dispatcher.renounce_role(DEFAULT_ADMIN_ROLE, ADMIN()); +} + diff --git a/src/openzeppelin/tests/mocks.cairo b/src/openzeppelin/tests/mocks.cairo index 0f36b9086..bb101efc0 100644 --- a/src/openzeppelin/tests/mocks.cairo +++ b/src/openzeppelin/tests/mocks.cairo @@ -3,6 +3,9 @@ mod reentrancy_mock; mod erc721_receiver; mod erc721_panic_mock; mod mock_pausable; +mod accesscontrol_panic_mock; +mod camel_accesscontrol_mock; +mod snake_accesscontrol_mock; mod dual_ownable_mocks; mod snake721_mock; mod camel721_mock; diff --git a/src/openzeppelin/tests/mocks/accesscontrol_panic_mock.cairo b/src/openzeppelin/tests/mocks/accesscontrol_panic_mock.cairo new file mode 100644 index 000000000..a1c13cf66 --- /dev/null +++ b/src/openzeppelin/tests/mocks/accesscontrol_panic_mock.cairo @@ -0,0 +1,69 @@ +// Although these modules are designed to panic, functions +// still need a valid return value. We chose: +// +// 3 for felt252 +// false for bool + +#[contract] +mod SnakeAccessControlPanicMock { + use starknet::ContractAddress; + + #[view] + fn has_role(role: felt252, account: ContractAddress) -> bool { + panic_with_felt252('Some error'); + false + } + + #[view] + fn get_role_admin(role: felt252) -> felt252 { + panic_with_felt252('Some error'); + 3 + } + + #[external] + fn grant_role(role: felt252, account: ContractAddress) { + panic_with_felt252('Some error'); + } + + #[external] + fn revoke_role(role: felt252, account: ContractAddress) { + panic_with_felt252('Some error'); + } + + #[external] + fn renounce_role(role: felt252, account: ContractAddress) { + panic_with_felt252('Some error'); + } +} + +#[contract] +mod CamelAccessControlPanicMock { + use starknet::ContractAddress; + + #[view] + fn hasRole(role: felt252, account: ContractAddress) -> bool { + panic_with_felt252('Some error'); + false + } + + #[view] + fn getRoleAdmin(role: felt252) -> felt252 { + panic_with_felt252('Some error'); + 3 + } + + #[external] + fn grantRole(role: felt252, account: ContractAddress) { + panic_with_felt252('Some error'); + } + + #[external] + fn revokeRole(role: felt252, account: ContractAddress) { + panic_with_felt252('Some error'); + } + + #[external] + fn renounceRole(role: felt252, account: ContractAddress) { + panic_with_felt252('Some error'); + } +} diff --git a/src/openzeppelin/tests/mocks/camel_accesscontrol_mock.cairo b/src/openzeppelin/tests/mocks/camel_accesscontrol_mock.cairo new file mode 100644 index 000000000..4d6cc6f7e --- /dev/null +++ b/src/openzeppelin/tests/mocks/camel_accesscontrol_mock.cairo @@ -0,0 +1,39 @@ +#[contract] +mod CamelAccessControlMock { + use starknet::ContractAddress; + use starknet::get_caller_address; + use openzeppelin::access::accesscontrol::AccessControl; + use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; + use openzeppelin::utils::serde::SpanSerde; + + #[constructor] + fn constructor(admin: ContractAddress) { + AccessControl::initializer(); + AccessControl::_grant_role(DEFAULT_ADMIN_ROLE, admin); + } + + #[view] + fn hasRole(role: felt252, account: ContractAddress) -> bool { + AccessControl::hasRole(role, account) + } + + #[view] + fn getRoleAdmin(role: felt252) -> felt252 { + AccessControl::getRoleAdmin(role) + } + + #[external] + fn grantRole(role: felt252, account: ContractAddress) { + AccessControl::grantRole(role, account); + } + + #[external] + fn revokeRole(role: felt252, account: ContractAddress) { + AccessControl::revokeRole(role, account); + } + + #[external] + fn renounceRole(role: felt252, account: ContractAddress) { + AccessControl::renounceRole(role, account); + } +} diff --git a/src/openzeppelin/tests/mocks/snake_accesscontrol_mock.cairo b/src/openzeppelin/tests/mocks/snake_accesscontrol_mock.cairo new file mode 100644 index 000000000..76c2d9f26 --- /dev/null +++ b/src/openzeppelin/tests/mocks/snake_accesscontrol_mock.cairo @@ -0,0 +1,39 @@ +#[contract] +mod SnakeAccessControlMock { + use starknet::ContractAddress; + use starknet::get_caller_address; + use openzeppelin::access::accesscontrol::AccessControl; + use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; + use openzeppelin::utils::serde::SpanSerde; + + #[constructor] + fn constructor(admin: ContractAddress) { + AccessControl::initializer(); + AccessControl::_grant_role(DEFAULT_ADMIN_ROLE, admin); + } + + #[view] + fn has_role(role: felt252, account: ContractAddress) -> bool { + AccessControl::has_role(role, account) + } + + #[view] + fn get_role_admin(role: felt252) -> felt252 { + AccessControl::get_role_admin(role) + } + + #[external] + fn grant_role(role: felt252, account: ContractAddress) { + AccessControl::grant_role(role, account); + } + + #[external] + fn revoke_role(role: felt252, account: ContractAddress) { + AccessControl::revoke_role(role, account); + } + + #[external] + fn renounce_role(role: felt252, account: ContractAddress) { + AccessControl::renounce_role(role, account); + } +} diff --git a/src/openzeppelin/utils/selectors.cairo b/src/openzeppelin/utils/selectors.cairo index 8f56ff9d5..33844ef8e 100644 --- a/src/openzeppelin/utils/selectors.cairo +++ b/src/openzeppelin/utils/selectors.cairo @@ -1,3 +1,18 @@ +// +// AccessControl +// + +const get_role_admin: felt252 = 0x302e0454f48778e0ca3a2e714a289c4e8d8e03d614b370130abb1a524a47f22; +const getRoleAdmin: felt252 = 0x2034da88f3e3f3382ab0faf97fe5f74fe34d551ddff7fda974d756bf12130a0; +const grant_role: felt252 = 0x18a2f881894a5eb15a2a00f598839abaa75bd7f1fea1a37e42779d7fbcd9cf8; +const grantRole: felt252 = 0x37322ff1aabefe50aec25a14eb84b168b7be4f2d66fbbdb5dd8135e8234c37a; +const has_role: felt252 = 0x30559321b47d576b645ed7bd24089943dd5fd3a359ecdd6fa8f05c1bab67d6b; +const hasRole: felt252 = 0x35ed3407ba17dc741dd3af821fa1548619ebcdc87c95bcea9e3bc510951fae8; +const renounce_role: felt252 = 0xd80093a4ee6a9e649f2ae3c64963d5096948d50cf4ea055500aa03a342fd43; +const renounceRole: felt252 = 0x3c4022816cd5119ac7938fd7a982062e4cacd4777b4eda6e6a8f64d9e6833; +const revoke_role: felt252 = 0x246116ed358bad337e64a4df51cb57a40929189494ad5905a39872c489136ec; +const revokeRole: felt252 = 0xa7ef1739dec1e216a0ba2987650983a3104c707ad0831a30184a3b1382dd7d; + // // Ownable // diff --git a/src/openzeppelin/utils/serde.cairo b/src/openzeppelin/utils/serde.cairo index 68c3720f0..aca966485 100644 --- a/src/openzeppelin/utils/serde.cairo +++ b/src/openzeppelin/utils/serde.cairo @@ -17,3 +17,13 @@ impl SpanSerde< Option::Some(deserialize_array_helper(ref serialized, arr, length)?.span()) } } + +trait SerializedAppend { + fn append_serde(ref self: Array, value: T); +} + +impl SerializedAppendImpl, impl TDrop: Drop> of SerializedAppend { + fn append_serde(ref self: Array, value: T) { + value.serialize(ref self); + } +} From 3a8e3e7234ac68319f75e1d6a7a4c7512c4a26b9 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Wed, 5 Jul 2023 17:41:47 -0400 Subject: [PATCH 057/246] erc20 dual dispatcher (#622) * add camel methods * update erc20 dispatcher path * refactor tests, add tests for camel methods * fix formatting * add try_selector_with_fallback and bool impl * add erc20 selectors * add abi to traits * add dual20 mocks * add panic trait * add dual20 mocks * add dual20 * add dual20 * add dual20 tests * tidy up tests * move tests to token dir * fix formatting * clean up tests * restructure token tests * remove panic trait * reorder imports * remove unused imports * fix comments * update const selector casings * remove comments * Apply suggestions from code review Co-authored-by: Eric Nordelo * change target member to contract_address * fix formatting * fix dispatcher calls * change dispatcher to dual_dispatcher * reorder use clauses * move selectors to selectors mod * fix formatting * reorder selectors * remove duplicate selectors * remove ignore * simplify dispatcher vars * fix comment * Apply suggestions from code review Co-authored-by: Eric Nordelo * fix test constants * add SerializedAppend trait * integrate append_serde * integrate append_serde and utils selector * change u256 fns to constants * change u256 fns to constants * move dual721 test to token/, apply append_serde, change u256 fn to constant * fix formatting * add append_serde * move append_serde to serde * update append_serde path * change dispatcher to IERC20 * remove unnecessary into() * remove bindings * add binding to empty arrays * fix formatting --------- Co-authored-by: Eric Nordelo --- .../access/ownable/dual_ownable.cairo | 8 +- src/openzeppelin/tests.cairo | 4 +- src/openzeppelin/tests/mocks.cairo | 4 + .../tests/mocks/camel20_mock.cairo | 58 ++ .../tests/mocks/erc20_panic.cairo | 96 ++++ .../tests/mocks/non721_mock.cairo | 7 + .../tests/mocks/snake20_mock.cairo | 58 ++ src/openzeppelin/tests/test_account.cairo | 39 +- src/openzeppelin/tests/test_erc20.cairo | 451 --------------- src/openzeppelin/tests/token.cairo | 3 + .../tests/token/test_dual20.cairo | 348 +++++++++++ .../tests/{ => token}/test_dual721.cairo | 102 ++-- src/openzeppelin/tests/token/test_erc20.cairo | 543 ++++++++++++++++++ .../tests/{ => token}/test_erc721.cairo | 155 +++-- src/openzeppelin/tests/utils.cairo | 7 +- src/openzeppelin/token/erc20.cairo | 236 +------- src/openzeppelin/token/erc20/dual20.cairo | 138 +++++ src/openzeppelin/token/erc20/erc20.cairo | 314 ++++++++++ src/openzeppelin/token/erc20/interface.cairo | 45 ++ src/openzeppelin/token/erc721/dual721.cairo | 58 +- src/openzeppelin/token/erc721/erc721.cairo | 8 +- src/openzeppelin/utils/selectors.cairo | 12 + 22 files changed, 1810 insertions(+), 884 deletions(-) create mode 100644 src/openzeppelin/tests/mocks/camel20_mock.cairo create mode 100644 src/openzeppelin/tests/mocks/erc20_panic.cairo create mode 100644 src/openzeppelin/tests/mocks/non721_mock.cairo create mode 100644 src/openzeppelin/tests/mocks/snake20_mock.cairo delete mode 100644 src/openzeppelin/tests/test_erc20.cairo create mode 100644 src/openzeppelin/tests/token.cairo create mode 100644 src/openzeppelin/tests/token/test_dual20.cairo rename src/openzeppelin/tests/{ => token}/test_dual721.cairo (83%) create mode 100644 src/openzeppelin/tests/token/test_erc20.cairo rename src/openzeppelin/tests/{ => token}/test_erc721.cairo (80%) create mode 100644 src/openzeppelin/token/erc20/dual20.cairo create mode 100644 src/openzeppelin/token/erc20/erc20.cairo create mode 100644 src/openzeppelin/token/erc20/interface.cairo diff --git a/src/openzeppelin/access/ownable/dual_ownable.cairo b/src/openzeppelin/access/ownable/dual_ownable.cairo index 840c371f5..fc7381f80 100644 --- a/src/openzeppelin/access/ownable/dual_ownable.cairo +++ b/src/openzeppelin/access/ownable/dual_ownable.cairo @@ -6,12 +6,12 @@ use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use starknet::SyscallResultTrait; use starknet::call_contract_syscall; -use traits::Into; use traits::TryInto; use openzeppelin::utils::try_selector_with_fallback; use openzeppelin::utils::Felt252TryIntoBool; use openzeppelin::utils::selectors; +use openzeppelin::utils::serde::SerializedAppend; #[derive(Copy, Drop)] struct DualCaseOwnable { @@ -26,7 +26,9 @@ trait DualCaseOwnableTrait { impl DualCaseOwnableImpl of DualCaseOwnableTrait { fn owner(self: @DualCaseOwnable) -> ContractAddress { - (*call_contract_syscall(*self.contract_address, selectors::owner, ArrayTrait::new().span()) + let args = ArrayTrait::new(); + + (*call_contract_syscall(*self.contract_address, selectors::owner, args.span()) .unwrap_syscall() .at(0)) .try_into() @@ -35,7 +37,7 @@ impl DualCaseOwnableImpl of DualCaseOwnableTrait { fn transfer_ownership(self: @DualCaseOwnable, new_owner: ContractAddress) { let mut args = ArrayTrait::new(); - args.append(new_owner.into()); + args.append_serde(new_owner); try_selector_with_fallback( *self.contract_address, diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index e50d9f319..800541185 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -2,9 +2,7 @@ mod access; mod test_reentrancyguard; mod test_src5; mod test_account; -mod test_erc20; -mod test_erc721; -mod test_dual721; +mod token; mod test_initializable; mod test_pausable; mod mocks; diff --git a/src/openzeppelin/tests/mocks.cairo b/src/openzeppelin/tests/mocks.cairo index bb101efc0..325546d0c 100644 --- a/src/openzeppelin/tests/mocks.cairo +++ b/src/openzeppelin/tests/mocks.cairo @@ -3,6 +3,10 @@ mod reentrancy_mock; mod erc721_receiver; mod erc721_panic_mock; mod mock_pausable; +mod camel20_mock; +mod snake20_mock; +mod erc20_panic; +mod non721_mock; mod accesscontrol_panic_mock; mod camel_accesscontrol_mock; mod snake_accesscontrol_mock; diff --git a/src/openzeppelin/tests/mocks/camel20_mock.cairo b/src/openzeppelin/tests/mocks/camel20_mock.cairo new file mode 100644 index 000000000..3b9bd1048 --- /dev/null +++ b/src/openzeppelin/tests/mocks/camel20_mock.cairo @@ -0,0 +1,58 @@ +#[contract] +mod CamelERC20Mock { + use starknet::ContractAddress; + use openzeppelin::token::erc20::ERC20; + + #[constructor] + fn constructor( + name: felt252, symbol: felt252, initial_supply: u256, recipient: ContractAddress + ) { + ERC20::initializer(name, symbol); + ERC20::_mint(recipient, initial_supply); + } + + #[view] + fn name() -> felt252 { + ERC20::name() + } + + #[view] + fn symbol() -> felt252 { + ERC20::symbol() + } + + #[view] + fn decimals() -> u8 { + ERC20::decimals() + } + + #[view] + fn totalSupply() -> u256 { + ERC20::totalSupply() + } + + #[view] + fn balanceOf(account: ContractAddress) -> u256 { + ERC20::balanceOf(account) + } + + #[view] + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { + ERC20::allowance(owner, spender) + } + + #[external] + fn transfer(recipient: ContractAddress, amount: u256) -> bool { + ERC20::transfer(recipient, amount) + } + + #[external] + fn transferFrom(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { + ERC20::transferFrom(sender, recipient, amount) + } + + #[external] + fn approve(spender: ContractAddress, amount: u256) -> bool { + ERC20::approve(spender, amount) + } +} diff --git a/src/openzeppelin/tests/mocks/erc20_panic.cairo b/src/openzeppelin/tests/mocks/erc20_panic.cairo new file mode 100644 index 000000000..89389bc74 --- /dev/null +++ b/src/openzeppelin/tests/mocks/erc20_panic.cairo @@ -0,0 +1,96 @@ +// Although these modules are designed to panic, functions +// still need a valid return value. We chose: +// +// 3 for felt252 and u8 +// zero for ContractAddress +// false for bool +// u256 { 3, 3 } for u256 + +#[contract] +mod SnakeERC20Panic { + use starknet::ContractAddress; + + /// + /// Agnostic + /// + + #[view] + fn name() -> felt252 { + panic_with_felt252('Some error'); + 3 + } + + #[view] + fn symbol() -> felt252 { + panic_with_felt252('Some error'); + 3 + } + + #[view] + fn decimals() -> u8 { + panic_with_felt252('Some error'); + 3_u8 + } + + #[view] + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { + panic_with_felt252('Some error'); + u256 { low: 3, high: 3 } + } + + #[external] + fn transfer(recipient: ContractAddress, amount: u256) -> bool { + panic_with_felt252('Some error'); + false + } + + #[external] + fn approve(to: ContractAddress, token_id: u256) -> bool { + panic_with_felt252('Some error'); + false + } + + /// + /// Snake + /// + + #[view] + fn total_supply() -> u256 { + panic_with_felt252('Some error'); + u256 { low: 3, high: 3 } + } + + #[view] + fn balance_of(account: ContractAddress) -> u256 { + panic_with_felt252('Some error'); + u256 { low: 3, high: 3 } + } + + #[external] + fn transfer_from(from: ContractAddress, to: ContractAddress, amount: u256) -> bool { + panic_with_felt252('Some error'); + false + } +} + +#[contract] +mod CamelERC20Panic { + use starknet::ContractAddress; + + #[view] + fn totalSupply() -> u256 { + panic_with_felt252('Some error'); + u256 { low: 3, high: 3 } + } + + #[view] + fn balanceOf(account: ContractAddress) -> u256 { + panic_with_felt252('Some error'); + u256 { low: 3, high: 3 } + } + + #[external] + fn transferFrom(sender: ContractAddress, recipient: ContractAddress, amount: u256) { + panic_with_felt252('Some error'); + } +} diff --git a/src/openzeppelin/tests/mocks/non721_mock.cairo b/src/openzeppelin/tests/mocks/non721_mock.cairo new file mode 100644 index 000000000..05c7d140c --- /dev/null +++ b/src/openzeppelin/tests/mocks/non721_mock.cairo @@ -0,0 +1,7 @@ +#[contract] +mod NonERC721 { + #[view] + fn nope() -> bool { + false + } +} diff --git a/src/openzeppelin/tests/mocks/snake20_mock.cairo b/src/openzeppelin/tests/mocks/snake20_mock.cairo new file mode 100644 index 000000000..dd5a034f7 --- /dev/null +++ b/src/openzeppelin/tests/mocks/snake20_mock.cairo @@ -0,0 +1,58 @@ +#[contract] +mod SnakeERC20Mock { + use starknet::ContractAddress; + use openzeppelin::token::erc20::ERC20; + + #[constructor] + fn constructor( + name: felt252, symbol: felt252, initial_supply: u256, recipient: ContractAddress + ) { + ERC20::initializer(name, symbol); + ERC20::_mint(recipient, initial_supply); + } + + #[view] + fn name() -> felt252 { + ERC20::name() + } + + #[view] + fn symbol() -> felt252 { + ERC20::symbol() + } + + #[view] + fn decimals() -> u8 { + ERC20::decimals() + } + + #[view] + fn total_supply() -> u256 { + ERC20::total_supply() + } + + #[view] + fn balance_of(account: ContractAddress) -> u256 { + ERC20::balance_of(account) + } + + #[view] + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { + ERC20::allowance(owner, spender) + } + + #[external] + fn transfer(recipient: ContractAddress, amount: u256) -> bool { + ERC20::transfer(recipient, amount) + } + + #[external] + fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { + ERC20::transfer_from(sender, recipient, amount) + } + + #[external] + fn approve(spender: ContractAddress, amount: u256) -> bool { + ERC20::approve(spender, amount) + } +} diff --git a/src/openzeppelin/tests/test_account.cairo b/src/openzeppelin/tests/test_account.cairo index 5a833327f..d39fa796c 100644 --- a/src/openzeppelin/tests/test_account.cairo +++ b/src/openzeppelin/tests/test_account.cairo @@ -17,12 +17,13 @@ use openzeppelin::account::TRANSACTION_VERSION; use openzeppelin::introspection::src5::ISRC5_ID; use openzeppelin::tests::utils; use openzeppelin::token::erc20::ERC20; -use openzeppelin::token::erc20::IERC20Dispatcher; -use openzeppelin::token::erc20::IERC20DispatcherTrait; +use openzeppelin::token::erc20::interface::IERC20Dispatcher; +use openzeppelin::token::erc20::interface::IERC20DispatcherTrait; +use openzeppelin::utils::selectors; +use openzeppelin::utils::serde::SerializedAppend; const PUBLIC_KEY: felt252 = 0x333333; const NEW_PUBKEY: felt252 = 0x789789; -const TRANSFER_SELECTOR: felt252 = 0x83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e; const SALT: felt252 = 123; #[derive(Drop)] @@ -81,11 +82,10 @@ fn deploy_erc20(recipient: ContractAddress, initial_supply: u256) -> IERC20Dispa let symbol = 0; let mut calldata = ArrayTrait::new(); - calldata.append(name); - calldata.append(symbol); - calldata.append(initial_supply.low.into()); - calldata.append(initial_supply.high.into()); - calldata.append(recipient.into()); + calldata.append_serde(name); + calldata.append_serde(symbol); + calldata.append_serde(initial_supply); + calldata.append_serde(recipient); let address = utils::deploy(ERC20::TEST_CLASS_HASH, calldata); IERC20Dispatcher { contract_address: address } @@ -241,10 +241,11 @@ fn test_execute_with_version(version: Option) { // Craft call and add to calls array let mut calldata = ArrayTrait::new(); let amount: u256 = 200; - calldata.append(recipient.into()); - calldata.append(amount.low.into()); - calldata.append(amount.high.into()); - let call = Call { to: erc20.contract_address, selector: TRANSFER_SELECTOR, calldata: calldata }; + calldata.append_serde(recipient); + calldata.append_serde(amount); + let call = Call { + to: erc20.contract_address, selector: selectors::transfer, calldata: calldata + }; let mut calls = ArrayTrait::new(); calls.append(call); @@ -318,21 +319,19 @@ fn test_multicall() { // Craft call1 let mut calldata1 = ArrayTrait::new(); let amount1: u256 = 300; - calldata1.append(recipient1.into()); - calldata1.append(amount1.low.into()); - calldata1.append(amount1.high.into()); + calldata1.append_serde(recipient1); + calldata1.append_serde(amount1); let call1 = Call { - to: erc20.contract_address, selector: TRANSFER_SELECTOR, calldata: calldata1 + to: erc20.contract_address, selector: selectors::transfer, calldata: calldata1 }; // Craft call2 let mut calldata2 = ArrayTrait::new(); let amount2: u256 = 500; - calldata2.append(recipient2.into()); - calldata2.append(amount2.low.into()); - calldata2.append(amount2.high.into()); + calldata2.append_serde(recipient2); + calldata2.append_serde(amount2); let call2 = Call { - to: erc20.contract_address, selector: TRANSFER_SELECTOR, calldata: calldata2 + to: erc20.contract_address, selector: selectors::transfer, calldata: calldata2 }; // Bundle calls and exeute diff --git a/src/openzeppelin/tests/test_erc20.cairo b/src/openzeppelin/tests/test_erc20.cairo deleted file mode 100644 index 5c5615990..000000000 --- a/src/openzeppelin/tests/test_erc20.cairo +++ /dev/null @@ -1,451 +0,0 @@ -use openzeppelin::token::erc20::ERC20; -use starknet::contract_address_const; -use starknet::ContractAddress; -use starknet::testing::set_caller_address; -use integer::u256; -use integer::u256_from_felt252; -use integer::BoundedInt; -use traits::Into; - -// -// Constants -// - -const NAME: felt252 = 111; -const SYMBOL: felt252 = 222; - -// -// Helper functions -// - -fn setup() -> (ContractAddress, u256) { - let initial_supply: u256 = u256_from_felt252(2000); - let account: ContractAddress = contract_address_const::<1>(); - // Set account as default caller - set_caller_address(account); - - ERC20::constructor(NAME, SYMBOL, initial_supply, account); - (account, initial_supply) -} - -fn set_caller_as_zero() { - set_caller_address(contract_address_const::<0>()); -} - -// -// Tests -// - -#[test] -#[available_gas(2000000)] -fn test_initializer() { - ERC20::initializer(NAME, SYMBOL); - - assert(ERC20::name() == NAME, 'Name should be NAME'); - assert(ERC20::symbol() == SYMBOL, 'Symbol should be SYMBOL'); - assert(ERC20::decimals() == 18_u8, 'Decimals should be 18'); - assert(ERC20::total_supply() == u256_from_felt252(0), 'Supply should eq 0'); -} - - -#[test] -#[available_gas(2000000)] -fn test_constructor() { - let initial_supply: u256 = u256_from_felt252(2000); - let account: ContractAddress = contract_address_const::<1>(); - let decimals: u8 = 18_u8; - - ERC20::constructor(NAME, SYMBOL, initial_supply, account); - - let owner_balance: u256 = ERC20::balance_of(account); - assert(owner_balance == initial_supply, 'Should eq inital_supply'); - - assert(ERC20::total_supply() == initial_supply, 'Should eq inital_supply'); - assert(ERC20::name() == NAME, 'Name should be NAME'); - assert(ERC20::symbol() == SYMBOL, 'Symbol should be SYMBOL'); - assert(ERC20::decimals() == decimals, 'Decimals should be 18'); -} - -#[test] -#[available_gas(2000000)] -fn test_approve() { - let (owner, supply) = setup(); - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt252(100); - - let success: bool = ERC20::approve(spender, amount); - assert(success, 'Should return true'); - assert(ERC20::allowance(owner, spender) == amount, 'Spender not approved correctly'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('ERC20: approve from 0', ))] -fn test_approve_from_zero() { - let (owner, supply) = setup(); - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt252(100); - - set_caller_as_zero(); - - ERC20::approve(spender, amount); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('ERC20: approve to 0', ))] -fn test_approve_to_zero() { - let (owner, supply) = setup(); - let spender: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt252(100); - - ERC20::approve(spender, amount); -} - -#[test] -#[available_gas(2000000)] -fn test__approve() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt252(100); - - ERC20::_approve(owner, spender, amount); - assert(ERC20::allowance(owner, spender) == amount, 'Spender not approved correctly'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('ERC20: approve from 0', ))] -fn test__approve_from_zero() { - let owner: ContractAddress = contract_address_const::<0>(); - let spender: ContractAddress = contract_address_const::<1>(); - let amount: u256 = u256_from_felt252(100); - ERC20::_approve(owner, spender, amount); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('ERC20: approve to 0', ))] -fn test__approve_to_zero() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt252(100); - ERC20::_approve(owner, spender, amount); -} - -#[test] -#[available_gas(2000000)] -fn test_transfer() { - let (sender, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt252(100); - let success: bool = ERC20::transfer(recipient, amount); - - assert(success, 'Should return true'); - assert(ERC20::balance_of(recipient) == amount, 'Balance should eq amount'); - assert(ERC20::balance_of(sender) == supply - amount, 'Should eq supply - amount'); - assert(ERC20::total_supply() == supply, 'Total supply should not change'); -} - -#[test] -#[available_gas(2000000)] -fn test__transfer() { - let (sender, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt252(100); - ERC20::_transfer(sender, recipient, amount); - - assert(ERC20::balance_of(recipient) == amount, 'Balance should eq amount'); - assert(ERC20::balance_of(sender) == supply - amount, 'Should eq supply - amount'); - assert(ERC20::total_supply() == supply, 'Total supply should not change'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('u256_sub Overflow', ))] -fn test__transfer_not_enough_balance() { - let (sender, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<2>(); - let amount: u256 = supply + u256_from_felt252(1); - ERC20::_transfer(sender, recipient, amount); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('ERC20: transfer from 0', ))] -fn test__transfer_from_zero() { - let sender: ContractAddress = contract_address_const::<0>(); - let recipient: ContractAddress = contract_address_const::<1>(); - let amount: u256 = u256_from_felt252(100); - ERC20::_transfer(sender, recipient, amount); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('ERC20: transfer to 0', ))] -fn test__transfer_to_zero() { - let (sender, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt252(100); - ERC20::_transfer(sender, recipient, amount); -} - -#[test] -#[available_gas(2000000)] -fn test_transfer_from() { - let (owner, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<2>(); - let spender: ContractAddress = contract_address_const::<3>(); - let amount: u256 = u256_from_felt252(100); - - ERC20::approve(spender, amount); - - set_caller_address(spender); - - let success: bool = ERC20::transfer_from(owner, recipient, amount); - assert(success, 'Should return true'); - - // Will dangle without setting as a var - let spender_allowance: u256 = ERC20::allowance(owner, spender); - - assert(ERC20::balance_of(recipient) == amount, 'Should eq amount'); - assert(ERC20::balance_of(owner) == supply - amount, 'Should eq suppy - amount'); - assert(spender_allowance == u256_from_felt252(0), 'Should eq 0'); - assert(ERC20::total_supply() == supply, 'Total supply should not change'); -} - -#[test] -#[available_gas(2000000)] -fn test_transfer_from_doesnt_consume_infinite_allowance() { - let (owner, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<2>(); - let spender: ContractAddress = contract_address_const::<3>(); - let amount: u256 = u256_from_felt252(100); - - ERC20::approve(spender, BoundedInt::max()); - - set_caller_address(spender); - ERC20::transfer_from(owner, recipient, amount); - - let spender_allowance: u256 = ERC20::allowance(owner, spender); - assert(spender_allowance == BoundedInt::max(), 'Allowance should not change'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('u256_sub Overflow', ))] -fn test_transfer_from_greater_than_allowance() { - let (owner, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<2>(); - let spender: ContractAddress = contract_address_const::<3>(); - let amount: u256 = u256_from_felt252(100); - let amount_plus_one: u256 = amount + u256_from_felt252(1); - - ERC20::approve(spender, amount); - - set_caller_address(spender); - - ERC20::transfer_from(owner, recipient, amount_plus_one); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('ERC20: transfer to 0', ))] -fn test_transfer_from_to_zero_address() { - let (owner, supply) = setup(); - - let recipient: ContractAddress = contract_address_const::<0>(); - let spender: ContractAddress = contract_address_const::<3>(); - let amount: u256 = u256_from_felt252(100); - - ERC20::approve(spender, amount); - - set_caller_address(spender); - - ERC20::transfer_from(owner, recipient, amount); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('u256_sub Overflow', ))] -fn test_transfer_from_from_zero_address() { - let (owner, supply) = setup(); - - let zero_address: ContractAddress = contract_address_const::<0>(); - let recipient: ContractAddress = contract_address_const::<2>(); - let spender: ContractAddress = contract_address_const::<3>(); - let amount: u256 = u256_from_felt252(100); - - set_caller_address(zero_address); - - ERC20::transfer_from(owner, recipient, amount); -} - -#[test] -#[available_gas(2000000)] -fn test_increase_allowance() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt252(100); - - ERC20::approve(spender, amount); - let success: bool = ERC20::increase_allowance(spender, amount); - assert(success, 'Should return true'); - - let spender_allowance: u256 = ERC20::allowance(owner, spender); - assert(spender_allowance == amount + amount, 'Should be amount * 2'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('ERC20: approve to 0', ))] -fn test_increase_allowance_to_zero_address() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt252(100); - - ERC20::increase_allowance(spender, amount); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('ERC20: approve from 0', ))] -fn test_increase_allowance_from_zero_address() { - let (owner, supply) = setup(); - - let zero_address: ContractAddress = contract_address_const::<0>(); - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt252(100); - - set_caller_address(zero_address); - - ERC20::increase_allowance(spender, amount); -} - -#[test] -#[available_gas(2000000)] -fn test_decrease_allowance() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt252(100); - - ERC20::approve(spender, amount); - let success: bool = ERC20::decrease_allowance(spender, amount); - assert(success, 'Should return true'); - - let spender_allowance: u256 = ERC20::allowance(owner, spender); - assert(spender_allowance == amount - amount, 'Should be 0'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('u256_sub Overflow', ))] -fn test_decrease_allowance_to_zero_address() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt252(100); - - ERC20::decrease_allowance(spender, amount); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('u256_sub Overflow', ))] -fn test_decrease_allowance_from_zero_address() { - let (owner, supply) = setup(); - - let zero_address: ContractAddress = contract_address_const::<0>(); - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt252(100); - - set_caller_address(zero_address); - - ERC20::decrease_allowance(spender, amount); -} - -#[test] -#[available_gas(2000000)] -fn test__spend_allowance_not_unlimited() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt252(100); - - ERC20::_approve(owner, spender, supply); - ERC20::_spend_allowance(owner, spender, amount); - assert(ERC20::allowance(owner, spender) == supply - amount, 'Should eq supply - amount'); -} - -#[test] -#[available_gas(2000000)] -fn test__spend_allowance_unlimited() { - let (owner, supply) = setup(); - - let spender: ContractAddress = contract_address_const::<2>(); - let max_minus_one: u256 = BoundedInt::max() - 1.into(); - - ERC20::_approve(owner, spender, BoundedInt::max()); - ERC20::_spend_allowance(owner, spender, max_minus_one); - - assert(ERC20::allowance(owner, spender) == BoundedInt::max(), 'Allowance should not change'); -} - -#[test] -#[available_gas(2000000)] -fn test__mint() { - let minter: ContractAddress = contract_address_const::<2>(); - let amount: u256 = u256_from_felt252(100); - - ERC20::_mint(minter, amount); - - let minter_balance: u256 = ERC20::balance_of(minter); - assert(minter_balance == amount, 'Should eq amount'); - - assert(ERC20::total_supply() == amount, 'Should eq total supply'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('ERC20: mint to 0', ))] -fn test__mint_to_zero() { - let minter: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt252(100); - - ERC20::_mint(minter, amount); -} - -#[test] -#[available_gas(2000000)] -fn test__burn() { - let (owner, supply) = setup(); - - let amount: u256 = u256_from_felt252(100); - ERC20::_burn(owner, amount); - - assert(ERC20::total_supply() == supply - amount, 'Should eq supply - amount'); - assert(ERC20::balance_of(owner) == supply - amount, 'Should eq supply - amount'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('ERC20: burn from 0', ))] -fn test__burn_from_zero() { - setup(); - let zero_address: ContractAddress = contract_address_const::<0>(); - let amount: u256 = u256_from_felt252(100); - - ERC20::_burn(zero_address, amount); -} diff --git a/src/openzeppelin/tests/token.cairo b/src/openzeppelin/tests/token.cairo new file mode 100644 index 000000000..a072d4a08 --- /dev/null +++ b/src/openzeppelin/tests/token.cairo @@ -0,0 +1,3 @@ +mod test_dual20; +mod test_erc20; +mod test_erc721; diff --git a/src/openzeppelin/tests/token/test_dual20.cairo b/src/openzeppelin/tests/token/test_dual20.cairo new file mode 100644 index 000000000..0da4ea5c7 --- /dev/null +++ b/src/openzeppelin/tests/token/test_dual20.cairo @@ -0,0 +1,348 @@ +use array::ArrayTrait; +use starknet::ContractAddress; +use starknet::contract_address_const; +use starknet::testing::set_contract_address; + +use openzeppelin::tests::mocks::camel20_mock::CamelERC20Mock; +use openzeppelin::tests::mocks::erc20_panic::SnakeERC20Panic; +use openzeppelin::tests::mocks::erc20_panic::CamelERC20Panic; +use openzeppelin::tests::mocks::non721_mock::NonERC721; +use openzeppelin::tests::mocks::snake20_mock::SnakeERC20Mock; +use openzeppelin::token::erc20::dual20::DualERC20; +use openzeppelin::token::erc20::dual20::DualERC20Trait; +use openzeppelin::token::erc20::interface::IERC20CamelDispatcher; +use openzeppelin::token::erc20::interface::IERC20CamelDispatcherTrait; +use openzeppelin::token::erc20::interface::IERC20Dispatcher; +use openzeppelin::token::erc20::interface::IERC20DispatcherTrait; +use openzeppelin::tests::utils; +use openzeppelin::utils::serde::SerializedAppend; + +/// +/// Constants +/// + +const NAME: felt252 = 111; +const SYMBOL: felt252 = 222; +const DECIMALS: u8 = 18_u8; +const SUPPLY: u256 = 2000; +const VALUE: u256 = 300; + +fn OWNER() -> ContractAddress { + contract_address_const::<10>() +} +fn SPENDER() -> ContractAddress { + contract_address_const::<20>() +} +fn RECIPIENT() -> ContractAddress { + contract_address_const::<30>() +} +fn OPERATOR() -> ContractAddress { + contract_address_const::<40>() +} + +/// +/// Setup +/// + +fn setup_snake() -> (DualERC20, IERC20Dispatcher) { + let mut calldata = ArrayTrait::new(); + calldata.append_serde(NAME); + calldata.append_serde(SYMBOL); + calldata.append_serde(SUPPLY); + calldata.append_serde(OWNER()); + let target = utils::deploy(SnakeERC20Mock::TEST_CLASS_HASH, calldata); + (DualERC20 { contract_address: target }, IERC20Dispatcher { contract_address: target }) +} + +fn setup_camel() -> (DualERC20, IERC20CamelDispatcher) { + let mut calldata = ArrayTrait::new(); + calldata.append_serde(NAME); + calldata.append_serde(SYMBOL); + calldata.append_serde(SUPPLY); + calldata.append_serde(OWNER()); + let target = utils::deploy(CamelERC20Mock::TEST_CLASS_HASH, calldata); + (DualERC20 { contract_address: target }, IERC20CamelDispatcher { contract_address: target }) +} + +fn setup_non_erc20() -> DualERC20 { + let calldata = ArrayTrait::new(); + let target = utils::deploy(NonERC721::TEST_CLASS_HASH, calldata); + DualERC20 { contract_address: target } +} + +fn setup_erc20_panic() -> (DualERC20, DualERC20) { + let snake_target = utils::deploy(SnakeERC20Panic::TEST_CLASS_HASH, ArrayTrait::new()); + let camel_target = utils::deploy(CamelERC20Panic::TEST_CLASS_HASH, ArrayTrait::new()); + (DualERC20 { contract_address: snake_target }, DualERC20 { contract_address: camel_target }) +} + +/// +/// Case agnostic methods +/// + +#[test] +#[available_gas(2000000)] +fn test_dual_name() { + let (snake_dispatcher, _) = setup_snake(); + let (camel_dispatcher, _) = setup_camel(); + assert(snake_dispatcher.name() == NAME, 'Should return name'); + assert(camel_dispatcher.name() == NAME, 'Should return name'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_name() { + let dispatcher = setup_non_erc20(); + dispatcher.name(); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_name_exists_and_panics() { + let (dispatcher, _) = setup_erc20_panic(); + dispatcher.name(); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_symbol() { + let (snake_dispatcher, _) = setup_snake(); + let (camel_dispatcher, _) = setup_camel(); + assert(snake_dispatcher.symbol() == SYMBOL, 'Should return symbol'); + assert(camel_dispatcher.symbol() == SYMBOL, 'Should return symbol'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_symbol() { + let dispatcher = setup_non_erc20(); + dispatcher.symbol(); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_symbol_exists_and_panics() { + let (dispatcher, _) = setup_erc20_panic(); + dispatcher.symbol(); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_decimals() { + let (snake_dispatcher, _) = setup_snake(); + let (camel_dispatcher, _) = setup_camel(); + assert(snake_dispatcher.decimals() == DECIMALS, 'Should return symbol'); + assert(camel_dispatcher.decimals() == DECIMALS, 'Should return symbol'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_decimals() { + let dispatcher = setup_non_erc20(); + dispatcher.decimals(); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_decimals_exists_and_panics() { + let (dispatcher, _) = setup_erc20_panic(); + dispatcher.decimals(); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_transfer() { + let (snake_dispatcher, snake_target) = setup_snake(); + set_contract_address(OWNER()); + assert(snake_dispatcher.transfer(RECIPIENT(), VALUE), 'Should return true'); + assert(snake_target.balance_of(RECIPIENT()) == VALUE, 'Should equal VALUE'); + + let (camel_dispatcher, camel_target) = setup_camel(); + set_contract_address(OWNER()); + assert(camel_dispatcher.transfer(RECIPIENT(), VALUE), 'Should return true'); + assert(camel_target.balanceOf(RECIPIENT()) == VALUE, 'Should equal VALUE'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_transfer() { + let dispatcher = setup_non_erc20(); + dispatcher.transfer(RECIPIENT(), VALUE); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_transfer_exists_and_panics() { + let (dispatcher, _) = setup_erc20_panic(); + dispatcher.transfer(RECIPIENT(), VALUE); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_approve() { + let (snake_dispatcher, snake_target) = setup_snake(); + set_contract_address(OWNER()); + assert(snake_dispatcher.approve(SPENDER(), VALUE), 'Should return true'); + assert(snake_target.allowance(OWNER(), SPENDER()) == VALUE, 'Allowance should equal VALUE'); + + let (camel_dispatcher, camel_target) = setup_camel(); + set_contract_address(OWNER()); + assert(camel_dispatcher.approve(SPENDER(), VALUE), 'Should return true'); + assert(camel_target.allowance(OWNER(), SPENDER()) == VALUE, 'Allowance should equal VALUE'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_approve() { + let dispatcher = setup_non_erc20(); + dispatcher.approve(SPENDER(), VALUE); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_approve_exists_and_panics() { + let (dispatcher, _) = setup_erc20_panic(); + dispatcher.approve(SPENDER(), VALUE); +} + +/// +/// snake_case target +/// + +#[test] +#[available_gas(2000000)] +fn test_dual_total_supply() { + let (dispatcher, _) = setup_snake(); + assert(dispatcher.total_supply() == SUPPLY, 'Should return balance'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_total_supply() { + let dispatcher = setup_non_erc20(); + dispatcher.total_supply(); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_total_supply_exists_and_panics() { + let (dispatcher, _) = setup_erc20_panic(); + dispatcher.total_supply(); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_balance_of() { + let (dispatcher, _) = setup_snake(); + assert(dispatcher.balance_of(OWNER()) == SUPPLY, 'Should return balance'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_balance_of() { + let dispatcher = setup_non_erc20(); + dispatcher.balance_of(OWNER()); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_balance_of_exists_and_panics() { + let (dispatcher, _) = setup_erc20_panic(); + dispatcher.balance_of(OWNER()); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_transfer_from() { + let (dispatcher, target) = setup_snake(); + set_contract_address(OWNER()); + target.approve(OPERATOR(), VALUE); + + set_contract_address(OPERATOR()); + dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); + assert(target.balance_of(RECIPIENT()) == VALUE, 'Should transfer VALUE'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_transfer_from() { + let dispatcher = setup_non_erc20(); + dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_transfer_from_exists_and_panics() { + let (dispatcher, _) = setup_erc20_panic(); + dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); +} + +/// +/// camelCase target +/// + +#[test] +#[available_gas(2000000)] +fn test_dual_totalSupply() { + let (dispatcher, _) = setup_camel(); + assert(dispatcher.total_supply() == SUPPLY, 'Should return supply'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_totalSupply_exists_and_panics() { + let (_, dispatcher) = setup_erc20_panic(); + dispatcher.total_supply(); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_balanceOf() { + let (dispatcher, _) = setup_camel(); + assert(dispatcher.balance_of(OWNER()) == SUPPLY, 'Should return balance'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_balanceOf_exists_and_panics() { + let (_, dispatcher) = setup_erc20_panic(); + dispatcher.balance_of(OWNER()); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_transferFrom() { + let (dispatcher, target) = setup_camel(); + set_contract_address(OWNER()); + target.approve(OPERATOR(), VALUE); + + set_contract_address(OPERATOR()); + dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); + assert(target.balanceOf(RECIPIENT()) == VALUE, 'Should transfer VALUE'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_transferFrom_exists_and_panics() { + let (_, dispatcher) = setup_erc20_panic(); + dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); +} diff --git a/src/openzeppelin/tests/test_dual721.cairo b/src/openzeppelin/tests/token/test_dual721.cairo similarity index 83% rename from src/openzeppelin/tests/test_dual721.cairo rename to src/openzeppelin/tests/token/test_dual721.cairo index 98d7459f9..0ef8a0521 100644 --- a/src/openzeppelin/tests/test_dual721.cairo +++ b/src/openzeppelin/tests/token/test_dual721.cairo @@ -1,4 +1,3 @@ -use traits::Into; use array::ArrayTrait; use starknet::ContractAddress; use starknet::contract_address_const; @@ -19,6 +18,7 @@ use openzeppelin::tests::mocks::erc721_panic_mock::SnakeERC721PanicMock; use openzeppelin::tests::mocks::erc721_panic_mock::CamelERC721PanicMock; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils; +use openzeppelin::utils::serde::SerializedAppend; /// /// Constants @@ -27,10 +27,8 @@ use openzeppelin::tests::utils; const NAME: felt252 = 111; const SYMBOL: felt252 = 222; const URI: felt252 = 333; +const TOKEN_ID: u256 = 7; -fn TOKEN_ID() -> u256 { - 7.into() -} fn OWNER() -> ContractAddress { contract_address_const::<10>() } @@ -46,9 +44,9 @@ fn OPERATOR() -> ContractAddress { fn DATA(success: bool) -> Span { let mut data = ArrayTrait::new(); if success { - data.append(SUCCESS); + data.append_serde(SUCCESS); } else { - data.append(FAILURE); + data.append_serde(FAILURE); } data.span() } @@ -59,11 +57,10 @@ fn DATA(success: bool) -> Span { fn setup_snake() -> (DualCaseERC721, IERC721Dispatcher) { let mut calldata = ArrayTrait::new(); - calldata.append(NAME); - calldata.append(SYMBOL); - calldata.append(TOKEN_ID().low.into()); - calldata.append(TOKEN_ID().high.into()); - calldata.append(URI); + calldata.append_serde(NAME); + calldata.append_serde(SYMBOL); + calldata.append_serde(TOKEN_ID); + calldata.append_serde(URI); set_caller_address(OWNER()); let target = utils::deploy(SnakeERC721Mock::TEST_CLASS_HASH, calldata); (DualCaseERC721 { contract_address: target }, IERC721Dispatcher { contract_address: target }) @@ -71,11 +68,10 @@ fn setup_snake() -> (DualCaseERC721, IERC721Dispatcher) { fn setup_camel() -> (DualCaseERC721, IERC721CamelDispatcher) { let mut calldata = ArrayTrait::new(); - calldata.append(NAME); - calldata.append(SYMBOL); - calldata.append(TOKEN_ID().low.into()); - calldata.append(TOKEN_ID().high.into()); - calldata.append(URI); + calldata.append_serde(NAME); + calldata.append_serde(SYMBOL); + calldata.append_serde(TOKEN_ID); + calldata.append_serde(URI); set_caller_address(OWNER()); let target = utils::deploy(CamelERC721Mock::TEST_CLASS_HASH, calldata); ( @@ -168,13 +164,13 @@ fn test_dual_symbol_exists_and_panics() { fn test_dual_approve() { let (snake_dispatcher, snake_target) = setup_snake(); set_contract_address(OWNER()); - snake_dispatcher.approve(SPENDER(), TOKEN_ID()); - assert(snake_target.get_approved(TOKEN_ID()) == SPENDER(), 'Spender not approved correctly'); + snake_dispatcher.approve(SPENDER(), TOKEN_ID); + assert(snake_target.get_approved(TOKEN_ID) == SPENDER(), 'Spender not approved correctly'); let (camel_dispatcher, camel_target) = setup_camel(); set_contract_address(OWNER()); - camel_dispatcher.approve(SPENDER(), TOKEN_ID()); - assert(camel_target.getApproved(TOKEN_ID()) == SPENDER(), 'Spender not approved correctly'); + camel_dispatcher.approve(SPENDER(), TOKEN_ID); + assert(camel_target.getApproved(TOKEN_ID) == SPENDER(), 'Spender not approved correctly'); } #[test] @@ -182,7 +178,7 @@ fn test_dual_approve() { #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] fn test_dual_no_approve() { let dispatcher = setup_non_erc721(); - dispatcher.approve(SPENDER(), TOKEN_ID()); + dispatcher.approve(SPENDER(), TOKEN_ID); } #[test] @@ -190,7 +186,7 @@ fn test_dual_no_approve() { #[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] fn test_dual_approve_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); - dispatcher.approve(SPENDER(), TOKEN_ID()); + dispatcher.approve(SPENDER(), TOKEN_ID); } /// @@ -224,7 +220,7 @@ fn test_dual_balance_of_exists_and_panics() { #[available_gas(2000000)] fn test_dual_owner_of() { let (dispatcher, target) = setup_snake(); - assert(dispatcher.owner_of(TOKEN_ID()) == OWNER(), 'Should return owner'); + assert(dispatcher.owner_of(TOKEN_ID) == OWNER(), 'Should return owner'); } #[test] @@ -232,7 +228,7 @@ fn test_dual_owner_of() { #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] fn test_dual_no_owner_of() { let dispatcher = setup_non_erc721(); - dispatcher.owner_of(TOKEN_ID()); + dispatcher.owner_of(TOKEN_ID); } #[test] @@ -240,15 +236,15 @@ fn test_dual_no_owner_of() { #[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] fn test_dual_owner_of_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); - dispatcher.owner_of(TOKEN_ID()); + dispatcher.owner_of(TOKEN_ID); } #[test] #[available_gas(2000000)] fn test_dual_transfer_from() { let (dispatcher, target) = setup_snake(); - dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID()); - assert(target.owner_of(TOKEN_ID()) == RECIPIENT(), 'Should transfer token'); + dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID); + assert(target.owner_of(TOKEN_ID) == RECIPIENT(), 'Should transfer token'); } #[test] @@ -256,7 +252,7 @@ fn test_dual_transfer_from() { #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] fn test_dual_no_transfer_from() { let dispatcher = setup_non_erc721(); - dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID()); + dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID); } #[test] @@ -264,7 +260,7 @@ fn test_dual_no_transfer_from() { #[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] fn test_dual_transfer_from_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); - dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID()); + dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID); } #[test] @@ -272,8 +268,8 @@ fn test_dual_transfer_from_exists_and_panics() { fn test_dual_safe_transfer_from() { let (dispatcher, target) = setup_snake(); let receiver = setup_receiver(); - dispatcher.safe_transfer_from(OWNER(), receiver, TOKEN_ID(), DATA(true)); - assert(target.owner_of(TOKEN_ID()) == receiver, 'Should transfer token'); + dispatcher.safe_transfer_from(OWNER(), receiver, TOKEN_ID, DATA(true)); + assert(target.owner_of(TOKEN_ID) == receiver, 'Should transfer token'); } #[test] @@ -281,7 +277,7 @@ fn test_dual_safe_transfer_from() { #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] fn test_dual_no_safe_transfer_from() { let dispatcher = setup_non_erc721(); - dispatcher.safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID(), DATA(true)); + dispatcher.safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); } #[test] @@ -289,7 +285,7 @@ fn test_dual_no_safe_transfer_from() { #[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] fn test_dual_safe_transfer_from_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); - dispatcher.safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID(), DATA(true)); + dispatcher.safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); } #[test] @@ -297,8 +293,8 @@ fn test_dual_safe_transfer_from_exists_and_panics() { fn test_dual_get_approved() { let (dispatcher, target) = setup_snake(); set_contract_address(OWNER()); - target.approve(SPENDER(), TOKEN_ID()); - assert(dispatcher.get_approved(TOKEN_ID()) == SPENDER(), 'Should return approval'); + target.approve(SPENDER(), TOKEN_ID); + assert(dispatcher.get_approved(TOKEN_ID) == SPENDER(), 'Should return approval'); } #[test] @@ -306,7 +302,7 @@ fn test_dual_get_approved() { #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] fn test_dual_no_get_approved() { let dispatcher = setup_non_erc721(); - dispatcher.get_approved(TOKEN_ID()); + dispatcher.get_approved(TOKEN_ID); } #[test] @@ -314,7 +310,7 @@ fn test_dual_no_get_approved() { #[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] fn test_dual_get_approved_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); - dispatcher.get_approved(TOKEN_ID()); + dispatcher.get_approved(TOKEN_ID); } #[test] @@ -371,7 +367,7 @@ fn test_dual_is_approved_for_all_exists_and_panics() { #[available_gas(2000000)] fn test_dual_token_uri() { let (dispatcher, target) = setup_snake(); - assert(dispatcher.token_uri(TOKEN_ID()) == URI, 'Should return URI'); + assert(dispatcher.token_uri(TOKEN_ID) == URI, 'Should return URI'); } #[test] @@ -379,7 +375,7 @@ fn test_dual_token_uri() { #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] fn test_dual_no_token_uri() { let dispatcher = setup_non_erc721(); - dispatcher.token_uri(TOKEN_ID()); + dispatcher.token_uri(TOKEN_ID); } #[test] @@ -387,7 +383,7 @@ fn test_dual_no_token_uri() { #[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] fn test_dual_token_uri_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); - dispatcher.token_uri(TOKEN_ID()); + dispatcher.token_uri(TOKEN_ID); } /// @@ -413,7 +409,7 @@ fn test_dual_balanceOf_exists_and_panics() { #[available_gas(2000000)] fn test_dual_ownerOf() { let (dispatcher, target) = setup_camel(); - assert(dispatcher.owner_of(TOKEN_ID()) == OWNER(), 'Should return owner'); + assert(dispatcher.owner_of(TOKEN_ID) == OWNER(), 'Should return owner'); } #[test] @@ -421,7 +417,7 @@ fn test_dual_ownerOf() { #[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] fn test_dual_ownerOf_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); - dispatcher.owner_of(TOKEN_ID()); + dispatcher.owner_of(TOKEN_ID); } #[test] @@ -429,8 +425,8 @@ fn test_dual_ownerOf_exists_and_panics() { fn test_dual_transferFrom() { let (dispatcher, target) = setup_camel(); set_contract_address(OWNER()); - dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID()); - assert(target.ownerOf(TOKEN_ID()) == RECIPIENT(), 'Should transfer token'); + dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID); + assert(target.ownerOf(TOKEN_ID) == RECIPIENT(), 'Should transfer token'); } #[test] @@ -438,7 +434,7 @@ fn test_dual_transferFrom() { #[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] fn test_dual_transferFrom_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); - dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID()); + dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID); } #[test] @@ -446,8 +442,8 @@ fn test_dual_transferFrom_exists_and_panics() { fn test_dual_safeTransferFrom() { let (dispatcher, target) = setup_camel(); let receiver = setup_receiver(); - dispatcher.safe_transfer_from(OWNER(), receiver, TOKEN_ID(), DATA(true)); - assert(target.ownerOf(TOKEN_ID()) == receiver, 'Should transfer token'); + dispatcher.safe_transfer_from(OWNER(), receiver, TOKEN_ID, DATA(true)); + assert(target.ownerOf(TOKEN_ID) == receiver, 'Should transfer token'); } #[test] @@ -455,7 +451,7 @@ fn test_dual_safeTransferFrom() { #[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] fn test_dual_safeTransferFrom_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); - dispatcher.safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID(), DATA(true)); + dispatcher.safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); } #[test] @@ -463,8 +459,8 @@ fn test_dual_safeTransferFrom_exists_and_panics() { fn test_dual_getApproved() { let (dispatcher, target) = setup_camel(); set_contract_address(OWNER()); - target.approve(SPENDER(), TOKEN_ID()); - assert(dispatcher.get_approved(TOKEN_ID()) == SPENDER(), 'Should return approval'); + target.approve(SPENDER(), TOKEN_ID); + assert(dispatcher.get_approved(TOKEN_ID) == SPENDER(), 'Should return approval'); } #[test] @@ -472,7 +468,7 @@ fn test_dual_getApproved() { #[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] fn test_dual_getApproved_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); - dispatcher.get_approved(TOKEN_ID()); + dispatcher.get_approved(TOKEN_ID); } #[test] @@ -513,7 +509,7 @@ fn test_dual_isApprovedForAll_exists_and_panics() { #[available_gas(2000000)] fn test_dual_tokenUri() { let (dispatcher, target) = setup_camel(); - assert(dispatcher.token_uri(TOKEN_ID()) == URI, 'Should return URI'); + assert(dispatcher.token_uri(TOKEN_ID) == URI, 'Should return URI'); } #[test] @@ -521,5 +517,5 @@ fn test_dual_tokenUri() { #[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] fn test_dual_tokenUri_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); - dispatcher.token_uri(TOKEN_ID()); + dispatcher.token_uri(TOKEN_ID); } diff --git a/src/openzeppelin/tests/token/test_erc20.cairo b/src/openzeppelin/tests/token/test_erc20.cairo new file mode 100644 index 000000000..775c974fa --- /dev/null +++ b/src/openzeppelin/tests/token/test_erc20.cairo @@ -0,0 +1,543 @@ +use openzeppelin::token::erc20::ERC20; +use integer::BoundedInt; +use integer::u256; +use integer::u256_from_felt252; +use starknet::ContractAddress; +use starknet::contract_address_const; +use starknet::testing::set_caller_address; +use traits::Into; +use zeroable::Zeroable; + +// +// Constants +// + +const NAME: felt252 = 111; +const SYMBOL: felt252 = 222; +const DECIMALS: u8 = 18_u8; +const SUPPLY: u256 = 2000; +const VALUE: u256 = 300; + +fn OWNER() -> ContractAddress { + contract_address_const::<1>() +} + +fn SPENDER() -> ContractAddress { + contract_address_const::<2>() +} + +fn RECIPIENT() -> ContractAddress { + contract_address_const::<3>() +} + +// +// Setup +// + +fn setup() { + ERC20::constructor(NAME, SYMBOL, SUPPLY, OWNER()); +} + +// +// initializer & constructor +// + +#[test] +#[available_gas(2000000)] +fn test_initializer() { + ERC20::initializer(NAME, SYMBOL); + + assert(ERC20::name() == NAME, 'Name should be NAME'); + assert(ERC20::symbol() == SYMBOL, 'Symbol should be SYMBOL'); + assert(ERC20::decimals() == DECIMALS, 'Decimals should be 18'); + assert(ERC20::total_supply() == 0, 'Supply should eq 0'); +} + + +#[test] +#[available_gas(2000000)] +fn test_constructor() { + ERC20::constructor(NAME, SYMBOL, SUPPLY, OWNER()); + + assert(ERC20::balance_of(OWNER()) == SUPPLY, 'Should eq inital_supply'); + assert(ERC20::total_supply() == SUPPLY, 'Should eq inital_supply'); + assert(ERC20::name() == NAME, 'Name should be NAME'); + assert(ERC20::symbol() == SYMBOL, 'Symbol should be SYMBOL'); + assert(ERC20::decimals() == DECIMALS, 'Decimals should be 18'); +} + +// +// Getters +// + +#[test] +#[available_gas(2000000)] +fn test_total_supply() { + ERC20::_mint(OWNER(), SUPPLY); + assert(ERC20::total_supply() == SUPPLY, 'Should eq SUPPLY'); +} + +#[test] +#[available_gas(2000000)] +fn test_totalSupply() { + ERC20::_mint(OWNER(), SUPPLY); + assert(ERC20::totalSupply() == SUPPLY, 'Should eq SUPPLY'); +} + +#[test] +#[available_gas(2000000)] +fn test_balance_of() { + ERC20::_mint(OWNER(), SUPPLY); + assert(ERC20::balance_of(OWNER()) == SUPPLY, 'Should eq SUPPLY'); +} + +#[test] +#[available_gas(2000000)] +fn test_balanceOf() { + ERC20::_mint(OWNER(), SUPPLY); + assert(ERC20::balanceOf(OWNER()) == SUPPLY, 'Should eq SUPPLY'); +} + +#[test] +#[available_gas(2000000)] +fn test_allowance() { + setup(); + set_caller_address(OWNER()); + ERC20::approve(SPENDER(), VALUE); + + assert(ERC20::allowance(OWNER(), SPENDER()) == VALUE, 'Should eq VALUE'); +} + +// +// approve & _approve +// + +#[test] +#[available_gas(2000000)] +fn test_approve() { + setup(); + set_caller_address(OWNER()); + assert(ERC20::approve(SPENDER(), VALUE), 'Should return true'); + + assert(ERC20::allowance(OWNER(), SPENDER()) == VALUE, 'Spender not approved correctly'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC20: approve from 0', ))] +fn test_approve_from_zero() { + setup(); + ERC20::approve(SPENDER(), VALUE); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC20: approve to 0', ))] +fn test_approve_to_zero() { + setup(); + set_caller_address(OWNER()); + ERC20::approve(Zeroable::zero(), VALUE); +} + +#[test] +#[available_gas(2000000)] +fn test__approve() { + setup(); + set_caller_address(OWNER()); + ERC20::_approve(OWNER(), SPENDER(), VALUE); + + assert(ERC20::allowance(OWNER(), SPENDER()) == VALUE, 'Spender not approved correctly'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC20: approve from 0', ))] +fn test__approve_from_zero() { + setup(); + ERC20::_approve(Zeroable::zero(), SPENDER(), VALUE); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC20: approve to 0', ))] +fn test__approve_to_zero() { + setup(); + set_caller_address(OWNER()); + ERC20::_approve(OWNER(), Zeroable::zero(), VALUE); +} + +// +// transfer & _transfer +// + +#[test] +#[available_gas(2000000)] +fn test_transfer() { + setup(); + set_caller_address(OWNER()); + assert(ERC20::transfer(RECIPIENT(), VALUE), 'Should return true'); + + assert(ERC20::balance_of(RECIPIENT()) == VALUE, 'Balance should eq VALUE'); + assert(ERC20::balance_of(OWNER()) == SUPPLY - VALUE, 'Should eq supply - VALUE'); + assert(ERC20::total_supply() == SUPPLY, 'Total supply should not change'); +} + +#[test] +#[available_gas(2000000)] +fn test__transfer() { + setup(); + + ERC20::_transfer(OWNER(), RECIPIENT(), VALUE); + assert(ERC20::balance_of(RECIPIENT()) == VALUE, 'Balance should eq amount'); + assert(ERC20::balance_of(OWNER()) == SUPPLY - VALUE, 'Should eq supply - amount'); + assert(ERC20::total_supply() == SUPPLY, 'Total supply should not change'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('u256_sub Overflow', ))] +fn test__transfer_not_enough_balance() { + setup(); + set_caller_address(OWNER()); + + let balance_plus_one = SUPPLY + 1; + ERC20::_transfer(OWNER(), RECIPIENT(), balance_plus_one); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC20: transfer from 0', ))] +fn test__transfer_from_zero() { + setup(); + ERC20::_transfer(Zeroable::zero(), RECIPIENT(), VALUE); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC20: transfer to 0', ))] +fn test__transfer_to_zero() { + setup(); + ERC20::_transfer(OWNER(), Zeroable::zero(), VALUE); +} + +// +// transfer_from & transferFrom +// + +#[test] +#[available_gas(2000000)] +fn test_transfer_from() { + setup(); + set_caller_address(OWNER()); + ERC20::approve(SPENDER(), VALUE); + + set_caller_address(SPENDER()); + assert(ERC20::transfer_from(OWNER(), RECIPIENT(), VALUE), 'Should return true'); + + assert(ERC20::balance_of(RECIPIENT()) == VALUE, 'Should eq amount'); + assert(ERC20::balance_of(OWNER()) == SUPPLY - VALUE, 'Should eq suppy - amount'); + assert(ERC20::allowance(OWNER(), SPENDER()) == 0, 'Should eq 0'); + assert(ERC20::total_supply() == SUPPLY, 'Total supply should not change'); +} + +#[test] +#[available_gas(2000000)] +fn test_transfer_from_doesnt_consume_infinite_allowance() { + setup(); + set_caller_address(OWNER()); + ERC20::approve(SPENDER(), BoundedInt::max()); + + set_caller_address(SPENDER()); + ERC20::transfer_from(OWNER(), RECIPIENT(), VALUE); + + assert( + ERC20::allowance(OWNER(), SPENDER()) == BoundedInt::max(), 'Allowance should not change' + ); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('u256_sub Overflow', ))] +fn test_transfer_from_greater_than_allowance() { + setup(); + set_caller_address(OWNER()); + ERC20::approve(SPENDER(), VALUE); + + set_caller_address(SPENDER()); + let allowance_plus_one = VALUE + 1; + ERC20::transfer_from(OWNER(), RECIPIENT(), allowance_plus_one); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC20: transfer to 0', ))] +fn test_transfer_from_to_zero_address() { + setup(); + set_caller_address(OWNER()); + ERC20::approve(SPENDER(), VALUE); + + set_caller_address(SPENDER()); + ERC20::transfer_from(OWNER(), Zeroable::zero(), VALUE); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('u256_sub Overflow', ))] +fn test_transfer_from_from_zero_address() { + setup(); + ERC20::transfer_from(Zeroable::zero(), RECIPIENT(), VALUE); +} + +#[test] +#[available_gas(2000000)] +fn test_transferFrom() { + setup(); + set_caller_address(OWNER()); + ERC20::approve(SPENDER(), VALUE); + + set_caller_address(SPENDER()); + assert(ERC20::transferFrom(OWNER(), RECIPIENT(), VALUE), 'Should return true'); + + assert(ERC20::balanceOf(RECIPIENT()) == VALUE, 'Should eq amount'); + assert(ERC20::balanceOf(OWNER()) == SUPPLY - VALUE, 'Should eq suppy - amount'); + assert(ERC20::allowance(OWNER(), SPENDER()) == 0, 'Should eq 0'); + assert(ERC20::totalSupply() == SUPPLY, 'Total supply should not change'); +} + +#[test] +#[available_gas(2000000)] +fn test_transferFrom_doesnt_consume_infinite_allowance() { + setup(); + set_caller_address(OWNER()); + ERC20::approve(SPENDER(), BoundedInt::max()); + + set_caller_address(SPENDER()); + ERC20::transferFrom(OWNER(), RECIPIENT(), VALUE); + + assert( + ERC20::allowance(OWNER(), SPENDER()) == BoundedInt::max(), 'Allowance should not change' + ); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('u256_sub Overflow', ))] +fn test_transferFrom_greater_than_allowance() { + setup(); + set_caller_address(OWNER()); + ERC20::approve(SPENDER(), VALUE); + + set_caller_address(SPENDER()); + let allowance_plus_one = VALUE + 1; + ERC20::transferFrom(OWNER(), RECIPIENT(), allowance_plus_one); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC20: transfer to 0', ))] +fn test_transferFrom_to_zero_address() { + setup(); + set_caller_address(OWNER()); + ERC20::approve(SPENDER(), VALUE); + + set_caller_address(SPENDER()); + ERC20::transferFrom(OWNER(), Zeroable::zero(), VALUE); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('u256_sub Overflow', ))] +fn test_transferFrom_from_zero_address() { + setup(); + ERC20::transferFrom(Zeroable::zero(), RECIPIENT(), VALUE); +} + +// +// increase_allowance & increaseAllowance +// + +#[test] +#[available_gas(2000000)] +fn test_increase_allowance() { + setup(); + set_caller_address(OWNER()); + ERC20::approve(SPENDER(), VALUE); + + assert(ERC20::increase_allowance(SPENDER(), VALUE), 'Should return true'); + assert(ERC20::allowance(OWNER(), SPENDER()) == VALUE * 2, 'Should be amount * 2'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC20: approve to 0', ))] +fn test_increase_allowance_to_zero_address() { + setup(); + set_caller_address(OWNER()); + ERC20::increase_allowance(Zeroable::zero(), VALUE); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC20: approve from 0', ))] +fn test_increase_allowance_from_zero_address() { + setup(); + ERC20::increase_allowance(SPENDER(), VALUE); +} + +#[test] +#[available_gas(2000000)] +fn test_increaseAllowance() { + setup(); + set_caller_address(OWNER()); + ERC20::approve(SPENDER(), VALUE); + + assert(ERC20::increaseAllowance(SPENDER(), VALUE), 'Should return true'); + assert(ERC20::allowance(OWNER(), SPENDER()) == VALUE * 2, 'Should be amount * 2'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC20: approve to 0', ))] +fn test_increaseAllowance_to_zero_address() { + setup(); + set_caller_address(OWNER()); + ERC20::increaseAllowance(Zeroable::zero(), VALUE); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC20: approve from 0', ))] +fn test_increaseAllowance_from_zero_address() { + setup(); + ERC20::increaseAllowance(SPENDER(), VALUE); +} + +// +// decrease_allowance & decreaseAllowance +// + +#[test] +#[available_gas(2000000)] +fn test_decrease_allowance() { + setup(); + set_caller_address(OWNER()); + ERC20::approve(SPENDER(), VALUE); + + assert(ERC20::decrease_allowance(SPENDER(), VALUE), 'Should return true'); + assert(ERC20::allowance(OWNER(), SPENDER()) == VALUE - VALUE, 'Should be 0'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('u256_sub Overflow', ))] +fn test_decrease_allowance_to_zero_address() { + setup(); + set_caller_address(OWNER()); + ERC20::decrease_allowance(Zeroable::zero(), VALUE); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('u256_sub Overflow', ))] +fn test_decrease_allowance_from_zero_address() { + setup(); + ERC20::decrease_allowance(SPENDER(), VALUE); +} + +#[test] +#[available_gas(2000000)] +fn test_decreaseAllowance() { + setup(); + set_caller_address(OWNER()); + ERC20::approve(SPENDER(), VALUE); + + assert(ERC20::decreaseAllowance(SPENDER(), VALUE), 'Should return true'); + assert(ERC20::allowance(OWNER(), SPENDER()) == VALUE - VALUE, 'Should be 0'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('u256_sub Overflow', ))] +fn test_decreaseAllowance_to_zero_address() { + setup(); + set_caller_address(OWNER()); + ERC20::decreaseAllowance(Zeroable::zero(), VALUE); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('u256_sub Overflow', ))] +fn test_decreaseAllowance_from_zero_address() { + setup(); + ERC20::decreaseAllowance(SPENDER(), VALUE); +} + +// +// _spend_allowance +// + +#[test] +#[available_gas(2000000)] +fn test__spend_allowance_not_unlimited() { + setup(); + + ERC20::_approve(OWNER(), SPENDER(), SUPPLY); + ERC20::_spend_allowance(OWNER(), SPENDER(), VALUE); + assert(ERC20::allowance(OWNER(), SPENDER()) == SUPPLY - VALUE, 'Should eq supply - amount'); +} + +#[test] +#[available_gas(2000000)] +fn test__spend_allowance_unlimited() { + setup(); + ERC20::_approve(OWNER(), SPENDER(), BoundedInt::max()); + + let max_minus_one: u256 = BoundedInt::max() - 1; + ERC20::_spend_allowance(OWNER(), SPENDER(), max_minus_one); + + assert( + ERC20::allowance(OWNER(), SPENDER()) == BoundedInt::max(), 'Allowance should not change' + ); +} + +// +// _mint +// + +#[test] +#[available_gas(2000000)] +fn test__mint() { + ERC20::_mint(OWNER(), VALUE); + + assert(ERC20::balance_of(OWNER()) == VALUE, 'Should eq amount'); + assert(ERC20::total_supply() == VALUE, 'Should eq total supply'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC20: mint to 0', ))] +fn test__mint_to_zero() { + ERC20::_mint(Zeroable::zero(), VALUE); +} + +// +// _burn +// + +#[test] +#[available_gas(2000000)] +fn test__burn() { + setup(); + ERC20::_burn(OWNER(), VALUE); + + assert(ERC20::total_supply() == SUPPLY - VALUE, 'Should eq supply - amount'); + assert(ERC20::balance_of(OWNER()) == SUPPLY - VALUE, 'Should eq supply - amount'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC20: burn from 0', ))] +fn test__burn_from_zero() { + setup(); + ERC20::_burn(Zeroable::zero(), VALUE); +} diff --git a/src/openzeppelin/tests/test_erc721.cairo b/src/openzeppelin/tests/token/test_erc721.cairo similarity index 80% rename from src/openzeppelin/tests/test_erc721.cairo rename to src/openzeppelin/tests/token/test_erc721.cairo index d37a495a8..4f968c0ee 100644 --- a/src/openzeppelin/tests/test_erc721.cairo +++ b/src/openzeppelin/tests/token/test_erc721.cairo @@ -1,7 +1,7 @@ +use openzeppelin::account::Account; use openzeppelin::introspection::src5; use openzeppelin::token::erc721; use openzeppelin::token::erc721::ERC721; -use openzeppelin::account::Account; use openzeppelin::tests::utils; use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; @@ -21,10 +21,7 @@ use zeroable::Zeroable; const NAME: felt252 = 111; const SYMBOL: felt252 = 222; const URI: felt252 = 333; - -fn TOKEN_ID() -> u256 { - 7.into() -} +const TOKEN_ID: u256 = 7; fn ZERO() -> ContractAddress { Zeroable::zero() @@ -61,7 +58,7 @@ fn DATA(success: bool) -> Span { fn setup() { ERC721::initializer(NAME, SYMBOL); - ERC721::_mint(OWNER(), TOKEN_ID()); + ERC721::_mint(OWNER(), TOKEN_ID); } fn setup_receiver() -> ContractAddress { @@ -86,7 +83,7 @@ fn test_constructor() { assert(ERC721::name() == NAME, 'Name should be NAME'); assert(ERC721::symbol() == SYMBOL, 'Symbol should be SYMBOL'); - assert(ERC721::balance_of(OWNER()) == 0.into(), 'Balance should be zero'); + assert(ERC721::balance_of(OWNER()) == 0, 'Balance should be zero'); assert(ERC721::supports_interface(erc721::interface::IERC721_ID), 'Missing interface ID'); assert( @@ -102,7 +99,7 @@ fn test_initialize() { assert(ERC721::name() == NAME, 'Name should be NAME'); assert(ERC721::symbol() == SYMBOL, 'Symbol should be SYMBOL'); - assert(ERC721::balance_of(OWNER()) == 0.into(), 'Balance should be zero'); + assert(ERC721::balance_of(OWNER()) == 0, 'Balance should be zero'); assert(ERC721::supports_interface(erc721::interface::IERC721_ID), 'Missing interface ID'); assert( @@ -119,7 +116,7 @@ fn test_initialize() { #[available_gas(2000000)] fn test_balance_of() { setup(); - assert(ERC721::balance_of(OWNER()) == 1.into(), 'Should return balance'); + assert(ERC721::balance_of(OWNER()) == 1, 'Should return balance'); } #[test] @@ -133,7 +130,7 @@ fn test_balance_of_zero() { #[available_gas(2000000)] fn test_owner_of() { setup(); - assert(ERC721::owner_of(TOKEN_ID()) == OWNER(), 'Should return owner'); + assert(ERC721::owner_of(TOKEN_ID) == OWNER(), 'Should return owner'); } #[test] @@ -155,7 +152,7 @@ fn test_token_uri_non_minted() { fn test_get_approved() { setup(); let spender = SPENDER(); - let token_id = TOKEN_ID(); + let token_id = TOKEN_ID; assert(ERC721::get_approved(token_id) == ZERO(), 'Should return non-approval'); ERC721::_approve(spender, token_id); @@ -173,7 +170,7 @@ fn test_get_approved_nonexistent() { #[available_gas(2000000)] fn test__exists() { let zero = ZERO(); - let token_id = TOKEN_ID(); + let token_id = TOKEN_ID; assert(!ERC721::_exists(token_id), 'Token should not exist'); assert(ERC721::_owners::read(token_id) == zero, 'Invalid owner'); @@ -198,8 +195,8 @@ fn test_approve_from_owner() { setup(); set_caller_address(OWNER()); - ERC721::approve(SPENDER(), TOKEN_ID()); - assert(ERC721::get_approved(TOKEN_ID()) == SPENDER(), 'Spender not approved correctly'); + ERC721::approve(SPENDER(), TOKEN_ID); + assert(ERC721::get_approved(TOKEN_ID) == SPENDER(), 'Spender not approved correctly'); } #[test] @@ -211,8 +208,8 @@ fn test_approve_from_operator() { ERC721::set_approval_for_all(OPERATOR(), true); set_caller_address(OPERATOR()); - ERC721::approve(SPENDER(), TOKEN_ID()); - assert(ERC721::get_approved(TOKEN_ID()) == SPENDER(), 'Spender not approved correctly'); + ERC721::approve(SPENDER(), TOKEN_ID); + assert(ERC721::get_approved(TOKEN_ID) == SPENDER(), 'Spender not approved correctly'); } #[test] @@ -222,7 +219,7 @@ fn test_approve_from_unauthorized() { setup(); set_caller_address(OTHER()); - ERC721::approve(SPENDER(), TOKEN_ID()); + ERC721::approve(SPENDER(), TOKEN_ID); } #[test] @@ -232,14 +229,14 @@ fn test_approve_to_owner() { setup(); set_caller_address(OWNER()); - ERC721::approve(OWNER(), TOKEN_ID()); + ERC721::approve(OWNER(), TOKEN_ID); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] fn test_approve_nonexistent() { - ERC721::approve(SPENDER(), TOKEN_ID()); + ERC721::approve(SPENDER(), TOKEN_ID); } #[test] @@ -247,8 +244,8 @@ fn test_approve_nonexistent() { fn test__approve() { setup(); - ERC721::_approve(SPENDER(), TOKEN_ID()); - assert(ERC721::get_approved(TOKEN_ID()) == SPENDER(), 'Spender not approved correctly'); + ERC721::_approve(SPENDER(), TOKEN_ID); + assert(ERC721::get_approved(TOKEN_ID) == SPENDER(), 'Spender not approved correctly'); } #[test] @@ -257,14 +254,14 @@ fn test__approve() { fn test__approve_to_owner() { setup(); - ERC721::_approve(OWNER(), TOKEN_ID()); + ERC721::_approve(OWNER(), TOKEN_ID); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] fn test__approve_nonexistent() { - ERC721::_approve(SPENDER(), TOKEN_ID()); + ERC721::_approve(SPENDER(), TOKEN_ID); } /// @@ -334,7 +331,7 @@ fn test__set_approval_for_all_owner_equal_operator_false() { #[available_gas(2000000)] fn test_transfer_from_owner() { setup(); - let token_id = TOKEN_ID(); + let token_id = TOKEN_ID; let owner = OWNER(); let recipient = RECIPIENT(); // set approval to check reset @@ -353,7 +350,7 @@ fn test_transfer_from_owner() { #[available_gas(2000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] fn test_transfer_from_nonexistent() { - ERC721::transfer_from(ZERO(), RECIPIENT(), TOKEN_ID()); + ERC721::transfer_from(ZERO(), RECIPIENT(), TOKEN_ID); } #[test] @@ -363,7 +360,7 @@ fn test_transfer_from_to_zero() { setup(); set_caller_address(OWNER()); - ERC721::transfer_from(OWNER(), ZERO(), TOKEN_ID()); + ERC721::transfer_from(OWNER(), ZERO(), TOKEN_ID); } #[test] @@ -371,21 +368,21 @@ fn test_transfer_from_to_zero() { fn test_transfer_from_to_owner() { setup(); - assert(ERC721::owner_of(TOKEN_ID()) == OWNER(), 'Ownership before'); - assert(ERC721::balance_of(OWNER()) == 1.into(), 'Balance of owner before'); + assert(ERC721::owner_of(TOKEN_ID) == OWNER(), 'Ownership before'); + assert(ERC721::balance_of(OWNER()) == 1, 'Balance of owner before'); set_caller_address(OWNER()); - ERC721::transfer_from(OWNER(), OWNER(), TOKEN_ID()); + ERC721::transfer_from(OWNER(), OWNER(), TOKEN_ID); - assert(ERC721::owner_of(TOKEN_ID()) == OWNER(), 'Ownership after'); - assert(ERC721::balance_of(OWNER()) == 1.into(), 'Balance of owner after'); + assert(ERC721::owner_of(TOKEN_ID) == OWNER(), 'Ownership after'); + assert(ERC721::balance_of(OWNER()) == 1, 'Balance of owner after'); } #[test] #[available_gas(2000000)] fn test_transfer_from_approved() { setup(); - let token_id = TOKEN_ID(); + let token_id = TOKEN_ID; let owner = OWNER(); let recipient = RECIPIENT(); assert_state_before_transfer(token_id, owner, recipient); @@ -403,7 +400,7 @@ fn test_transfer_from_approved() { #[available_gas(2000000)] fn test_transfer_from_approved_for_all() { setup(); - let token_id = TOKEN_ID(); + let token_id = TOKEN_ID; let owner = OWNER(); let recipient = RECIPIENT(); @@ -425,7 +422,7 @@ fn test_transfer_from_unauthorized() { setup(); set_caller_address(OTHER()); - ERC721::transfer_from(OWNER(), RECIPIENT(), TOKEN_ID()); + ERC721::transfer_from(OWNER(), RECIPIENT(), TOKEN_ID); } // @@ -437,7 +434,7 @@ fn test_transfer_from_unauthorized() { fn test_safe_transfer_from_to_account() { setup(); let account = setup_account(); - let token_id = TOKEN_ID(); + let token_id = TOKEN_ID; let owner = OWNER(); assert_state_before_transfer(token_id, owner, account); @@ -453,7 +450,7 @@ fn test_safe_transfer_from_to_account() { fn test_safe_transfer_from_to_receiver() { setup(); let receiver = setup_receiver(); - let token_id = TOKEN_ID(); + let token_id = TOKEN_ID; let owner = OWNER(); assert_state_before_transfer(token_id, owner, receiver); @@ -470,7 +467,7 @@ fn test_safe_transfer_from_to_receiver() { fn test_safe_transfer_from_to_receiver_failure() { setup(); let receiver = setup_receiver(); - let token_id = TOKEN_ID(); + let token_id = TOKEN_ID; let owner = OWNER(); set_caller_address(owner); @@ -483,7 +480,7 @@ fn test_safe_transfer_from_to_receiver_failure() { fn test_safe_transfer_from_to_non_receiver() { setup(); let recipient = utils::deploy(ERC721NonReceiver::TEST_CLASS_HASH, ArrayTrait::new()); - let token_id = TOKEN_ID(); + let token_id = TOKEN_ID; let owner = OWNER(); set_caller_address(owner); @@ -494,7 +491,7 @@ fn test_safe_transfer_from_to_non_receiver() { #[available_gas(2000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] fn test_safe_transfer_from_nonexistent() { - ERC721::safe_transfer_from(ZERO(), RECIPIENT(), TOKEN_ID(), DATA(true)); + ERC721::safe_transfer_from(ZERO(), RECIPIENT(), TOKEN_ID, DATA(true)); } #[test] @@ -504,25 +501,25 @@ fn test_safe_transfer_from_to_zero() { setup(); set_caller_address(OWNER()); - ERC721::safe_transfer_from(OWNER(), ZERO(), TOKEN_ID(), DATA(true)); + ERC721::safe_transfer_from(OWNER(), ZERO(), TOKEN_ID, DATA(true)); } #[test] #[available_gas(2000000)] fn test_safe_transfer_from_to_owner() { - let token_id = TOKEN_ID(); + let token_id = TOKEN_ID; let owner = setup_receiver(); ERC721::initializer(NAME, SYMBOL); ERC721::_mint(owner, token_id); assert(ERC721::owner_of(token_id) == owner, 'Ownership before'); - assert(ERC721::balance_of(owner) == 1.into(), 'Balance of owner before'); + assert(ERC721::balance_of(owner) == 1, 'Balance of owner before'); set_caller_address(owner); ERC721::safe_transfer_from(owner, owner, token_id, DATA(true)); assert(ERC721::owner_of(token_id) == owner, 'Ownership after'); - assert(ERC721::balance_of(owner) == 1.into(), 'Balance of owner after'); + assert(ERC721::balance_of(owner) == 1, 'Balance of owner after'); } #[test] @@ -530,7 +527,7 @@ fn test_safe_transfer_from_to_owner() { fn test_safe_transfer_from_approved() { setup(); let receiver = setup_receiver(); - let token_id = TOKEN_ID(); + let token_id = TOKEN_ID; let owner = OWNER(); assert_state_before_transfer(token_id, owner, receiver); @@ -549,7 +546,7 @@ fn test_safe_transfer_from_approved() { fn test_safe_transfer_from_approved_for_all() { setup(); let receiver = setup_receiver(); - let token_id = TOKEN_ID(); + let token_id = TOKEN_ID; let owner = OWNER(); assert_state_before_transfer(token_id, owner, receiver); @@ -569,7 +566,7 @@ fn test_safe_transfer_from_approved_for_all() { fn test_safe_transfer_from_unauthorized() { setup(); set_caller_address(OTHER()); - ERC721::safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID(), DATA(true)); + ERC721::safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); } // @@ -580,7 +577,7 @@ fn test_safe_transfer_from_unauthorized() { #[available_gas(2000000)] fn test__transfer() { setup(); - let token_id = TOKEN_ID(); + let token_id = TOKEN_ID; let owner = OWNER(); let recipient = RECIPIENT(); @@ -593,7 +590,7 @@ fn test__transfer() { #[available_gas(2000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] fn test__transfer_nonexistent() { - ERC721::_transfer(ZERO(), RECIPIENT(), TOKEN_ID()); + ERC721::_transfer(ZERO(), RECIPIENT(), TOKEN_ID); } #[test] @@ -602,7 +599,7 @@ fn test__transfer_nonexistent() { fn test__transfer_to_zero() { setup(); - ERC721::_transfer(OWNER(), ZERO(), TOKEN_ID()); + ERC721::_transfer(OWNER(), ZERO(), TOKEN_ID); } #[test] @@ -611,7 +608,7 @@ fn test__transfer_to_zero() { fn test__transfer_from_invalid_owner() { setup(); - ERC721::_transfer(RECIPIENT(), OWNER(), TOKEN_ID()); + ERC721::_transfer(RECIPIENT(), OWNER(), TOKEN_ID); } /// @@ -622,9 +619,9 @@ fn test__transfer_from_invalid_owner() { #[available_gas(2000000)] fn test__mint() { let recipient = RECIPIENT(); - let token_id = TOKEN_ID(); + let token_id = TOKEN_ID; assert_state_before_mint(recipient); - ERC721::_mint(RECIPIENT(), TOKEN_ID()); + ERC721::_mint(RECIPIENT(), TOKEN_ID); assert_state_after_mint(token_id, recipient); } @@ -632,7 +629,7 @@ fn test__mint() { #[available_gas(2000000)] #[should_panic(expected: ('ERC721: invalid receiver', ))] fn test__mint_to_zero() { - ERC721::_mint(ZERO(), TOKEN_ID()); + ERC721::_mint(ZERO(), TOKEN_ID); } #[test] @@ -641,7 +638,7 @@ fn test__mint_to_zero() { fn test__mint_already_exist() { setup(); - ERC721::_mint(RECIPIENT(), TOKEN_ID()); + ERC721::_mint(RECIPIENT(), TOKEN_ID); } /// @@ -652,7 +649,7 @@ fn test__mint_already_exist() { #[available_gas(2000000)] fn test__safe_mint_to_receiver() { let recipient = setup_receiver(); - let token_id = TOKEN_ID(); + let token_id = TOKEN_ID; assert_state_before_mint(recipient); ERC721::_safe_mint(recipient, token_id, DATA(true)); @@ -663,7 +660,7 @@ fn test__safe_mint_to_receiver() { #[available_gas(2000000)] fn test__safe_mint_to_account() { let account = setup_account(); - let token_id = TOKEN_ID(); + let token_id = TOKEN_ID; assert_state_before_mint(account); ERC721::_safe_mint(account, token_id, DATA(true)); @@ -675,7 +672,7 @@ fn test__safe_mint_to_account() { #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] fn test__safe_mint_to_non_receiver() { let recipient = utils::deploy(ERC721NonReceiver::TEST_CLASS_HASH, ArrayTrait::new()); - let token_id = TOKEN_ID(); + let token_id = TOKEN_ID; assert_state_before_mint(recipient); ERC721::_safe_mint(recipient, token_id, DATA(true)); @@ -687,7 +684,7 @@ fn test__safe_mint_to_non_receiver() { #[should_panic(expected: ('ERC721: safe mint failed', ))] fn test__safe_mint_to_receiver_failure() { let recipient = setup_receiver(); - let token_id = TOKEN_ID(); + let token_id = TOKEN_ID; assert_state_before_mint(recipient); ERC721::_safe_mint(recipient, token_id, DATA(false)); @@ -698,7 +695,7 @@ fn test__safe_mint_to_receiver_failure() { #[available_gas(2000000)] #[should_panic(expected: ('ERC721: invalid receiver', ))] fn test__safe_mint_to_zero() { - ERC721::_safe_mint(ZERO(), TOKEN_ID(), DATA(true)); + ERC721::_safe_mint(ZERO(), TOKEN_ID, DATA(true)); } #[test] @@ -706,7 +703,7 @@ fn test__safe_mint_to_zero() { #[should_panic(expected: ('ERC721: token already minted', ))] fn test__safe_mint_already_exist() { setup(); - ERC721::_safe_mint(RECIPIENT(), TOKEN_ID(), DATA(true)); + ERC721::_safe_mint(RECIPIENT(), TOKEN_ID, DATA(true)); } /// @@ -718,24 +715,24 @@ fn test__safe_mint_already_exist() { fn test__burn() { setup(); - ERC721::_approve(OTHER(), TOKEN_ID()); + ERC721::_approve(OTHER(), TOKEN_ID); - assert(ERC721::owner_of(TOKEN_ID()) == OWNER(), 'Ownership before'); - assert(ERC721::balance_of(OWNER()) == 1.into(), 'Balance of owner before'); - assert(ERC721::get_approved(TOKEN_ID()) == OTHER(), 'Approval before'); + assert(ERC721::owner_of(TOKEN_ID) == OWNER(), 'Ownership before'); + assert(ERC721::balance_of(OWNER()) == 1, 'Balance of owner before'); + assert(ERC721::get_approved(TOKEN_ID) == OTHER(), 'Approval before'); - ERC721::_burn(TOKEN_ID()); + ERC721::_burn(TOKEN_ID); - assert(ERC721::_owners::read(TOKEN_ID()) == ZERO(), 'Ownership after'); - assert(ERC721::balance_of(OWNER()) == 0.into(), 'Balance of owner after'); - assert(ERC721::_token_approvals::read(TOKEN_ID()) == ZERO(), 'Approval after'); + assert(ERC721::_owners::read(TOKEN_ID) == ZERO(), 'Ownership after'); + assert(ERC721::balance_of(OWNER()) == 0, 'Balance of owner after'); + assert(ERC721::_token_approvals::read(TOKEN_ID) == ZERO(), 'Approval after'); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] fn test__burn_nonexistent() { - ERC721::_burn(TOKEN_ID()); + ERC721::_burn(TOKEN_ID); } /// @@ -747,16 +744,16 @@ fn test__burn_nonexistent() { fn test__set_token_uri() { setup(); - assert(ERC721::token_uri(TOKEN_ID()) == 0, 'URI should be 0'); - ERC721::_set_token_uri(TOKEN_ID(), URI); - assert(ERC721::token_uri(TOKEN_ID()) == URI, 'URI should be set'); + assert(ERC721::token_uri(TOKEN_ID) == 0, 'URI should be 0'); + ERC721::_set_token_uri(TOKEN_ID, URI); + assert(ERC721::token_uri(TOKEN_ID) == URI, 'URI should be set'); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] fn test__set_token_uri_nonexistent() { - ERC721::_set_token_uri(TOKEN_ID(), URI); + ERC721::_set_token_uri(TOKEN_ID, URI); } // @@ -767,23 +764,23 @@ fn assert_state_before_transfer( token_id: u256, owner: ContractAddress, recipient: ContractAddress ) { assert(ERC721::owner_of(token_id) == owner, 'Ownership before'); - assert(ERC721::balance_of(owner) == 1.into(), 'Balance of owner before'); - assert(ERC721::balance_of(recipient) == 0.into(), 'Balance of recipient before'); + assert(ERC721::balance_of(owner) == 1, 'Balance of owner before'); + assert(ERC721::balance_of(recipient) == 0, 'Balance of recipient before'); } fn assert_state_after_transfer(token_id: u256, owner: ContractAddress, recipient: ContractAddress) { assert(ERC721::owner_of(token_id) == recipient, 'Ownership after'); - assert(ERC721::balance_of(owner) == 0.into(), 'Balance of owner after'); - assert(ERC721::balance_of(recipient) == 1.into(), 'Balance of recipient after'); + assert(ERC721::balance_of(owner) == 0, 'Balance of owner after'); + assert(ERC721::balance_of(recipient) == 1, 'Balance of recipient after'); assert(ERC721::get_approved(token_id) == ZERO(), 'Approval not implicitly reset'); } fn assert_state_before_mint(recipient: ContractAddress) { - assert(ERC721::balance_of(recipient) == 0.into(), 'Balance of recipient before'); + assert(ERC721::balance_of(recipient) == 0, 'Balance of recipient before'); } fn assert_state_after_mint(token_id: u256, recipient: ContractAddress) { assert(ERC721::owner_of(token_id) == recipient, 'Ownership after'); - assert(ERC721::balance_of(recipient) == 1.into(), 'Balance of recipient after'); + assert(ERC721::balance_of(recipient) == 1, 'Balance of recipient after'); assert(ERC721::get_approved(token_id) == ZERO(), 'Approval implicitly set'); } diff --git a/src/openzeppelin/tests/utils.cairo b/src/openzeppelin/tests/utils.cairo index e0eaea264..d3b720cce 100644 --- a/src/openzeppelin/tests/utils.cairo +++ b/src/openzeppelin/tests/utils.cairo @@ -1,12 +1,9 @@ +use array::ArrayTrait; use core::result::ResultTrait; use option::OptionTrait; -use array::ArrayTrait; -use traits::TryInto; -use traits::Into; - -use openzeppelin::utils::BoolIntoFelt252; use starknet::class_hash::Felt252TryIntoClassHash; use starknet::ContractAddress; +use traits::TryInto; fn deploy(contract_class_hash: felt252, calldata: Array) -> ContractAddress { let (address, _) = starknet::deploy_syscall( diff --git a/src/openzeppelin/token/erc20.cairo b/src/openzeppelin/token/erc20.cairo index eca9933d7..1a728ef23 100644 --- a/src/openzeppelin/token/erc20.cairo +++ b/src/openzeppelin/token/erc20.cairo @@ -1,233 +1,5 @@ -use starknet::ContractAddress; +mod erc20; +use erc20::{ERC20, ERC20ABIDispatcher, ERC20ABIDispatcherTrait}; -#[abi] -trait IERC20 { - #[view] - fn name() -> felt252; - #[view] - fn symbol() -> felt252; - #[view] - fn decimals() -> u8; - #[view] - fn total_supply() -> u256; - #[view] - fn balance_of(account: ContractAddress) -> u256; - #[view] - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256; - #[external] - fn transfer(recipient: ContractAddress, amount: u256) -> bool; - #[external] - fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool; - #[external] - fn approve(spender: ContractAddress, amount: u256) -> bool; - #[external] - fn increase_allowance(spender: ContractAddress, added_value: u256) -> bool; - #[external] - fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool; -} - -#[contract] -mod ERC20 { - use super::IERC20; - use integer::BoundedInt; - use starknet::ContractAddress; - use starknet::get_caller_address; - use zeroable::Zeroable; - - struct Storage { - _name: felt252, - _symbol: felt252, - _total_supply: u256, - _balances: LegacyMap, - _allowances: LegacyMap<(ContractAddress, ContractAddress), u256>, - } - - #[event] - fn Transfer(from: ContractAddress, to: ContractAddress, value: u256) {} - - #[event] - fn Approval(owner: ContractAddress, spender: ContractAddress, value: u256) {} - - impl ERC20 of IERC20 { - fn name() -> felt252 { - _name::read() - } - - fn symbol() -> felt252 { - _symbol::read() - } - - fn decimals() -> u8 { - 18_u8 - } - - fn total_supply() -> u256 { - _total_supply::read() - } - - fn balance_of(account: ContractAddress) -> u256 { - _balances::read(account) - } - - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { - _allowances::read((owner, spender)) - } - - fn transfer(recipient: ContractAddress, amount: u256) -> bool { - let sender = get_caller_address(); - _transfer(sender, recipient, amount); - true - } - - fn transfer_from( - sender: ContractAddress, recipient: ContractAddress, amount: u256 - ) -> bool { - let caller = get_caller_address(); - _spend_allowance(sender, caller, amount); - _transfer(sender, recipient, amount); - true - } - - fn approve(spender: ContractAddress, amount: u256) -> bool { - let caller = get_caller_address(); - _approve(caller, spender, amount); - true - } - - fn increase_allowance(spender: ContractAddress, added_value: u256) -> bool { - _increase_allowance(spender, added_value) - } - - fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { - _decrease_allowance(spender, subtracted_value) - } - } - - #[constructor] - fn constructor( - name: felt252, symbol: felt252, initial_supply: u256, recipient: ContractAddress - ) { - initializer(name, symbol); - _mint(recipient, initial_supply); - } - - #[view] - fn name() -> felt252 { - ERC20::name() - } - - #[view] - fn symbol() -> felt252 { - ERC20::symbol() - } - - #[view] - fn decimals() -> u8 { - ERC20::decimals() - } - - #[view] - fn total_supply() -> u256 { - ERC20::total_supply() - } - - #[view] - fn balance_of(account: ContractAddress) -> u256 { - ERC20::balance_of(account) - } - - #[view] - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { - ERC20::allowance(owner, spender) - } - - #[external] - fn transfer(recipient: ContractAddress, amount: u256) -> bool { - ERC20::transfer(recipient, amount) - } - - #[external] - fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { - ERC20::transfer_from(sender, recipient, amount) - } - - #[external] - fn approve(spender: ContractAddress, amount: u256) -> bool { - ERC20::approve(spender, amount) - } - - #[external] - fn increase_allowance(spender: ContractAddress, added_value: u256) -> bool { - ERC20::increase_allowance(spender, added_value) - } - - #[external] - fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { - ERC20::decrease_allowance(spender, subtracted_value) - } - - /// - /// Internals - /// - - #[internal] - fn initializer(name_: felt252, symbol_: felt252) { - _name::write(name_); - _symbol::write(symbol_); - } - - #[internal] - fn _increase_allowance(spender: ContractAddress, added_value: u256) -> bool { - let caller = get_caller_address(); - _approve(caller, spender, _allowances::read((caller, spender)) + added_value); - true - } - - #[internal] - fn _decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { - let caller = get_caller_address(); - _approve(caller, spender, _allowances::read((caller, spender)) - subtracted_value); - true - } - - #[internal] - fn _mint(recipient: ContractAddress, amount: u256) { - assert(!recipient.is_zero(), 'ERC20: mint to 0'); - _total_supply::write(_total_supply::read() + amount); - _balances::write(recipient, _balances::read(recipient) + amount); - Transfer(Zeroable::zero(), recipient, amount); - } - - #[internal] - fn _burn(account: ContractAddress, amount: u256) { - assert(!account.is_zero(), 'ERC20: burn from 0'); - _total_supply::write(_total_supply::read() - amount); - _balances::write(account, _balances::read(account) - amount); - Transfer(account, Zeroable::zero(), amount); - } - - #[internal] - fn _approve(owner: ContractAddress, spender: ContractAddress, amount: u256) { - assert(!owner.is_zero(), 'ERC20: approve from 0'); - assert(!spender.is_zero(), 'ERC20: approve to 0'); - _allowances::write((owner, spender), amount); - Approval(owner, spender, amount); - } - - #[internal] - fn _transfer(sender: ContractAddress, recipient: ContractAddress, amount: u256) { - assert(!sender.is_zero(), 'ERC20: transfer from 0'); - assert(!recipient.is_zero(), 'ERC20: transfer to 0'); - _balances::write(sender, _balances::read(sender) - amount); - _balances::write(recipient, _balances::read(recipient) + amount); - Transfer(sender, recipient, amount); - } - - #[internal] - fn _spend_allowance(owner: ContractAddress, spender: ContractAddress, amount: u256) { - let current_allowance = _allowances::read((owner, spender)); - if current_allowance != BoundedInt::max() { - _approve(owner, spender, current_allowance - amount); - } - } -} +mod interface; +mod dual20; diff --git a/src/openzeppelin/token/erc20/dual20.cairo b/src/openzeppelin/token/erc20/dual20.cairo new file mode 100644 index 000000000..9e0ece69e --- /dev/null +++ b/src/openzeppelin/token/erc20/dual20.cairo @@ -0,0 +1,138 @@ +use array::ArrayTrait; +use array::SpanTrait; +use core::result::ResultTrait; +use integer::Felt252TryIntoU8; +use option::OptionTrait; +use starknet::call_contract_syscall; +use starknet::ContractAddress; +use starknet::Felt252TryIntoContractAddress; +use starknet::SyscallResultTrait; +use traits::Into; +use traits::TryInto; + +use openzeppelin::utils::try_selector_with_fallback; +use openzeppelin::utils::Felt252TryIntoBool; +use openzeppelin::utils::BoolIntoFelt252; +use openzeppelin::utils::selectors; +use openzeppelin::utils::serde::SerializedAppend; + +#[derive(Copy, Drop)] +struct DualERC20 { + contract_address: ContractAddress +} + +trait DualERC20Trait { + fn name(self: @DualERC20) -> felt252; + fn symbol(self: @DualERC20) -> felt252; + fn decimals(self: @DualERC20) -> u8; + fn total_supply(self: @DualERC20) -> u256; + fn balance_of(self: @DualERC20, account: ContractAddress) -> u256; + fn allowance(self: @DualERC20, owner: ContractAddress, spender: ContractAddress) -> u256; + fn transfer(self: @DualERC20, recipient: ContractAddress, amount: u256) -> bool; + fn transfer_from( + self: @DualERC20, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool; + fn approve(self: @DualERC20, spender: ContractAddress, amount: u256) -> bool; +} + +impl DualERC20Impl of DualERC20Trait { + fn name(self: @DualERC20) -> felt252 { + let args = ArrayTrait::new(); + + *call_contract_syscall(*self.contract_address, selectors::name, args.span()) + .unwrap_syscall() + .at(0) + } + + fn symbol(self: @DualERC20) -> felt252 { + let args = ArrayTrait::new(); + + *call_contract_syscall(*self.contract_address, selectors::symbol, args.span()) + .unwrap_syscall() + .at(0) + } + + fn decimals(self: @DualERC20) -> u8 { + let args = ArrayTrait::new(); + + (*call_contract_syscall(*self.contract_address, selectors::decimals, args.span()) + .unwrap_syscall() + .at(0)) + .try_into() + .unwrap() + } + + fn total_supply(self: @DualERC20) -> u256 { + let mut args = ArrayTrait::new(); + let res = try_selector_with_fallback( + *self.contract_address, selectors::total_supply, selectors::totalSupply, args.span() + ) + .unwrap_syscall(); + + u256 { low: (*res.at(0)).try_into().unwrap(), high: (*res.at(1)).try_into().unwrap(), } + } + + fn balance_of(self: @DualERC20, account: ContractAddress) -> u256 { + let mut args = ArrayTrait::new(); + args.append_serde(account); + + let res = try_selector_with_fallback( + *self.contract_address, selectors::balance_of, selectors::balanceOf, args.span() + ) + .unwrap_syscall(); + + u256 { low: (*res.at(0)).try_into().unwrap(), high: (*res.at(1)).try_into().unwrap(), } + } + + fn allowance(self: @DualERC20, owner: ContractAddress, spender: ContractAddress) -> u256 { + let mut args = ArrayTrait::new(); + args.append_serde(owner); + args.append_serde(spender); + + let res = call_contract_syscall(*self.contract_address, selectors::allowance, args.span()) + .unwrap_syscall(); + + u256 { low: (*res.at(0)).try_into().unwrap(), high: (*res.at(1)).try_into().unwrap(), } + } + + fn transfer(self: @DualERC20, recipient: ContractAddress, amount: u256) -> bool { + let mut args = ArrayTrait::new(); + args.append_serde(recipient); + args.append_serde(amount); + + (*call_contract_syscall(*self.contract_address, selectors::transfer, args.span()) + .unwrap_syscall() + .at(0)) + .try_into() + .unwrap() + } + + fn transfer_from( + self: @DualERC20, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool { + let mut args = ArrayTrait::new(); + args.append_serde(sender); + args.append_serde(recipient); + args.append_serde(amount); + + (*try_selector_with_fallback( + *self.contract_address, selectors::transfer_from, selectors::transferFrom, args.span() + ) + .unwrap_syscall() + .at(0)) + .try_into() + .unwrap() + } + + fn approve(self: @DualERC20, spender: ContractAddress, amount: u256) -> bool { + let mut args = ArrayTrait::new(); + args.append_serde(spender); + args.append_serde(amount); + + (*call_contract_syscall(*self.contract_address, selectors::approve, args.span()) + .unwrap_syscall() + .at(0)) + .try_into() + .unwrap() + } +} diff --git a/src/openzeppelin/token/erc20/erc20.cairo b/src/openzeppelin/token/erc20/erc20.cairo new file mode 100644 index 000000000..3342742ba --- /dev/null +++ b/src/openzeppelin/token/erc20/erc20.cairo @@ -0,0 +1,314 @@ +use starknet::ContractAddress; + +#[abi] +trait ERC20ABI { + #[view] + fn name() -> felt252; + #[view] + fn symbol() -> felt252; + #[view] + fn decimals() -> u8; + #[view] + fn total_supply() -> u256; + #[view] + fn balance_of(account: ContractAddress) -> u256; + #[view] + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256; + #[external] + fn transfer(recipient: ContractAddress, amount: u256) -> bool; + #[external] + fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool; + #[external] + fn approve(spender: ContractAddress, amount: u256) -> bool; + #[external] + fn increase_allowance(spender: ContractAddress, added_value: u256) -> bool; + #[external] + fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool; +} + +#[abi] +trait ERC20CamelABI { + #[view] + fn name() -> felt252; + #[view] + fn symbol() -> felt252; + #[view] + fn decimals() -> u8; + #[view] + fn totalSupply() -> u256; + #[view] + fn balanceOf(account: ContractAddress) -> u256; + #[view] + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256; + #[external] + fn transfer(recipient: ContractAddress, amount: u256) -> bool; + #[external] + fn transferFrom(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool; + #[external] + fn approve(spender: ContractAddress, amount: u256) -> bool; + #[external] + fn increaseAllowance(spender: ContractAddress, addedValue: u256) -> bool; + #[external] + fn decreaseAllowance(spender: ContractAddress, subtractedValue: u256) -> bool; +} + +#[contract] +mod ERC20 { + use openzeppelin::token::erc20::interface::{IERC20, IERC20Camel}; + use integer::BoundedInt; + use starknet::ContractAddress; + use starknet::get_caller_address; + use zeroable::Zeroable; + + struct Storage { + _name: felt252, + _symbol: felt252, + _total_supply: u256, + _balances: LegacyMap, + _allowances: LegacyMap<(ContractAddress, ContractAddress), u256>, + } + + #[event] + fn Transfer(from: ContractAddress, to: ContractAddress, value: u256) {} + + #[event] + fn Approval(owner: ContractAddress, spender: ContractAddress, value: u256) {} + + impl ERC20Impl of IERC20 { + fn name() -> felt252 { + _name::read() + } + + fn symbol() -> felt252 { + _symbol::read() + } + + fn decimals() -> u8 { + 18_u8 + } + + fn total_supply() -> u256 { + _total_supply::read() + } + + fn balance_of(account: ContractAddress) -> u256 { + _balances::read(account) + } + + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { + _allowances::read((owner, spender)) + } + + fn transfer(recipient: ContractAddress, amount: u256) -> bool { + let sender = get_caller_address(); + _transfer(sender, recipient, amount); + true + } + + fn transfer_from( + sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool { + let caller = get_caller_address(); + _spend_allowance(sender, caller, amount); + _transfer(sender, recipient, amount); + true + } + + fn approve(spender: ContractAddress, amount: u256) -> bool { + let caller = get_caller_address(); + _approve(caller, spender, amount); + true + } + } + + impl ERC20CamelImpl of IERC20Camel { + fn name() -> felt252 { + ERC20Impl::name() + } + + fn symbol() -> felt252 { + ERC20Impl::symbol() + } + + fn decimals() -> u8 { + ERC20Impl::decimals() + } + + fn totalSupply() -> u256 { + ERC20Impl::total_supply() + } + + fn balanceOf(account: ContractAddress) -> u256 { + ERC20Impl::balance_of(account) + } + + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { + ERC20Impl::allowance(owner, spender) + } + + fn transfer(recipient: ContractAddress, amount: u256) -> bool { + ERC20Impl::transfer(recipient, amount) + } + + fn transferFrom(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { + ERC20Impl::transfer_from(sender, recipient, amount) + } + + fn approve(spender: ContractAddress, amount: u256) -> bool { + ERC20Impl::approve(spender, amount) + } + } + + #[constructor] + fn constructor( + name: felt252, symbol: felt252, initial_supply: u256, recipient: ContractAddress + ) { + initializer(name, symbol); + _mint(recipient, initial_supply); + } + + #[view] + fn name() -> felt252 { + ERC20Impl::name() + } + + #[view] + fn symbol() -> felt252 { + ERC20Impl::symbol() + } + + #[view] + fn decimals() -> u8 { + ERC20Impl::decimals() + } + + #[view] + fn total_supply() -> u256 { + ERC20Impl::total_supply() + } + + #[view] + fn totalSupply() -> u256 { + ERC20CamelImpl::totalSupply() + } + + #[view] + fn balance_of(account: ContractAddress) -> u256 { + ERC20Impl::balance_of(account) + } + + #[view] + fn balanceOf(account: ContractAddress) -> u256 { + ERC20CamelImpl::balanceOf(account) + } + + #[view] + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { + ERC20Impl::allowance(owner, spender) + } + + #[external] + fn transfer(recipient: ContractAddress, amount: u256) -> bool { + ERC20Impl::transfer(recipient, amount) + } + + #[external] + fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { + ERC20Impl::transfer_from(sender, recipient, amount) + } + + #[external] + fn transferFrom(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { + ERC20CamelImpl::transferFrom(sender, recipient, amount) + } + + #[external] + fn approve(spender: ContractAddress, amount: u256) -> bool { + ERC20Impl::approve(spender, amount) + } + + #[external] + fn increase_allowance(spender: ContractAddress, added_value: u256) -> bool { + _increase_allowance(spender, added_value) + } + + #[external] + fn increaseAllowance(spender: ContractAddress, addedValue: u256) -> bool { + increase_allowance(spender, addedValue) + } + + #[external] + fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { + _decrease_allowance(spender, subtracted_value) + } + + #[external] + fn decreaseAllowance(spender: ContractAddress, subtractedValue: u256) -> bool { + decrease_allowance(spender, subtractedValue) + } + + /// + /// Internals + /// + + #[internal] + fn initializer(name_: felt252, symbol_: felt252) { + _name::write(name_); + _symbol::write(symbol_); + } + + #[internal] + fn _increase_allowance(spender: ContractAddress, added_value: u256) -> bool { + let caller = get_caller_address(); + _approve(caller, spender, _allowances::read((caller, spender)) + added_value); + true + } + + #[internal] + fn _decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { + let caller = get_caller_address(); + _approve(caller, spender, _allowances::read((caller, spender)) - subtracted_value); + true + } + + #[internal] + fn _mint(recipient: ContractAddress, amount: u256) { + assert(!recipient.is_zero(), 'ERC20: mint to 0'); + _total_supply::write(_total_supply::read() + amount); + _balances::write(recipient, _balances::read(recipient) + amount); + Transfer(Zeroable::zero(), recipient, amount); + } + + #[internal] + fn _burn(account: ContractAddress, amount: u256) { + assert(!account.is_zero(), 'ERC20: burn from 0'); + _total_supply::write(_total_supply::read() - amount); + _balances::write(account, _balances::read(account) - amount); + Transfer(account, Zeroable::zero(), amount); + } + + #[internal] + fn _approve(owner: ContractAddress, spender: ContractAddress, amount: u256) { + assert(!owner.is_zero(), 'ERC20: approve from 0'); + assert(!spender.is_zero(), 'ERC20: approve to 0'); + _allowances::write((owner, spender), amount); + Approval(owner, spender, amount); + } + + #[internal] + fn _transfer(sender: ContractAddress, recipient: ContractAddress, amount: u256) { + assert(!sender.is_zero(), 'ERC20: transfer from 0'); + assert(!recipient.is_zero(), 'ERC20: transfer to 0'); + _balances::write(sender, _balances::read(sender) - amount); + _balances::write(recipient, _balances::read(recipient) + amount); + Transfer(sender, recipient, amount); + } + + #[internal] + fn _spend_allowance(owner: ContractAddress, spender: ContractAddress, amount: u256) { + let current_allowance = _allowances::read((owner, spender)); + if current_allowance != BoundedInt::max() { + _approve(owner, spender, current_allowance - amount); + } + } +} diff --git a/src/openzeppelin/token/erc20/interface.cairo b/src/openzeppelin/token/erc20/interface.cairo new file mode 100644 index 000000000..26b00e63b --- /dev/null +++ b/src/openzeppelin/token/erc20/interface.cairo @@ -0,0 +1,45 @@ +use starknet::ContractAddress; + +#[abi] +trait IERC20 { + #[view] + fn name() -> felt252; + #[view] + fn symbol() -> felt252; + #[view] + fn decimals() -> u8; + #[view] + fn total_supply() -> u256; + #[view] + fn balance_of(account: ContractAddress) -> u256; + #[view] + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256; + #[external] + fn transfer(recipient: ContractAddress, amount: u256) -> bool; + #[external] + fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool; + #[external] + fn approve(spender: ContractAddress, amount: u256) -> bool; +} + +#[abi] +trait IERC20Camel { + #[view] + fn name() -> felt252; + #[view] + fn symbol() -> felt252; + #[view] + fn decimals() -> u8; + #[view] + fn totalSupply() -> u256; + #[view] + fn balanceOf(account: ContractAddress) -> u256; + #[view] + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256; + #[external] + fn transfer(recipient: ContractAddress, amount: u256) -> bool; + #[external] + fn transferFrom(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool; + #[external] + fn approve(spender: ContractAddress, amount: u256) -> bool; +} diff --git a/src/openzeppelin/token/erc721/dual721.cairo b/src/openzeppelin/token/erc721/dual721.cairo index e71b08874..b1d022686 100644 --- a/src/openzeppelin/token/erc721/dual721.cairo +++ b/src/openzeppelin/token/erc721/dual721.cairo @@ -12,6 +12,7 @@ use openzeppelin::utils::try_selector_with_fallback; use openzeppelin::utils::Felt252TryIntoBool; use openzeppelin::utils::BoolIntoFelt252; use openzeppelin::utils::selectors; +use openzeppelin::utils::serde::SerializedAppend; #[derive(Copy, Drop)] struct DualCaseERC721 { @@ -44,21 +45,24 @@ trait DualCaseERC721Trait { impl DualCaseERC721Impl of DualCaseERC721Trait { fn name(self: @DualCaseERC721) -> felt252 { - *call_contract_syscall(*self.contract_address, selectors::name, ArrayTrait::new().span()) + let args = ArrayTrait::new(); + + *call_contract_syscall(*self.contract_address, selectors::name, args.span()) .unwrap_syscall() .at(0) } fn symbol(self: @DualCaseERC721) -> felt252 { - *call_contract_syscall(*self.contract_address, selectors::symbol, ArrayTrait::new().span()) + let args = ArrayTrait::new(); + + *call_contract_syscall(*self.contract_address, selectors::symbol, args.span()) .unwrap_syscall() .at(0) } fn token_uri(self: @DualCaseERC721, token_id: u256) -> felt252 { let mut args = ArrayTrait::new(); - args.append(token_id.low.into()); - args.append(token_id.high.into()); + args.append_serde(token_id); *try_selector_with_fallback( *self.contract_address, selectors::token_uri, selectors::tokenUri, args.span() @@ -69,7 +73,7 @@ impl DualCaseERC721Impl of DualCaseERC721Trait { fn balance_of(self: @DualCaseERC721, account: ContractAddress) -> u256 { let mut args = ArrayTrait::new(); - args.append(account.into()); + args.append_serde(account); let res = try_selector_with_fallback( *self.contract_address, selectors::balance_of, selectors::balanceOf, args.span() @@ -81,8 +85,7 @@ impl DualCaseERC721Impl of DualCaseERC721Trait { fn owner_of(self: @DualCaseERC721, token_id: u256) -> ContractAddress { let mut args = ArrayTrait::new(); - args.append(token_id.low.into()); - args.append(token_id.high.into()); + args.append_serde(token_id); (*try_selector_with_fallback( *self.contract_address, selectors::owner_of, selectors::ownerOf, args.span() @@ -95,8 +98,7 @@ impl DualCaseERC721Impl of DualCaseERC721Trait { fn get_approved(self: @DualCaseERC721, token_id: u256) -> ContractAddress { let mut args = ArrayTrait::new(); - args.append(token_id.low.into()); - args.append(token_id.high.into()); + args.append_serde(token_id); (*try_selector_with_fallback( *self.contract_address, selectors::get_approved, selectors::getApproved, args.span() @@ -111,8 +113,8 @@ impl DualCaseERC721Impl of DualCaseERC721Trait { self: @DualCaseERC721, owner: ContractAddress, operator: ContractAddress ) -> bool { let mut args = ArrayTrait::new(); - args.append(owner.into()); - args.append(operator.into()); + args.append_serde(owner); + args.append_serde(operator); (*try_selector_with_fallback( *self.contract_address, @@ -128,17 +130,16 @@ impl DualCaseERC721Impl of DualCaseERC721Trait { fn approve(self: @DualCaseERC721, to: ContractAddress, token_id: u256) { let mut args = ArrayTrait::new(); - args.append(to.into()); - args.append(token_id.low.into()); - args.append(token_id.high.into()); + args.append_serde(to); + args.append_serde(token_id); call_contract_syscall(*self.contract_address, selectors::approve, args.span()) .unwrap_syscall(); } fn set_approval_for_all(self: @DualCaseERC721, operator: ContractAddress, approved: bool) { let mut args = ArrayTrait::new(); - args.append(operator.into()); - args.append(approved.into()); + args.append_serde(operator); + args.append_serde(approved); try_selector_with_fallback( *self.contract_address, @@ -153,10 +154,9 @@ impl DualCaseERC721Impl of DualCaseERC721Trait { self: @DualCaseERC721, from: ContractAddress, to: ContractAddress, token_id: u256 ) { let mut args = ArrayTrait::new(); - args.append(from.into()); - args.append(to.into()); - args.append(token_id.low.into()); - args.append(token_id.high.into()); + args.append_serde(from); + args.append_serde(to); + args.append_serde(token_id); try_selector_with_fallback( *self.contract_address, selectors::transfer_from, selectors::transferFrom, args.span() @@ -172,20 +172,10 @@ impl DualCaseERC721Impl of DualCaseERC721Trait { mut data: Span ) { let mut args = ArrayTrait::new(); - args.append(from.into()); - args.append(to.into()); - args.append(token_id.low.into()); - args.append(token_id.high.into()); - args.append(data.len().into()); - - loop { - match data.pop_front() { - Option::Some(x) => args.append(*x), - Option::None(_) => { - break (); - } - }; - }; + args.append_serde(from); + args.append_serde(to); + args.append_serde(token_id); + args.append_serde(data); try_selector_with_fallback( *self.contract_address, diff --git a/src/openzeppelin/token/erc721/erc721.cairo b/src/openzeppelin/token/erc721/erc721.cairo index 7c1345447..6aaf0bc54 100644 --- a/src/openzeppelin/token/erc721/erc721.cairo +++ b/src/openzeppelin/token/erc721/erc721.cairo @@ -370,7 +370,7 @@ mod ERC721 { assert(!_exists(token_id), 'ERC721: token already minted'); // Update balances - _balances::write(to, _balances::read(to) + 1.into()); + _balances::write(to, _balances::read(to) + 1); // Update token_id owner _owners::write(token_id, to); @@ -389,8 +389,8 @@ mod ERC721 { _token_approvals::write(token_id, Zeroable::zero()); // Update balances - _balances::write(from, _balances::read(from) - 1.into()); - _balances::write(to, _balances::read(to) + 1.into()); + _balances::write(from, _balances::read(from) - 1); + _balances::write(to, _balances::read(to) + 1); // Update token_id owner _owners::write(token_id, to); @@ -407,7 +407,7 @@ mod ERC721 { _token_approvals::write(token_id, Zeroable::zero()); // Update balances - _balances::write(owner, _balances::read(owner) - 1.into()); + _balances::write(owner, _balances::read(owner) - 1); // Delete owner _owners::write(token_id, Zeroable::zero()); diff --git a/src/openzeppelin/utils/selectors.cairo b/src/openzeppelin/utils/selectors.cairo index 33844ef8e..8c17091ce 100644 --- a/src/openzeppelin/utils/selectors.cairo +++ b/src/openzeppelin/utils/selectors.cairo @@ -52,3 +52,15 @@ const transferFrom: felt252 = 0x41b033f4a31df8067c24d1e9b550a2ce75fd4a29e1147af9 const safe_transfer_from: felt252 = 0x16f0218b33b5cf273196787d7cf139a9ad13d58e6674dcdce722b3bf8389863; const safeTransferFrom: felt252 = 0x19d59d013d4aa1a8b1ce4c8299086f070733b453c02d0dc46e735edc04d6444; + +// +// ERC20 +// + +// The following ERC20 selectors are already defined in ERC721 above: +// name, symbol, balance_of, balanceOf, transfer_from, transferFrom, approve +const decimals: felt252 = 0x4c4fb1ab068f6039d5780c68dd0fa2f8742cceb3426d19667778ca7f3518a9; +const total_supply: felt252 = 0x1557182e4359a1f0c6301278e8f5b35a776ab58d39892581e357578fb287836; +const totalSupply: felt252 = 0x80aa9fdbfaf9615e4afc7f5f722e265daca5ccc655360fa5ccacf9c267936d; +const allowance: felt252 = 0x1e888a1026b19c8c0b57c72d63ed1737106aa10034105b980ba117bd0c29fe1; +const transfer: felt252 = 0x83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e; From 6a1f72910ef24d1bde896bd8572596322ed0757f Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Thu, 6 Jul 2023 08:46:01 -0400 Subject: [PATCH 058/246] Add `UnwrapAndCast` trait (#644) * add unwrap_and_cast trait and impls * use unwrap_and_cast * tidy up code * add UnwrapAndCastU8 impl * reorder use clauses * use unwrap_and_cast on view fns --- .../accesscontrol/dual_accesscontrol.cairo | 8 ++- .../access/ownable/dual_ownable.cairo | 10 ++-- src/openzeppelin/token/erc20/dual20.cairo | 49 ++++++------------ src/openzeppelin/token/erc721/dual721.cairo | 28 ++++------- src/openzeppelin/utils.cairo | 50 ++++++++++++++++--- 5 files changed, 75 insertions(+), 70 deletions(-) diff --git a/src/openzeppelin/access/accesscontrol/dual_accesscontrol.cairo b/src/openzeppelin/access/accesscontrol/dual_accesscontrol.cairo index 3e9c0151a..032c4f452 100644 --- a/src/openzeppelin/access/accesscontrol/dual_accesscontrol.cairo +++ b/src/openzeppelin/access/accesscontrol/dual_accesscontrol.cairo @@ -11,6 +11,7 @@ use openzeppelin::utils::Felt252TryIntoBool; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; use openzeppelin::utils::try_selector_with_fallback; +use openzeppelin::utils::UnwrapAndCast; #[derive(Copy, Drop)] struct DualCaseAccessControl { @@ -31,13 +32,10 @@ impl DualCaseAccessControlImpl of DualCaseAccessControlTrait { args.append_serde(role); args.append_serde(account); - (*try_selector_with_fallback( + try_selector_with_fallback( *self.contract_address, selectors::has_role, selectors::hasRole, args.span() ) - .unwrap_syscall() - .at(0)) - .try_into() - .unwrap() + .unwrap_and_cast() } fn get_role_admin(self: @DualCaseAccessControl, role: felt252) -> felt252 { diff --git a/src/openzeppelin/access/ownable/dual_ownable.cairo b/src/openzeppelin/access/ownable/dual_ownable.cairo index fc7381f80..583d96ffe 100644 --- a/src/openzeppelin/access/ownable/dual_ownable.cairo +++ b/src/openzeppelin/access/ownable/dual_ownable.cairo @@ -8,10 +8,11 @@ use starknet::SyscallResultTrait; use starknet::call_contract_syscall; use traits::TryInto; -use openzeppelin::utils::try_selector_with_fallback; use openzeppelin::utils::Felt252TryIntoBool; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; +use openzeppelin::utils::try_selector_with_fallback; +use openzeppelin::utils::UnwrapAndCast; #[derive(Copy, Drop)] struct DualCaseOwnable { @@ -28,11 +29,8 @@ impl DualCaseOwnableImpl of DualCaseOwnableTrait { fn owner(self: @DualCaseOwnable) -> ContractAddress { let args = ArrayTrait::new(); - (*call_contract_syscall(*self.contract_address, selectors::owner, args.span()) - .unwrap_syscall() - .at(0)) - .try_into() - .unwrap() + call_contract_syscall(*self.contract_address, selectors::owner, args.span()) + .unwrap_and_cast() } fn transfer_ownership(self: @DualCaseOwnable, new_owner: ContractAddress) { diff --git a/src/openzeppelin/token/erc20/dual20.cairo b/src/openzeppelin/token/erc20/dual20.cairo index 9e0ece69e..2d9028f70 100644 --- a/src/openzeppelin/token/erc20/dual20.cairo +++ b/src/openzeppelin/token/erc20/dual20.cairo @@ -7,7 +7,6 @@ use starknet::call_contract_syscall; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use starknet::SyscallResultTrait; -use traits::Into; use traits::TryInto; use openzeppelin::utils::try_selector_with_fallback; @@ -15,6 +14,7 @@ use openzeppelin::utils::Felt252TryIntoBool; use openzeppelin::utils::BoolIntoFelt252; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; +use openzeppelin::utils::UnwrapAndCast; #[derive(Copy, Drop)] struct DualERC20 { @@ -55,33 +55,27 @@ impl DualERC20Impl of DualERC20Trait { fn decimals(self: @DualERC20) -> u8 { let args = ArrayTrait::new(); - (*call_contract_syscall(*self.contract_address, selectors::decimals, args.span()) - .unwrap_syscall() - .at(0)) - .try_into() - .unwrap() + call_contract_syscall(*self.contract_address, selectors::decimals, args.span()) + .unwrap_and_cast() } fn total_supply(self: @DualERC20) -> u256 { let mut args = ArrayTrait::new(); - let res = try_selector_with_fallback( + + try_selector_with_fallback( *self.contract_address, selectors::total_supply, selectors::totalSupply, args.span() ) - .unwrap_syscall(); - - u256 { low: (*res.at(0)).try_into().unwrap(), high: (*res.at(1)).try_into().unwrap(), } + .unwrap_and_cast() } fn balance_of(self: @DualERC20, account: ContractAddress) -> u256 { let mut args = ArrayTrait::new(); args.append_serde(account); - let res = try_selector_with_fallback( + try_selector_with_fallback( *self.contract_address, selectors::balance_of, selectors::balanceOf, args.span() ) - .unwrap_syscall(); - - u256 { low: (*res.at(0)).try_into().unwrap(), high: (*res.at(1)).try_into().unwrap(), } + .unwrap_and_cast() } fn allowance(self: @DualERC20, owner: ContractAddress, spender: ContractAddress) -> u256 { @@ -89,10 +83,8 @@ impl DualERC20Impl of DualERC20Trait { args.append_serde(owner); args.append_serde(spender); - let res = call_contract_syscall(*self.contract_address, selectors::allowance, args.span()) - .unwrap_syscall(); - - u256 { low: (*res.at(0)).try_into().unwrap(), high: (*res.at(1)).try_into().unwrap(), } + call_contract_syscall(*self.contract_address, selectors::allowance, args.span()) + .unwrap_and_cast() } fn transfer(self: @DualERC20, recipient: ContractAddress, amount: u256) -> bool { @@ -100,11 +92,8 @@ impl DualERC20Impl of DualERC20Trait { args.append_serde(recipient); args.append_serde(amount); - (*call_contract_syscall(*self.contract_address, selectors::transfer, args.span()) - .unwrap_syscall() - .at(0)) - .try_into() - .unwrap() + call_contract_syscall(*self.contract_address, selectors::transfer, args.span()) + .unwrap_and_cast() } fn transfer_from( @@ -115,13 +104,10 @@ impl DualERC20Impl of DualERC20Trait { args.append_serde(recipient); args.append_serde(amount); - (*try_selector_with_fallback( + try_selector_with_fallback( *self.contract_address, selectors::transfer_from, selectors::transferFrom, args.span() ) - .unwrap_syscall() - .at(0)) - .try_into() - .unwrap() + .unwrap_and_cast() } fn approve(self: @DualERC20, spender: ContractAddress, amount: u256) -> bool { @@ -129,10 +115,7 @@ impl DualERC20Impl of DualERC20Trait { args.append_serde(spender); args.append_serde(amount); - (*call_contract_syscall(*self.contract_address, selectors::approve, args.span()) - .unwrap_syscall() - .at(0)) - .try_into() - .unwrap() + call_contract_syscall(*self.contract_address, selectors::approve, args.span()) + .unwrap_and_cast() } } diff --git a/src/openzeppelin/token/erc721/dual721.cairo b/src/openzeppelin/token/erc721/dual721.cairo index b1d022686..35389f357 100644 --- a/src/openzeppelin/token/erc721/dual721.cairo +++ b/src/openzeppelin/token/erc721/dual721.cairo @@ -12,6 +12,7 @@ use openzeppelin::utils::try_selector_with_fallback; use openzeppelin::utils::Felt252TryIntoBool; use openzeppelin::utils::BoolIntoFelt252; use openzeppelin::utils::selectors; +use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::serde::SerializedAppend; #[derive(Copy, Drop)] @@ -75,38 +76,30 @@ impl DualCaseERC721Impl of DualCaseERC721Trait { let mut args = ArrayTrait::new(); args.append_serde(account); - let res = try_selector_with_fallback( + try_selector_with_fallback( *self.contract_address, selectors::balance_of, selectors::balanceOf, args.span() ) - .unwrap_syscall(); - - u256 { low: (*res.at(0)).try_into().unwrap(), high: (*res.at(1)).try_into().unwrap(), } + .unwrap_and_cast() } fn owner_of(self: @DualCaseERC721, token_id: u256) -> ContractAddress { let mut args = ArrayTrait::new(); args.append_serde(token_id); - (*try_selector_with_fallback( + try_selector_with_fallback( *self.contract_address, selectors::owner_of, selectors::ownerOf, args.span() ) - .unwrap_syscall() - .at(0)) - .try_into() - .unwrap() + .unwrap_and_cast() } fn get_approved(self: @DualCaseERC721, token_id: u256) -> ContractAddress { let mut args = ArrayTrait::new(); args.append_serde(token_id); - (*try_selector_with_fallback( + try_selector_with_fallback( *self.contract_address, selectors::get_approved, selectors::getApproved, args.span() ) - .unwrap_syscall() - .at(0)) - .try_into() - .unwrap() + .unwrap_and_cast() } fn is_approved_for_all( @@ -116,16 +109,13 @@ impl DualCaseERC721Impl of DualCaseERC721Trait { args.append_serde(owner); args.append_serde(operator); - (*try_selector_with_fallback( + try_selector_with_fallback( *self.contract_address, selectors::is_approved_for_all, selectors::isApprovedForAll, args.span() ) - .unwrap_syscall() - .at(0)) - .try_into() - .unwrap() + .unwrap_and_cast() } fn approve(self: @DualCaseERC721, to: ContractAddress, token_id: u256) { diff --git a/src/openzeppelin/utils.cairo b/src/openzeppelin/utils.cairo index 433693466..28e9ac044 100644 --- a/src/openzeppelin/utils.cairo +++ b/src/openzeppelin/utils.cairo @@ -1,14 +1,18 @@ -use starknet::call_contract_syscall; -use starknet::ContractAddress; -use starknet::SyscallResult; -use option::OptionTrait; -use array::ArrayTrait; -use array::SpanTrait; -use box::BoxTrait; mod constants; mod selectors; mod serde; +use array::ArrayTrait; +use array::SpanTrait; +use box::BoxTrait; +use option::OptionTrait; +use starknet::call_contract_syscall; +use starknet::ContractAddress; +use starknet::Felt252TryIntoContractAddress; +use starknet::SyscallResult; +use starknet::SyscallResultTrait; +use traits::TryInto; + fn try_selector_with_fallback( target: ContractAddress, snake_selector: felt252, camel_selector: felt252, args: Span ) -> SyscallResult> { @@ -57,3 +61,35 @@ fn check_gas() { }, } } + +trait UnwrapAndCast { + fn unwrap_and_cast(self: SyscallResult>) -> T; +} + +impl UnwrapAndCastBool of UnwrapAndCast { + fn unwrap_and_cast(self: SyscallResult>) -> bool { + (*self.unwrap_syscall().at(0)).try_into().unwrap() + } +} + +impl UnwrapAndCastContractAddress of UnwrapAndCast { + fn unwrap_and_cast(self: SyscallResult>) -> ContractAddress { + (*self.unwrap_syscall().at(0)).try_into().unwrap() + } +} + +impl UnwrapAndCastU8 of UnwrapAndCast { + fn unwrap_and_cast(self: SyscallResult>) -> u8 { + (*self.unwrap_syscall().at(0)).try_into().unwrap() + } +} + +impl UnwrapAndCastU256 of UnwrapAndCast { + fn unwrap_and_cast(self: SyscallResult>) -> u256 { + let unwrapped = self.unwrap_syscall(); + u256 { + low: (*unwrapped.at(0)).try_into().unwrap(), + high: (*unwrapped.at(1)).try_into().unwrap(), + } + } +} From bbcb8758b6b5c2a9d66b5c7efbdb43ccc0a40f5b Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Sat, 8 Jul 2023 14:26:09 -0400 Subject: [PATCH 059/246] add test_dual721 mod (#648) --- src/openzeppelin/tests/token.cairo | 1 + 1 file changed, 1 insertion(+) diff --git a/src/openzeppelin/tests/token.cairo b/src/openzeppelin/tests/token.cairo index a072d4a08..1020d4439 100644 --- a/src/openzeppelin/tests/token.cairo +++ b/src/openzeppelin/tests/token.cairo @@ -1,3 +1,4 @@ mod test_dual20; +mod test_dual721; mod test_erc20; mod test_erc721; From 5a27baa9918a4e2e4de3eb3eeee043104958310e Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Mon, 10 Jul 2023 14:30:01 +0200 Subject: [PATCH 060/246] Update account interface (#646) * feat: update interface and ids * refactor: format files * feat: update from reviews * Update src/openzeppelin/account/account.cairo Co-authored-by: Andrew Fleming --------- Co-authored-by: Andrew Fleming --- src/openzeppelin/account/account.cairo | 41 ++++++++++++---------- src/openzeppelin/account/interface.cairo | 11 +++--- src/openzeppelin/tests/test_account.cairo | 7 ++-- src/openzeppelin/token/erc721/erc721.cairo | 2 +- src/openzeppelin/utils/constants.cairo | 5 +-- 5 files changed, 36 insertions(+), 30 deletions(-) diff --git a/src/openzeppelin/account/account.cairo b/src/openzeppelin/account/account.cairo index 4061052b0..1823dfac7 100644 --- a/src/openzeppelin/account/account.cairo +++ b/src/openzeppelin/account/account.cairo @@ -29,7 +29,7 @@ trait AccountABI { #[view] fn get_public_key() -> felt252; #[view] - fn is_valid_signature(message: felt252, signature: Array) -> u32; + fn is_valid_signature(message: felt252, signature: Array) -> felt252; #[view] fn supports_interface(interface_id: felt252) -> bool; } @@ -47,9 +47,10 @@ mod Account { use starknet::get_contract_address; use zeroable::Zeroable; - use openzeppelin::account::interface::IAccount; - use openzeppelin::account::interface::IACCOUNT_ID; - use openzeppelin::account::interface::ERC1271_VALIDATED; + use openzeppelin::account::interface::IDeclarer; + use openzeppelin::account::interface::ISRC6; + use openzeppelin::account::interface::ISRC6_ID; + use openzeppelin::introspection::src5::ISRC5; use openzeppelin::introspection::src5::SRC5; use super::Call; @@ -66,7 +67,7 @@ mod Account { initializer(_public_key); } - impl AccountImpl of IAccount { + impl SRC6Impl of ISRC6 { fn __execute__(mut calls: Array) -> Array> { // Avoid calls from other contracts // https://github.com/OpenZeppelin/cairo-contracts/issues/344 @@ -87,18 +88,22 @@ mod Account { validate_transaction() } - fn __validate_declare__(class_hash: felt252) -> felt252 { - validate_transaction() - } - - fn is_valid_signature(message: felt252, signature: Array) -> u32 { + fn is_valid_signature(message: felt252, signature: Array) -> felt252 { if _is_valid_signature(message, signature.span()) { - ERC1271_VALIDATED + starknet::VALIDATED } else { 0 } } + } + + impl DeclarerImpl of IDeclarer { + fn __validate_declare__(class_hash: felt252) -> felt252 { + validate_transaction() + } + } + impl SRC5Impl of ISRC5 { fn supports_interface(interface_id: felt252) -> bool { SRC5::supports_interface(interface_id) } @@ -110,17 +115,17 @@ mod Account { #[external] fn __execute__(mut calls: Array) -> Array> { - AccountImpl::__execute__(calls) + SRC6Impl::__execute__(calls) } #[external] fn __validate__(mut calls: Array) -> felt252 { - AccountImpl::__validate__(calls) + SRC6Impl::__validate__(calls) } #[external] fn __validate_declare__(class_hash: felt252) -> felt252 { - AccountImpl::__validate_declare__(class_hash) + DeclarerImpl::__validate_declare__(class_hash) } #[external] @@ -146,13 +151,13 @@ mod Account { } #[view] - fn is_valid_signature(message: felt252, signature: Array) -> u32 { - AccountImpl::is_valid_signature(message, signature) + fn is_valid_signature(message: felt252, signature: Array) -> felt252 { + SRC6Impl::is_valid_signature(message, signature) } #[view] fn supports_interface(interface_id: felt252) -> bool { - AccountImpl::supports_interface(interface_id) + SRC5Impl::supports_interface(interface_id) } // @@ -161,7 +166,7 @@ mod Account { #[internal] fn initializer(_public_key: felt252) { - SRC5::register_interface(IACCOUNT_ID); + SRC5::register_interface(ISRC6_ID); public_key::write(_public_key); } diff --git a/src/openzeppelin/account/interface.cairo b/src/openzeppelin/account/interface.cairo index 6f30f12d7..75ee9f574 100644 --- a/src/openzeppelin/account/interface.cairo +++ b/src/openzeppelin/account/interface.cairo @@ -2,8 +2,7 @@ use array::ArrayTrait; use array::SpanTrait; use starknet::ContractAddress; -const IACCOUNT_ID: felt252 = 0x36c738c1c375b993078fe6b517d477e5a3c9b104e40c04662c4bdd3e2f5fa4a; -const ERC1271_VALIDATED: u32 = 0x1626ba7e_u32; +const ISRC6_ID: felt252 = 0x2ceccef7f994940b3962a6c67e0ba4fcd37df7d131417c604f91e03caecc1cd; #[derive(Serde, Drop)] struct Call { @@ -12,10 +11,12 @@ struct Call { calldata: Array } -trait IAccount { +trait ISRC6 { fn __execute__(calls: Array) -> Array>; fn __validate__(calls: Array) -> felt252; + fn is_valid_signature(message: felt252, signature: Array) -> felt252; +} + +trait IDeclarer { fn __validate_declare__(class_hash: felt252) -> felt252; - fn is_valid_signature(message: felt252, signature: Array) -> u32; - fn supports_interface(interface_id: felt252) -> bool; } diff --git a/src/openzeppelin/tests/test_account.cairo b/src/openzeppelin/tests/test_account.cairo index d39fa796c..ab076289d 100644 --- a/src/openzeppelin/tests/test_account.cairo +++ b/src/openzeppelin/tests/test_account.cairo @@ -10,8 +10,7 @@ use openzeppelin::account::Account; use openzeppelin::account::AccountABIDispatcher; use openzeppelin::account::AccountABIDispatcherTrait; use openzeppelin::account::interface::Call; -use openzeppelin::account::interface::ERC1271_VALIDATED; -use openzeppelin::account::interface::IACCOUNT_ID; +use openzeppelin::account::interface::ISRC6_ID; use openzeppelin::account::QUERY_VERSION; use openzeppelin::account::TRANSACTION_VERSION; use openzeppelin::introspection::src5::ISRC5_ID; @@ -106,7 +105,7 @@ fn test_interfaces() { let supports_default_interface = Account::supports_interface(ISRC5_ID); assert(supports_default_interface, 'Should support base interface'); - let supports_account_interface = Account::supports_interface(IACCOUNT_ID); + let supports_account_interface = Account::supports_interface(ISRC6_ID); assert(supports_account_interface, 'Should support account id'); } @@ -127,7 +126,7 @@ fn test_is_valid_signature() { Account::set_public_key(data.public_key); let is_valid = Account::is_valid_signature(message, good_signature); - assert(is_valid == ERC1271_VALIDATED, 'Should accept valid signature'); + assert(is_valid == starknet::VALIDATED, 'Should accept valid signature'); let is_valid = Account::is_valid_signature(message, bad_signature); assert(is_valid == 0, 'Should reject invalid signature'); diff --git a/src/openzeppelin/token/erc721/erc721.cairo b/src/openzeppelin/token/erc721/erc721.cairo index 6aaf0bc54..d61bc1a6f 100644 --- a/src/openzeppelin/token/erc721/erc721.cairo +++ b/src/openzeppelin/token/erc721/erc721.cairo @@ -456,7 +456,7 @@ mod ERC721 { } else { ISRC5Dispatcher { contract_address: to - }.supports_interface(account::interface::IACCOUNT_ID) + }.supports_interface(account::interface::ISRC6_ID) } } } diff --git a/src/openzeppelin/utils/constants.cairo b/src/openzeppelin/utils/constants.cairo index 7ab63d0da..9d2414bac 100644 --- a/src/openzeppelin/utils/constants.cairo +++ b/src/openzeppelin/utils/constants.cairo @@ -6,8 +6,9 @@ // See: https://github.com/starknet-io/SNIPs/pull/24 const ISRC5_ID: felt252 = 0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055; -// Account -const IACCOUNT_ID: felt252 = 0x36c738c1c375b993078fe6b517d477e5a3c9b104e40c04662c4bdd3e2f5fa4a; +// SRC6 +// See: https://github.com/starknet-io/SNIPs/pull/27 +const ISRC6_ID: felt252 = 0x2ceccef7f994940b3962a6c67e0ba4fcd37df7d131417c604f91e03caecc1cd; // ERC721 // See: https://eips.ethereum.org/EIPS/eip-721 From ba291339517928fd920d33903e7083bfd5eea631 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Wed, 12 Jul 2023 16:02:50 +0200 Subject: [PATCH 061/246] Add camel support for Account (#647) * feat: update interface and ids * refactor: format files * feat: update from reviews * feat: add dual interface to account * fix: tests * feat: add tests * feat: add more tests * feat: apply review suggestions * feat: apply review updates * Update src/openzeppelin/tests/account/test_dual_account.cairo Co-authored-by: Andrew Fleming --------- Co-authored-by: Andrew Fleming --- src/openzeppelin/account.cairo | 4 +- src/openzeppelin/account/account.cairo | 89 ++++-- src/openzeppelin/account/dual_account.cairo | 73 +++++ src/openzeppelin/account/interface.cairo | 8 +- src/openzeppelin/tests.cairo | 2 +- .../access/test_dual_accesscontrol.cairo | 24 +- .../tests/access/test_dual_ownable.cairo | 30 +- .../tests/access/test_ownable.cairo | 18 +- src/openzeppelin/tests/account.cairo | 2 + .../tests/{ => account}/test_account.cairo | 116 +++++++- .../tests/account/test_dual_account.cairo | 276 ++++++++++++++++++ src/openzeppelin/tests/mocks.cairo | 3 + .../tests/mocks/account_panic_mock.cairo | 57 ++++ .../mocks/camel_accesscontrol_mock.cairo | 1 - .../tests/mocks/camel_account_mock.cairo | 31 ++ .../tests/mocks/snake_account_mock.cairo | 53 ++++ src/openzeppelin/utils.cairo | 36 +-- src/openzeppelin/utils/selectors.cairo | 16 + src/openzeppelin/utils/unwrap_and_cast.cairo | 47 +++ 19 files changed, 775 insertions(+), 111 deletions(-) create mode 100644 src/openzeppelin/account/dual_account.cairo create mode 100644 src/openzeppelin/tests/account.cairo rename src/openzeppelin/tests/{ => account}/test_account.cairo (84%) create mode 100644 src/openzeppelin/tests/account/test_dual_account.cairo create mode 100644 src/openzeppelin/tests/mocks/account_panic_mock.cairo create mode 100644 src/openzeppelin/tests/mocks/camel_account_mock.cairo create mode 100644 src/openzeppelin/tests/mocks/snake_account_mock.cairo create mode 100644 src/openzeppelin/utils/unwrap_and_cast.cairo diff --git a/src/openzeppelin/account.cairo b/src/openzeppelin/account.cairo index 75f26fdf1..d995b6329 100644 --- a/src/openzeppelin/account.cairo +++ b/src/openzeppelin/account.cairo @@ -1,6 +1,8 @@ mod account; use account::{ - Account, AccountABIDispatcher, AccountABIDispatcherTrait, TRANSACTION_VERSION, QUERY_VERSION + Account, AccountABIDispatcher, AccountABIDispatcherTrait, AccountABICamelDispatcher, + AccountABICamelDispatcherTrait, TRANSACTION_VERSION, QUERY_VERSION }; +mod dual_account; mod interface; diff --git a/src/openzeppelin/account/account.cairo b/src/openzeppelin/account/account.cairo index 1823dfac7..3c7a860ea 100644 --- a/src/openzeppelin/account/account.cairo +++ b/src/openzeppelin/account/account.cairo @@ -2,11 +2,10 @@ use array::ArrayTrait; use array::SpanTrait; use option::OptionTrait; use serde::Serde; -use serde::deserialize_array_helper; -use serde::serialize_array_helper; use starknet::ContractAddress; use openzeppelin::account::interface::Call; +use openzeppelin::utils::serde::SpanSerde; const TRANSACTION_VERSION: felt252 = 1; // 2**128 + TRANSACTION_VERSION @@ -29,11 +28,34 @@ trait AccountABI { #[view] fn get_public_key() -> felt252; #[view] - fn is_valid_signature(message: felt252, signature: Array) -> felt252; + fn is_valid_signature(hash: felt252, signature: Array) -> felt252; #[view] fn supports_interface(interface_id: felt252) -> bool; } +// Entry points case-convention is enforced by the protocol +#[abi] +trait AccountABICamel { + #[external] + fn __execute__(calls: Array) -> Array>; + #[external] + fn __validate__(calls: Array) -> felt252; + #[external] + fn __validate_declare__(classHash: felt252) -> felt252; + #[external] + fn __validate_deploy__( + classHash: felt252, contractAddressSalt: felt252, _publicKey: felt252 + ) -> felt252; + #[external] + fn setPublicKey(newPublicKey: felt252); + #[view] + fn getPublicKey() -> felt252; + #[view] + fn isValidSignature(hash: felt252, signature: Array) -> felt252; + #[view] + fn supportsInterface(interfaceId: felt252) -> bool; +} + #[account_contract] mod Account { use array::SpanTrait; @@ -49,6 +71,7 @@ mod Account { use openzeppelin::account::interface::IDeclarer; use openzeppelin::account::interface::ISRC6; + use openzeppelin::account::interface::ISRC6Camel; use openzeppelin::account::interface::ISRC6_ID; use openzeppelin::introspection::src5::ISRC5; use openzeppelin::introspection::src5::SRC5; @@ -88,8 +111,8 @@ mod Account { validate_transaction() } - fn is_valid_signature(message: felt252, signature: Array) -> felt252 { - if _is_valid_signature(message, signature.span()) { + fn is_valid_signature(hash: felt252, signature: Array) -> felt252 { + if _is_valid_signature(hash, signature.span()) { starknet::VALIDATED } else { 0 @@ -97,6 +120,20 @@ mod Account { } } + impl SRC6CamelImpl of ISRC6Camel { + fn __execute__(mut calls: Array) -> Array> { + SRC6Impl::__execute__(calls) + } + + fn __validate__(mut calls: Array) -> felt252 { + SRC6Impl::__validate__(calls) + } + + fn isValidSignature(hash: felt252, signature: Array) -> felt252 { + SRC6Impl::is_valid_signature(hash, signature) + } + } + impl DeclarerImpl of IDeclarer { fn __validate_declare__(class_hash: felt252) -> felt252 { validate_transaction() @@ -141,6 +178,11 @@ mod Account { public_key::write(new_public_key); } + #[external] + fn setPublicKey(newPublicKey: felt252) { + set_public_key(newPublicKey); + } + // // View // @@ -151,8 +193,18 @@ mod Account { } #[view] - fn is_valid_signature(message: felt252, signature: Array) -> felt252 { - SRC6Impl::is_valid_signature(message, signature) + fn getPublicKey() -> felt252 { + get_public_key() + } + + #[view] + fn is_valid_signature(hash: felt252, signature: Array) -> felt252 { + SRC6Impl::is_valid_signature(hash, signature) + } + + #[view] + fn isValidSignature(hash: felt252, signature: Array) -> felt252 { + SRC6CamelImpl::isValidSignature(hash, signature) } #[view] @@ -160,6 +212,11 @@ mod Account { SRC5Impl::supports_interface(interface_id) } + #[view] + fn supportsInterface(interfaceId: felt252) -> bool { + supports_interface(interfaceId) + } + // // Internals // @@ -187,12 +244,12 @@ mod Account { } #[internal] - fn _is_valid_signature(message: felt252, signature: Span) -> bool { + fn _is_valid_signature(hash: felt252, signature: Span) -> bool { let valid_length = signature.len() == 2_u32; if valid_length { check_ecdsa_signature( - message, public_key::read(), *signature.at(0_u32), *signature.at(1_u32) + hash, public_key::read(), *signature.at(0_u32), *signature.at(1_u32) ) } else { false @@ -222,17 +279,3 @@ mod Account { starknet::call_contract_syscall(to, selector, calldata.span()).unwrap_syscall() } } - -impl SpanSerde< - T, impl TSerde: Serde, impl TCopy: Copy, impl TDrop: Drop -> of Serde> { - fn serialize(self: @Span, ref output: Array) { - (*self).len().serialize(ref output); - serialize_array_helper(*self, ref output); - } - fn deserialize(ref serialized: Span) -> Option> { - let length = *serialized.pop_front()?; - let mut arr = ArrayTrait::new(); - Option::Some(deserialize_array_helper(ref serialized, arr, length)?.span()) - } -} diff --git a/src/openzeppelin/account/dual_account.cairo b/src/openzeppelin/account/dual_account.cairo new file mode 100644 index 000000000..ad6903659 --- /dev/null +++ b/src/openzeppelin/account/dual_account.cairo @@ -0,0 +1,73 @@ +use array::ArrayTrait; +use array::SpanTrait; +use starknet::ContractAddress; +use starknet::SyscallResultTrait; + +use openzeppelin::utils::selectors; +use openzeppelin::utils::serde::SerializedAppend; +use openzeppelin::utils::try_selector_with_fallback; +use openzeppelin::utils::UnwrapAndCast; + +#[derive(Copy, Drop)] +struct DualCaseAccount { + contract_address: ContractAddress +} + +trait DualCaseAccountTrait { + fn set_public_key(self: @DualCaseAccount, new_public_key: felt252); + fn get_public_key(self: @DualCaseAccount) -> felt252; + fn is_valid_signature( + self: @DualCaseAccount, hash: felt252, signature: Array + ) -> felt252; + fn supports_interface(self: @DualCaseAccount, interface_id: felt252) -> bool; +} + +impl DualCaseAccountImpl of DualCaseAccountTrait { + fn set_public_key(self: @DualCaseAccount, new_public_key: felt252) { + let mut args = ArrayTrait::new(); + args.append_serde(new_public_key); + + try_selector_with_fallback( + *self.contract_address, selectors::set_public_key, selectors::setPublicKey, args.span() + ) + .unwrap_syscall(); + } + + fn get_public_key(self: @DualCaseAccount) -> felt252 { + let mut args = ArrayTrait::new(); + + try_selector_with_fallback( + *self.contract_address, selectors::get_public_key, selectors::getPublicKey, args.span() + ) + .unwrap_and_cast() + } + + fn is_valid_signature( + self: @DualCaseAccount, hash: felt252, signature: Array + ) -> felt252 { + let mut args = ArrayTrait::new(); + args.append_serde(hash); + args.append_serde(signature); + + try_selector_with_fallback( + *self.contract_address, + selectors::is_valid_signature, + selectors::isValidSignature, + args.span() + ) + .unwrap_and_cast() + } + + fn supports_interface(self: @DualCaseAccount, interface_id: felt252) -> bool { + let mut args = ArrayTrait::new(); + args.append_serde(interface_id); + + try_selector_with_fallback( + *self.contract_address, + selectors::supports_interface, + selectors::supportsInterface, + args.span() + ) + .unwrap_and_cast() + } +} diff --git a/src/openzeppelin/account/interface.cairo b/src/openzeppelin/account/interface.cairo index 75ee9f574..af454163c 100644 --- a/src/openzeppelin/account/interface.cairo +++ b/src/openzeppelin/account/interface.cairo @@ -14,7 +14,13 @@ struct Call { trait ISRC6 { fn __execute__(calls: Array) -> Array>; fn __validate__(calls: Array) -> felt252; - fn is_valid_signature(message: felt252, signature: Array) -> felt252; + fn is_valid_signature(hash: felt252, signature: Array) -> felt252; +} + +trait ISRC6Camel { + fn __execute__(calls: Array) -> Array>; + fn __validate__(calls: Array) -> felt252; + fn isValidSignature(hash: felt252, signature: Array) -> felt252; } trait IDeclarer { diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index 800541185..1aab31950 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -1,7 +1,7 @@ mod access; mod test_reentrancyguard; mod test_src5; -mod test_account; +mod account; mod token; mod test_initializable; mod test_pausable; diff --git a/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo b/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo index 19ae46a4f..4ddf7e8bc 100644 --- a/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo +++ b/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo @@ -18,9 +18,9 @@ use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils; use openzeppelin::utils::serde::SerializedAppend; -/// -/// Constants -/// +// +// Constants +// const ROLE: felt252 = 41; @@ -32,9 +32,9 @@ fn AUTHORIZED() -> ContractAddress { contract_address_const::<20>() } -/// -/// Setup -/// +// +// Setup +// fn setup_snake() -> (DualCaseAccessControl, IAccessControlDispatcher) { let mut calldata = ArrayTrait::new(); @@ -84,9 +84,9 @@ fn setup_accesscontrol_panic() -> (DualCaseAccessControl, DualCaseAccessControl) ) } -/// -/// snake_case target -/// +// +// snake_case target +// #[test] #[available_gas(2000000)] @@ -209,9 +209,9 @@ fn test_dual_renounce_role_exists_and_panics() { dispatcher.renounce_role(DEFAULT_ADMIN_ROLE, ADMIN()); } -/// -/// camelCase target -/// +// +// camelCase target +// #[test] #[available_gas(2000000)] diff --git a/src/openzeppelin/tests/access/test_dual_ownable.cairo b/src/openzeppelin/tests/access/test_dual_ownable.cairo index dca5595f7..c093838fc 100644 --- a/src/openzeppelin/tests/access/test_dual_ownable.cairo +++ b/src/openzeppelin/tests/access/test_dual_ownable.cairo @@ -18,9 +18,9 @@ use openzeppelin::tests::mocks::dual_ownable_mocks::CamelOwnablePanicMock; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils; -/// -/// Constants -/// +// +// Constants +// fn OWNER() -> ContractAddress { contract_address_const::<10>() @@ -30,9 +30,9 @@ fn NEW_OWNER() -> ContractAddress { contract_address_const::<20>() } -/// -/// Setup -/// +// +// Setup +// fn setup_snake() -> (DualCaseOwnable, IOwnableDispatcher) { let mut calldata = ArrayTrait::new(); @@ -74,9 +74,9 @@ fn setup_ownable_panic() -> (DualCaseOwnable, DualCaseOwnable) { ) } -/// -/// Case agnostic methods -/// +// +// Case agnostic methods +// #[test] #[available_gas(2000000)] @@ -103,9 +103,9 @@ fn test_dual_owner_exists_and_panics() { dispatcher.owner(); } -/// -/// snake_case target -/// +// +// snake_case target +// #[test] #[available_gas(2000000)] @@ -158,9 +158,9 @@ fn test_dual_renounce_ownership_exists_and_panics() { dispatcher.renounce_ownership(); } -/// -/// camelCase target -/// +// +// camelCase target +// #[test] #[available_gas(2000000)] diff --git a/src/openzeppelin/tests/access/test_ownable.cairo b/src/openzeppelin/tests/access/test_ownable.cairo index 99a717ff6..7f3980699 100644 --- a/src/openzeppelin/tests/access/test_ownable.cairo +++ b/src/openzeppelin/tests/access/test_ownable.cairo @@ -22,9 +22,9 @@ fn setup() { Ownable::initializer(); } -/// -/// initializer -/// +// +// initializer +// #[test] #[available_gas(2000000)] @@ -34,9 +34,9 @@ fn test_initializer() { assert(Ownable::owner() == OWNER(), 'Owner should be set'); } -/// -/// transfer_ownership & transferOwnership -/// +// +// transfer_ownership & transferOwnership +// #[test] #[available_gas(2000000)] @@ -102,9 +102,9 @@ fn test_transferOwnership_from_nonowner() { Ownable::transferOwnership(OTHER()); } -/// -/// renounce_ownership & renounceOwnership -/// +// +// renounce_ownership & renounceOwnership +// #[test] #[available_gas(2000000)] diff --git a/src/openzeppelin/tests/account.cairo b/src/openzeppelin/tests/account.cairo new file mode 100644 index 000000000..1708ec912 --- /dev/null +++ b/src/openzeppelin/tests/account.cairo @@ -0,0 +1,2 @@ +mod test_account; +mod test_dual_account; diff --git a/src/openzeppelin/tests/test_account.cairo b/src/openzeppelin/tests/account/test_account.cairo similarity index 84% rename from src/openzeppelin/tests/test_account.cairo rename to src/openzeppelin/tests/account/test_account.cairo index ab076289d..6ece0c5e9 100644 --- a/src/openzeppelin/tests/test_account.cairo +++ b/src/openzeppelin/tests/account/test_account.cairo @@ -2,8 +2,8 @@ use array::ArrayTrait; use core::traits::Into; use option::OptionTrait; use serde::Serde; -use starknet::ContractAddress; use starknet::contract_address_const; +use starknet::ContractAddress; use starknet::testing; use openzeppelin::account::Account; @@ -21,6 +21,10 @@ use openzeppelin::token::erc20::interface::IERC20DispatcherTrait; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; +// +// Constants +// + const PUBLIC_KEY: felt252 = 0x333333; const NEW_PUBKEY: felt252 = 0x789789; const SALT: felt252 = 123; @@ -50,6 +54,10 @@ fn SIGNED_TX_DATA() -> SignedTransactionData { } } +// +// Setup +// + fn setup_dispatcher(data: Option<@SignedTransactionData>) -> AccountABIDispatcher { // Set the transaction version testing::set_version(TRANSACTION_VERSION); @@ -72,7 +80,7 @@ fn setup_dispatcher(data: Option<@SignedTransactionData>) -> AccountABIDispatche calldata.append(PUBLIC_KEY); } - let address = utils::deploy(Account::TEST_CLASS_HASH, calldata); + let address = utils::deploy(CLASS_HASH(), calldata); AccountABIDispatcher { contract_address: address } } @@ -90,6 +98,10 @@ fn deploy_erc20(recipient: ContractAddress, initial_supply: u256) -> IERC20Dispa IERC20Dispatcher { contract_address: address } } +// +// constructor +// + #[test] #[available_gas(2000000)] fn test_constructor() { @@ -97,9 +109,13 @@ fn test_constructor() { assert(Account::get_public_key() == PUBLIC_KEY, 'Should return public key'); } +// +// supports_interface & supportsInterface +// + #[test] #[available_gas(2000000)] -fn test_interfaces() { +fn test_supports_interface() { Account::constructor(PUBLIC_KEY); let supports_default_interface = Account::supports_interface(ISRC5_ID); @@ -109,11 +125,50 @@ fn test_interfaces() { assert(supports_account_interface, 'Should support account id'); } +#[test] +#[available_gas(2000000)] +fn test_supportsInterface() { + Account::constructor(PUBLIC_KEY); + + let supports_default_interface = Account::supportsInterface(ISRC5_ID); + assert(supports_default_interface, 'Should support base interface'); + + let supports_account_interface = Account::supportsInterface(ISRC6_ID); + assert(supports_account_interface, 'Should support account id'); +} + +// +// is_valid_signature & isValidSignature +// + #[test] #[available_gas(2000000)] fn test_is_valid_signature() { let data = SIGNED_TX_DATA(); - let message = data.transaction_hash; + let hash = data.transaction_hash; + + let mut good_signature = ArrayTrait::new(); + good_signature.append(data.r); + good_signature.append(data.s); + + let mut bad_signature = ArrayTrait::new(); + bad_signature.append(0x987); + bad_signature.append(0x564); + + Account::set_public_key(data.public_key); + + let is_valid = Account::is_valid_signature(hash, good_signature); + assert(is_valid == starknet::VALIDATED, 'Should accept valid signature'); + + let is_valid = Account::is_valid_signature(hash, bad_signature); + assert(is_valid == 0, 'Should reject invalid signature'); +} + +#[test] +#[available_gas(2000000)] +fn test_isValidSignature() { + let data = SIGNED_TX_DATA(); + let hash = data.transaction_hash; let mut good_signature = ArrayTrait::new(); good_signature.append(data.r); @@ -125,13 +180,17 @@ fn test_is_valid_signature() { Account::set_public_key(data.public_key); - let is_valid = Account::is_valid_signature(message, good_signature); + let is_valid = Account::is_valid_signature(hash, good_signature); assert(is_valid == starknet::VALIDATED, 'Should accept valid signature'); - let is_valid = Account::is_valid_signature(message, bad_signature); + let is_valid = Account::is_valid_signature(hash, bad_signature); assert(is_valid == 0, 'Should reject invalid signature'); } +// +// Entry points +// + #[test] #[available_gas(2000000)] fn test_validate_deploy() { @@ -364,6 +423,21 @@ fn test_multicall_zero_calls() { assert(ret.len() == 0, 'Should have an empty response'); } +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Account: invalid caller', ))] +fn test_account_called_from_contract() { + let calls = ArrayTrait::new(); + let caller = contract_address_const::<0x123>(); + testing::set_contract_address(ACCOUNT_ADDRESS()); + testing::set_caller_address(caller); + Account::__execute__(calls); +} + +// +// set_public_key & get_public_key +// + #[test] #[available_gas(2000000)] fn test_public_key_setter_and_getter() { @@ -385,15 +459,29 @@ fn test_public_key_setter_different_account() { Account::set_public_key(NEW_PUBKEY); } +// +// setPublicKey & getPublicKey +// + #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Account: invalid caller', ))] -fn test_account_called_from_contract() { - let calls = ArrayTrait::new(); +fn test_public_key_setter_and_getter_camel() { + testing::set_contract_address(ACCOUNT_ADDRESS()); + testing::set_caller_address(ACCOUNT_ADDRESS()); + Account::setPublicKey(NEW_PUBKEY); + + let public_key = Account::getPublicKey(); + assert(public_key == NEW_PUBKEY, 'Should update key'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Account: unauthorized', ))] +fn test_public_key_setter_different_account_camel() { let caller = contract_address_const::<0x123>(); testing::set_contract_address(ACCOUNT_ADDRESS()); testing::set_caller_address(caller); - Account::__execute__(calls); + Account::setPublicKey(NEW_PUBKEY); } // @@ -429,7 +517,7 @@ fn test_assert_only_self_false() { #[available_gas(2000000)] fn test__is_valid_signature() { let data = SIGNED_TX_DATA(); - let message = data.transaction_hash; + let hash = data.transaction_hash; let mut good_signature = ArrayTrait::new(); good_signature.append(data.r); @@ -444,12 +532,12 @@ fn test__is_valid_signature() { Account::set_public_key(data.public_key); - let is_valid = Account::_is_valid_signature(message, good_signature.span()); + let is_valid = Account::_is_valid_signature(hash, good_signature.span()); assert(is_valid, 'Should accept valid signature'); - let is_valid = Account::_is_valid_signature(message, bad_signature.span()); + let is_valid = Account::_is_valid_signature(hash, bad_signature.span()); assert(!is_valid, 'Should reject invalid signature'); - let is_valid = Account::_is_valid_signature(message, invalid_length_signature.span()); + let is_valid = Account::_is_valid_signature(hash, invalid_length_signature.span()); assert(!is_valid, 'Should reject invalid length'); } diff --git a/src/openzeppelin/tests/account/test_dual_account.cairo b/src/openzeppelin/tests/account/test_dual_account.cairo new file mode 100644 index 000000000..946119a6c --- /dev/null +++ b/src/openzeppelin/tests/account/test_dual_account.cairo @@ -0,0 +1,276 @@ +use array::ArrayTrait; +use starknet::testing; + +use openzeppelin::account::dual_account::DualCaseAccount; +use openzeppelin::account::dual_account::DualCaseAccountTrait; +use openzeppelin::account::AccountABICamelDispatcher; +use openzeppelin::account::AccountABICamelDispatcherTrait; +use openzeppelin::account::AccountABIDispatcher; +use openzeppelin::account::AccountABIDispatcherTrait; +use openzeppelin::introspection::src5::ISRC5_ID; +use openzeppelin::tests::account::test_account::SIGNED_TX_DATA; +use openzeppelin::tests::mocks::account_panic_mock::CamelAccountPanicMock; +use openzeppelin::tests::mocks::account_panic_mock::SnakeAccountPanicMock; +use openzeppelin::tests::mocks::camel_account_mock::CamelAccountMock; +use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; +use openzeppelin::tests::mocks::snake_account_mock::SnakeAccountMock; +use openzeppelin::tests::utils; +use openzeppelin::utils::serde::SerializedAppend; + +// +// Constants +// + +const PUBLIC_KEY: felt252 = 0x333333; +const NEW_PUBLIC_KEY: felt252 = 0x444444; + +// +// Setup +// + +fn setup_snake() -> (DualCaseAccount, AccountABIDispatcher) { + let mut calldata = ArrayTrait::new(); + calldata.append_serde(PUBLIC_KEY); + let target = utils::deploy(SnakeAccountMock::TEST_CLASS_HASH, calldata); + ( + DualCaseAccount { + contract_address: target + }, AccountABIDispatcher { + contract_address: target + } + ) +} + +fn setup_camel() -> (DualCaseAccount, AccountABICamelDispatcher) { + let mut calldata = ArrayTrait::new(); + calldata.append_serde(PUBLIC_KEY); + let target = utils::deploy(CamelAccountMock::TEST_CLASS_HASH, calldata); + ( + DualCaseAccount { + contract_address: target + }, AccountABICamelDispatcher { + contract_address: target + } + ) +} + +fn setup_non_account() -> DualCaseAccount { + let calldata = ArrayTrait::new(); + let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); + DualCaseAccount { contract_address: target } +} + +fn setup_account_panic() -> (DualCaseAccount, DualCaseAccount) { + let snake_target = utils::deploy(SnakeAccountPanicMock::TEST_CLASS_HASH, ArrayTrait::new()); + let camel_target = utils::deploy(CamelAccountPanicMock::TEST_CLASS_HASH, ArrayTrait::new()); + ( + DualCaseAccount { + contract_address: snake_target + }, DualCaseAccount { + contract_address: camel_target + } + ) +} + +// +// snake_case target +// + +#[test] +#[available_gas(2000000)] +fn test_dual_set_public_key() { + let (snake_dispatcher, target) = setup_snake(); + + testing::set_contract_address(snake_dispatcher.contract_address); + + snake_dispatcher.set_public_key(NEW_PUBLIC_KEY); + assert(target.get_public_key() == NEW_PUBLIC_KEY, 'Should return NEW_PUBLIC_KEY'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_set_public_key() { + let dispatcher = setup_non_account(); + dispatcher.set_public_key(NEW_PUBLIC_KEY); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_set_public_key_exists_and_panics() { + let (dispatcher, _) = setup_account_panic(); + dispatcher.set_public_key(NEW_PUBLIC_KEY); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_get_public_key() { + let (snake_dispatcher, _) = setup_snake(); + assert(snake_dispatcher.get_public_key() == PUBLIC_KEY, 'Should return PUBLIC_KEY'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_get_public_key() { + let dispatcher = setup_non_account(); + dispatcher.get_public_key(); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_get_public_key_exists_and_panics() { + let (dispatcher, _) = setup_account_panic(); + dispatcher.get_public_key(); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_is_valid_signature() { + let (snake_dispatcher, target) = setup_snake(); + + let data = SIGNED_TX_DATA(); + let hash = data.transaction_hash; + + let mut signature = ArrayTrait::new(); + signature.append(data.r); + signature.append(data.s); + + testing::set_contract_address(snake_dispatcher.contract_address); + target.set_public_key(data.public_key); + + let is_valid = snake_dispatcher.is_valid_signature(hash, signature); + assert(is_valid == 'VALID', 'Should accept valid signature'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_is_valid_signature() { + let hash = 0x0; + let signature = ArrayTrait::::new(); + + let dispatcher = setup_non_account(); + dispatcher.is_valid_signature(hash, signature); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_is_valid_signature_exists_and_panics() { + let hash = 0x0; + let signature = ArrayTrait::::new(); + + let (dispatcher, _) = setup_account_panic(); + dispatcher.is_valid_signature(hash, signature); +} + + +#[test] +#[available_gas(2000000)] +fn test_dual_supports_interface() { + let (snake_dispatcher, target) = setup_snake(); + assert(snake_dispatcher.supports_interface(ISRC5_ID), 'Should implement ISRC5'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_supports_interface() { + let dispatcher = setup_non_account(); + dispatcher.supports_interface(ISRC5_ID); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_supports_interface_exists_and_panics() { + let (dispatcher, _) = setup_account_panic(); + dispatcher.supports_interface(ISRC5_ID); +} + +// +// camelCase target +// + +#[test] +#[available_gas(2000000)] +fn test_dual_setPublicKey() { + let (camel_dispatcher, target) = setup_camel(); + + testing::set_contract_address(camel_dispatcher.contract_address); + + camel_dispatcher.set_public_key(NEW_PUBLIC_KEY); + assert(target.getPublicKey() == NEW_PUBLIC_KEY, 'Should return NEW_PUBLIC_KEY'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_setPublicKey_exists_and_panics() { + let (_, dispatcher) = setup_account_panic(); + dispatcher.set_public_key(NEW_PUBLIC_KEY); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_getPublicKey() { + let (camel_dispatcher, _) = setup_camel(); + assert(camel_dispatcher.get_public_key() == PUBLIC_KEY, 'Should return PUBLIC_KEY'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_getPublicKey_exists_and_panics() { + let (_, dispatcher) = setup_account_panic(); + dispatcher.get_public_key(); +} + +#[test] +#[available_gas(2000000)] +fn test_dual_isValidSignature() { + let (camel_dispatcher, target) = setup_camel(); + + let data = SIGNED_TX_DATA(); + let hash = data.transaction_hash; + + let mut signature = ArrayTrait::new(); + signature.append(data.r); + signature.append(data.s); + + testing::set_contract_address(camel_dispatcher.contract_address); + target.setPublicKey(data.public_key); + + let is_valid = camel_dispatcher.is_valid_signature(hash, signature); + assert(is_valid == 'VALID', 'Should accept valid signature'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_isValidSignature_exists_and_panics() { + let hash = 0x0; + let signature = ArrayTrait::::new(); + + let (_, dispatcher) = setup_account_panic(); + dispatcher.is_valid_signature(hash, signature); +} + + +#[test] +#[available_gas(2000000)] +fn test_dual_supportsInterface() { + let (camel_dispatcher, _) = setup_camel(); + assert(camel_dispatcher.supports_interface(ISRC5_ID), 'Should implement ISRC5'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_supportsInterface_exists_and_panics() { + let (_, dispatcher) = setup_account_panic(); + dispatcher.supports_interface(ISRC5_ID); +} diff --git a/src/openzeppelin/tests/mocks.cairo b/src/openzeppelin/tests/mocks.cairo index 325546d0c..86bd7ce90 100644 --- a/src/openzeppelin/tests/mocks.cairo +++ b/src/openzeppelin/tests/mocks.cairo @@ -8,8 +8,11 @@ mod snake20_mock; mod erc20_panic; mod non721_mock; mod accesscontrol_panic_mock; +mod account_panic_mock; mod camel_accesscontrol_mock; +mod camel_account_mock; mod snake_accesscontrol_mock; +mod snake_account_mock; mod dual_ownable_mocks; mod snake721_mock; mod camel721_mock; diff --git a/src/openzeppelin/tests/mocks/account_panic_mock.cairo b/src/openzeppelin/tests/mocks/account_panic_mock.cairo new file mode 100644 index 000000000..f3809b807 --- /dev/null +++ b/src/openzeppelin/tests/mocks/account_panic_mock.cairo @@ -0,0 +1,57 @@ +// Although these modules are designed to panic, functions +// still need a valid return value. We chose: +// +// 3 for felt252 +// false for bool + +#[account_contract] +mod SnakeAccountPanicMock { + #[external] + fn set_public_key(new_public_key: felt252) { + panic_with_felt252('Some error'); + } + + #[view] + fn get_public_key() -> felt252 { + panic_with_felt252('Some error'); + 3 + } + + #[view] + fn is_valid_signature(hash: felt252, signature: Array) -> felt252 { + panic_with_felt252('Some error'); + 3 + } + + #[view] + fn supports_interface(interface_id: felt252) -> bool { + panic_with_felt252('Some error'); + false + } +} + +#[account_contract] +mod CamelAccountPanicMock { + #[external] + fn setPublicKey(newPublicKey: felt252) { + panic_with_felt252('Some error'); + } + + #[view] + fn getPublicKey() -> felt252 { + panic_with_felt252('Some error'); + 3 + } + + #[view] + fn isValidSignature(hash: felt252, signature: Array) -> felt252 { + panic_with_felt252('Some error'); + 3 + } + + #[view] + fn supportsInterface(interfaceId: felt252) -> bool { + panic_with_felt252('Some error'); + false + } +} diff --git a/src/openzeppelin/tests/mocks/camel_accesscontrol_mock.cairo b/src/openzeppelin/tests/mocks/camel_accesscontrol_mock.cairo index 4d6cc6f7e..b1f898855 100644 --- a/src/openzeppelin/tests/mocks/camel_accesscontrol_mock.cairo +++ b/src/openzeppelin/tests/mocks/camel_accesscontrol_mock.cairo @@ -4,7 +4,6 @@ mod CamelAccessControlMock { use starknet::get_caller_address; use openzeppelin::access::accesscontrol::AccessControl; use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; - use openzeppelin::utils::serde::SpanSerde; #[constructor] fn constructor(admin: ContractAddress) { diff --git a/src/openzeppelin/tests/mocks/camel_account_mock.cairo b/src/openzeppelin/tests/mocks/camel_account_mock.cairo new file mode 100644 index 000000000..01ee5b53e --- /dev/null +++ b/src/openzeppelin/tests/mocks/camel_account_mock.cairo @@ -0,0 +1,31 @@ +#[account_contract] +mod CamelAccountMock { + use openzeppelin::account::interface::Call; + use openzeppelin::account::Account; + use openzeppelin::utils::serde::SpanSerde; + + #[constructor] + fn constructor(_publicKey: felt252) { + Account::initializer(_publicKey); + } + + #[external] + fn setPublicKey(newPublicKey: felt252) { + Account::setPublicKey(newPublicKey); + } + + #[view] + fn getPublicKey() -> felt252 { + Account::getPublicKey() + } + + #[view] + fn isValidSignature(hash: felt252, signature: Array) -> felt252 { + Account::isValidSignature(hash, signature) + } + + #[view] + fn supportsInterface(interfaceId: felt252) -> bool { + Account::supportsInterface(interfaceId) + } +} diff --git a/src/openzeppelin/tests/mocks/snake_account_mock.cairo b/src/openzeppelin/tests/mocks/snake_account_mock.cairo new file mode 100644 index 000000000..03b9cd05d --- /dev/null +++ b/src/openzeppelin/tests/mocks/snake_account_mock.cairo @@ -0,0 +1,53 @@ +#[account_contract] +mod SnakeAccountMock { + use openzeppelin::account::interface::Call; + use openzeppelin::account::Account; + use openzeppelin::utils::serde::SpanSerde; + + #[constructor] + fn constructor(_public_key: felt252) { + Account::initializer(_public_key); + } + + #[external] + fn __execute__(mut calls: Array) -> Array> { + Account::__execute__(calls) + } + + #[external] + fn __validate__(mut calls: Array) -> felt252 { + Account::__validate__(calls) + } + + #[external] + fn __validate_declare__(class_hash: felt252) -> felt252 { + Account::__validate_declare__(class_hash) + } + + #[external] + fn __validate_deploy__( + class_hash: felt252, contract_address_salt: felt252, _public_key: felt252 + ) -> felt252 { + Account::__validate_deploy__(class_hash, contract_address_salt, _public_key) + } + + #[external] + fn set_public_key(new_public_key: felt252) { + Account::set_public_key(new_public_key); + } + + #[view] + fn get_public_key() -> felt252 { + Account::get_public_key() + } + + #[view] + fn is_valid_signature(hash: felt252, signature: Array) -> felt252 { + Account::is_valid_signature(hash, signature) + } + + #[view] + fn supports_interface(interface_id: felt252) -> bool { + Account::supports_interface(interface_id) + } +} diff --git a/src/openzeppelin/utils.cairo b/src/openzeppelin/utils.cairo index 28e9ac044..e8d356c77 100644 --- a/src/openzeppelin/utils.cairo +++ b/src/openzeppelin/utils.cairo @@ -1,6 +1,7 @@ mod constants; mod selectors; mod serde; +mod unwrap_and_cast; use array::ArrayTrait; use array::SpanTrait; @@ -8,10 +9,9 @@ use box::BoxTrait; use option::OptionTrait; use starknet::call_contract_syscall; use starknet::ContractAddress; -use starknet::Felt252TryIntoContractAddress; use starknet::SyscallResult; use starknet::SyscallResultTrait; -use traits::TryInto; +use unwrap_and_cast::UnwrapAndCast; fn try_selector_with_fallback( target: ContractAddress, snake_selector: felt252, camel_selector: felt252, args: Span @@ -61,35 +61,3 @@ fn check_gas() { }, } } - -trait UnwrapAndCast { - fn unwrap_and_cast(self: SyscallResult>) -> T; -} - -impl UnwrapAndCastBool of UnwrapAndCast { - fn unwrap_and_cast(self: SyscallResult>) -> bool { - (*self.unwrap_syscall().at(0)).try_into().unwrap() - } -} - -impl UnwrapAndCastContractAddress of UnwrapAndCast { - fn unwrap_and_cast(self: SyscallResult>) -> ContractAddress { - (*self.unwrap_syscall().at(0)).try_into().unwrap() - } -} - -impl UnwrapAndCastU8 of UnwrapAndCast { - fn unwrap_and_cast(self: SyscallResult>) -> u8 { - (*self.unwrap_syscall().at(0)).try_into().unwrap() - } -} - -impl UnwrapAndCastU256 of UnwrapAndCast { - fn unwrap_and_cast(self: SyscallResult>) -> u256 { - let unwrapped = self.unwrap_syscall(); - u256 { - low: (*unwrapped.at(0)).try_into().unwrap(), - high: (*unwrapped.at(1)).try_into().unwrap(), - } - } -} diff --git a/src/openzeppelin/utils/selectors.cairo b/src/openzeppelin/utils/selectors.cairo index 8c17091ce..6541e28ab 100644 --- a/src/openzeppelin/utils/selectors.cairo +++ b/src/openzeppelin/utils/selectors.cairo @@ -64,3 +64,19 @@ const total_supply: felt252 = 0x1557182e4359a1f0c6301278e8f5b35a776ab58d39892581 const totalSupply: felt252 = 0x80aa9fdbfaf9615e4afc7f5f722e265daca5ccc655360fa5ccacf9c267936d; const allowance: felt252 = 0x1e888a1026b19c8c0b57c72d63ed1737106aa10034105b980ba117bd0c29fe1; const transfer: felt252 = 0x83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e; + +// +// Account +// + +const set_public_key: felt252 = 0x2e3e21ff5952b2531241e37999d9c4c8b3034cccc89a202a6bf019bdf5294f9; +const setPublicKey: felt252 = 0xbc0eb87884ab91e330445c3584a50d7ddf4b568f02fbeb456a6242cce3f5d9; +const get_public_key: felt252 = 0x1a35984e05126dbecb7c3bb9929e7dd9106d460c59b1633739a5c733a5fb13b; +const getPublicKey: felt252 = 0x1a6c6a0bdec86cc645c91997d8eea83e87148659e3e61122f72361fd5e94079; +const is_valid_signature: felt252 = + 0x28420862938116cb3bbdbedee07451ccc54d4e9412dbef71142ad1980a30941; +const isValidSignature: felt252 = 0x213dfe25e2ca309c4d615a09cfc95fdb2fc7dc73fbcad12c450fe93b1f2ff9e; +const supports_interface: felt252 = + 0xfe80f537b66d12a00b6d3c072b44afbb716e78dde5c3f0ef116ee93d3e3283; +const supportsInterface: felt252 = + 0x29e211664c0b63c79638fbea474206ca74016b3e9a3dc4f9ac300ffd8bdf2cd; diff --git a/src/openzeppelin/utils/unwrap_and_cast.cairo b/src/openzeppelin/utils/unwrap_and_cast.cairo new file mode 100644 index 000000000..0e3959664 --- /dev/null +++ b/src/openzeppelin/utils/unwrap_and_cast.cairo @@ -0,0 +1,47 @@ +use array::SpanTrait; +use option::OptionTrait; +use starknet::ContractAddress; +use starknet::Felt252TryIntoContractAddress; +use starknet::SyscallResult; +use starknet::SyscallResultTrait; +use traits::TryInto; + +use openzeppelin::utils::Felt252TryIntoBool; + +trait UnwrapAndCast { + fn unwrap_and_cast(self: SyscallResult>) -> T; +} + +impl UnwrapAndCastBool of UnwrapAndCast { + fn unwrap_and_cast(self: SyscallResult>) -> bool { + (*self.unwrap_syscall().at(0)).try_into().unwrap() + } +} + +impl UnwrapAndCastContractAddress of UnwrapAndCast { + fn unwrap_and_cast(self: SyscallResult>) -> ContractAddress { + (*self.unwrap_syscall().at(0)).try_into().unwrap() + } +} + +impl UnwrapFelt of UnwrapAndCast { + fn unwrap_and_cast(self: SyscallResult>) -> felt252 { + *self.unwrap_syscall().at(0) + } +} + +impl UnwrapAndCastU8 of UnwrapAndCast { + fn unwrap_and_cast(self: SyscallResult>) -> u8 { + (*self.unwrap_syscall().at(0)).try_into().unwrap() + } +} + +impl UnwrapAndCastU256 of UnwrapAndCast { + fn unwrap_and_cast(self: SyscallResult>) -> u256 { + let unwrapped = self.unwrap_syscall(); + u256 { + low: (*unwrapped.at(0)).try_into().unwrap(), + high: (*unwrapped.at(1)).try_into().unwrap(), + } + } +} From b44f7dc53cb42594b12dfa65adc63ba0f8d6cc9a Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Wed, 12 Jul 2023 10:23:10 -0400 Subject: [PATCH 062/246] Add src5 dual dispatcher (#659) * add dual src5 * add dual src5 tests and mocks * add felt impl * remove unused imports, integrate unwrap_and_cast for felts * add supports_interface to interface * add supports_interface to erc721 interface * add camelCase supportsInterface test * add dual supports_interface to mocks * add dual supports_interface tests * add dual supports_interface to tests * add comment * fix formatting * clean up test * add comments * remove unused imports * fix interface * fix src5 impl * apply review suggestion --- .../access/accesscontrol/accesscontrol.cairo | 23 ++++- .../accesscontrol/dual_accesscontrol.cairo | 24 +++-- src/openzeppelin/introspection.cairo | 1 + .../introspection/dual_src5.cairo | 31 ++++++ src/openzeppelin/introspection/src5.cairo | 16 ++++ src/openzeppelin/tests.cairo | 2 +- .../tests/access/test_accesscontrol.cairo | 9 +- .../access/test_dual_accesscontrol.cairo | 39 ++++++++ src/openzeppelin/tests/introspection.cairo | 2 + .../tests/introspection/test_dual_src5.cairo | 94 +++++++++++++++++++ .../tests/{ => introspection}/test_src5.cairo | 0 src/openzeppelin/tests/mocks.cairo | 1 + .../mocks/accesscontrol_panic_mock.cairo | 12 +++ .../tests/mocks/camel721_mock.cairo | 2 +- .../mocks/camel_accesscontrol_mock.cairo | 5 + .../tests/mocks/erc721_panic_mock.cairo | 12 +++ .../mocks/snake_accesscontrol_mock.cairo | 5 + src/openzeppelin/tests/mocks/src5_mocks.cairo | 35 +++++++ .../tests/token/test_dual721.cairo | 39 ++++++++ src/openzeppelin/token/erc20/dual20.cairo | 18 +--- src/openzeppelin/token/erc721/dual721.cairo | 37 ++++---- src/openzeppelin/token/erc721/erc721.cairo | 30 ++++-- 22 files changed, 384 insertions(+), 53 deletions(-) create mode 100644 src/openzeppelin/introspection/dual_src5.cairo create mode 100644 src/openzeppelin/tests/introspection.cairo create mode 100644 src/openzeppelin/tests/introspection/test_dual_src5.cairo rename src/openzeppelin/tests/{ => introspection}/test_src5.cairo (100%) create mode 100644 src/openzeppelin/tests/mocks/src5_mocks.cairo diff --git a/src/openzeppelin/access/accesscontrol/accesscontrol.cairo b/src/openzeppelin/access/accesscontrol/accesscontrol.cairo index af4e9ce04..fd7dbf36c 100644 --- a/src/openzeppelin/access/accesscontrol/accesscontrol.cairo +++ b/src/openzeppelin/access/accesscontrol/accesscontrol.cairo @@ -1,7 +1,7 @@ #[contract] mod AccessControl { use openzeppelin::access::accesscontrol::interface; - use openzeppelin::introspection::src5::SRC5; + use openzeppelin::introspection::src5; use starknet::ContractAddress; use starknet::get_caller_address; @@ -32,6 +32,18 @@ mod AccessControl { #[event] fn RoleAdminChanged(role: felt252, previous_admin_role: felt252, new_admin_role: felt252) {} + impl ISRC5Impl of src5::ISRC5 { + fn supports_interface(interface_id: felt252) -> bool { + src5::SRC5::supports_interface(interface_id) + } + } + + impl ISRC5CamelImpl of src5::ISRC5Camel { + fn supportsInterface(interfaceId: felt252) -> bool { + src5::SRC5::supportsInterface(interfaceId) + } + } + impl AccessControlImpl of interface::IAccessControl { fn has_role(role: felt252, account: ContractAddress) -> bool { role_members::read((role, account)) @@ -88,7 +100,12 @@ mod AccessControl { #[view] fn supports_interface(interface_id: felt252) -> bool { - SRC5::supports_interface(interface_id) + ISRC5Impl::supports_interface(interface_id) + } + + #[view] + fn supportsInterface(interfaceId: felt252) -> bool { + ISRC5CamelImpl::supportsInterface(interfaceId) } #[view] @@ -151,7 +168,7 @@ mod AccessControl { #[internal] fn initializer() { - SRC5::register_interface(interface::IACCESSCONTROL_ID); + src5::SRC5::register_interface(interface::IACCESSCONTROL_ID); } #[internal] diff --git a/src/openzeppelin/access/accesscontrol/dual_accesscontrol.cairo b/src/openzeppelin/access/accesscontrol/dual_accesscontrol.cairo index 032c4f452..e4955afd3 100644 --- a/src/openzeppelin/access/accesscontrol/dual_accesscontrol.cairo +++ b/src/openzeppelin/access/accesscontrol/dual_accesscontrol.cairo @@ -1,11 +1,6 @@ use array::ArrayTrait; -use array::SpanTrait; -use core::result::ResultTrait; -use option::OptionTrait; use starknet::ContractAddress; -use starknet::Felt252TryIntoContractAddress; use starknet::SyscallResultTrait; -use traits::TryInto; use openzeppelin::utils::Felt252TryIntoBool; use openzeppelin::utils::selectors; @@ -24,6 +19,7 @@ trait DualCaseAccessControlTrait { fn grant_role(self: @DualCaseAccessControl, role: felt252, account: ContractAddress); fn revoke_role(self: @DualCaseAccessControl, role: felt252, account: ContractAddress); fn renounce_role(self: @DualCaseAccessControl, role: felt252, account: ContractAddress); + fn supports_interface(self: @DualCaseAccessControl, interface_id: felt252) -> bool; } impl DualCaseAccessControlImpl of DualCaseAccessControlTrait { @@ -42,11 +38,10 @@ impl DualCaseAccessControlImpl of DualCaseAccessControlTrait { let mut args = ArrayTrait::new(); args.append_serde(role); - *try_selector_with_fallback( + try_selector_with_fallback( *self.contract_address, selectors::get_role_admin, selectors::getRoleAdmin, args.span() ) - .unwrap_syscall() - .at(0) + .unwrap_and_cast() } fn grant_role(self: @DualCaseAccessControl, role: felt252, account: ContractAddress) { @@ -81,4 +76,17 @@ impl DualCaseAccessControlImpl of DualCaseAccessControlTrait { ) .unwrap_syscall(); } + + fn supports_interface(self: @DualCaseAccessControl, interface_id: felt252) -> bool { + let mut args = ArrayTrait::new(); + args.append_serde(interface_id); + + try_selector_with_fallback( + *self.contract_address, + selectors::supports_interface, + selectors::supportsInterface, + args.span() + ) + .unwrap_and_cast() + } } diff --git a/src/openzeppelin/introspection.cairo b/src/openzeppelin/introspection.cairo index 2d1a93a9f..0819e4257 100644 --- a/src/openzeppelin/introspection.cairo +++ b/src/openzeppelin/introspection.cairo @@ -1 +1,2 @@ mod src5; +mod dual_src5; diff --git a/src/openzeppelin/introspection/dual_src5.cairo b/src/openzeppelin/introspection/dual_src5.cairo new file mode 100644 index 000000000..f693aac45 --- /dev/null +++ b/src/openzeppelin/introspection/dual_src5.cairo @@ -0,0 +1,31 @@ +use array::ArrayTrait; +use starknet::ContractAddress; + +use openzeppelin::utils::selectors; +use openzeppelin::utils::serde::SerializedAppend; +use openzeppelin::utils::try_selector_with_fallback; +use openzeppelin::utils::UnwrapAndCast; + +#[derive(Copy, Drop)] +struct DualCaseSRC5 { + contract_address: ContractAddress +} + +trait DualCaseSRC5Trait { + fn supports_interface(self: @DualCaseSRC5, interface_id: felt252) -> bool; +} + +impl DualCaseSRC5Impl of DualCaseSRC5Trait { + fn supports_interface(self: @DualCaseSRC5, interface_id: felt252) -> bool { + let mut args = ArrayTrait::new(); + args.append_serde(interface_id); + + try_selector_with_fallback( + *self.contract_address, + selectors::supports_interface, + selectors::supportsInterface, + args.span() + ) + .unwrap_and_cast() + } +} diff --git a/src/openzeppelin/introspection/src5.cairo b/src/openzeppelin/introspection/src5.cairo index 424a6faf1..2b0384098 100644 --- a/src/openzeppelin/introspection/src5.cairo +++ b/src/openzeppelin/introspection/src5.cairo @@ -5,6 +5,11 @@ trait ISRC5 { fn supports_interface(interface_id: felt252) -> bool; } +#[abi] +trait ISRC5Camel { + fn supportsInterface(interfaceId: felt252) -> bool; +} + #[contract] mod SRC5 { use openzeppelin::introspection::src5; @@ -22,11 +27,22 @@ mod SRC5 { } } + impl SRC5CamelImpl of src5::ISRC5Camel { + fn supportsInterface(interfaceId: felt252) -> bool { + SRC5Impl::supports_interface(interfaceId) + } + } + #[view] fn supports_interface(interface_id: felt252) -> bool { SRC5Impl::supports_interface(interface_id) } + #[view] + fn supportsInterface(interfaceId: felt252) -> bool { + SRC5CamelImpl::supportsInterface(interfaceId) + } + #[internal] fn register_interface(interface_id: felt252) { supported_interfaces::write(interface_id, true); diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index 1aab31950..0110b5abc 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -1,6 +1,6 @@ mod access; mod test_reentrancyguard; -mod test_src5; +mod introspection; mod account; mod token; mod test_initializable; diff --git a/src/openzeppelin/tests/access/test_accesscontrol.cairo b/src/openzeppelin/tests/access/test_accesscontrol.cairo index 241f01553..5e6c71489 100644 --- a/src/openzeppelin/tests/access/test_accesscontrol.cairo +++ b/src/openzeppelin/tests/access/test_accesscontrol.cairo @@ -41,7 +41,7 @@ fn test_initializer() { } // -// supports_interface +// supports_interface & supportsInterface // #[test] @@ -51,6 +51,13 @@ fn test_supports_interface() { assert(AccessControl::supports_interface(IACCESSCONTROL_ID), 'Should support own interface'); } +#[test] +#[available_gas(2000000)] +fn test_supportsInterface() { + AccessControl::initializer(); + assert(AccessControl::supportsInterface(IACCESSCONTROL_ID), 'Should support own interface'); +} + // // has_role & hasRole // diff --git a/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo b/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo index 4ddf7e8bc..83908f95d 100644 --- a/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo +++ b/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo @@ -4,6 +4,7 @@ use starknet::contract_address_const; use starknet::testing::set_contract_address; use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; +use openzeppelin::access::accesscontrol::interface::IACCESSCONTROL_ID; use openzeppelin::access::accesscontrol::interface::IAccessControlDispatcher; use openzeppelin::access::accesscontrol::interface::IAccessControlCamelDispatcher; use openzeppelin::access::accesscontrol::interface::IAccessControlDispatcherTrait; @@ -88,6 +89,29 @@ fn setup_accesscontrol_panic() -> (DualCaseAccessControl, DualCaseAccessControl) // snake_case target // +#[test] +#[available_gas(2000000)] +fn test_dual_supports_interface() { + let (dispatcher, _) = setup_snake(); + assert(dispatcher.supports_interface(IACCESSCONTROL_ID), 'Should support own interface'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_supports_interface() { + let dispatcher = setup_non_accesscontrol(); + dispatcher.supports_interface(IACCESSCONTROL_ID); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_supports_interface_exists_and_panics() { + let (dispatcher, _) = setup_accesscontrol_panic(); + dispatcher.supports_interface(IACCESSCONTROL_ID); +} + #[test] #[available_gas(2000000)] fn test_dual_has_role() { @@ -213,6 +237,21 @@ fn test_dual_renounce_role_exists_and_panics() { // camelCase target // +#[test] +#[available_gas(2000000)] +fn test_dual_supportsInterface() { + let (dispatcher, _) = setup_camel(); + assert(dispatcher.supports_interface(IACCESSCONTROL_ID), 'Should support own interface'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_supportsInterface_exists_and_panics() { + let (_, dispatcher) = setup_accesscontrol_panic(); + dispatcher.supports_interface(IACCESSCONTROL_ID); +} + #[test] #[available_gas(2000000)] fn test_dual_hasRole() { diff --git a/src/openzeppelin/tests/introspection.cairo b/src/openzeppelin/tests/introspection.cairo new file mode 100644 index 000000000..d46bcfe7d --- /dev/null +++ b/src/openzeppelin/tests/introspection.cairo @@ -0,0 +1,2 @@ +mod test_src5; +mod test_dual_src5; diff --git a/src/openzeppelin/tests/introspection/test_dual_src5.cairo b/src/openzeppelin/tests/introspection/test_dual_src5.cairo new file mode 100644 index 000000000..5693b941c --- /dev/null +++ b/src/openzeppelin/tests/introspection/test_dual_src5.cairo @@ -0,0 +1,94 @@ +use array::ArrayTrait; +use openzeppelin::introspection::src5::ISRC5_ID; +use openzeppelin::introspection::src5::ISRC5Dispatcher; +use openzeppelin::introspection::src5::ISRC5DispatcherTrait; +use openzeppelin::introspection::src5::ISRC5CamelDispatcher; +use openzeppelin::introspection::src5::ISRC5CamelDispatcherTrait; +use openzeppelin::introspection::dual_src5::DualCaseSRC5; +use openzeppelin::introspection::dual_src5::DualCaseSRC5Trait; +use openzeppelin::tests::mocks::src5_mocks::SnakeSRC5Mock; +use openzeppelin::tests::mocks::src5_mocks::CamelSRC5Mock; +use openzeppelin::tests::mocks::src5_mocks::SnakeSRC5PanicMock; +use openzeppelin::tests::mocks::src5_mocks::CamelSRC5PanicMock; +use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; +use openzeppelin::tests::utils; + +// +// Setup +// + +fn setup_snake() -> DualCaseSRC5 { + let mut calldata = ArrayTrait::new(); + let target = utils::deploy(SnakeSRC5Mock::TEST_CLASS_HASH, calldata); + DualCaseSRC5 { contract_address: target } +} + +fn setup_camel() -> DualCaseSRC5 { + let mut calldata = ArrayTrait::new(); + let target = utils::deploy(CamelSRC5Mock::TEST_CLASS_HASH, calldata); + DualCaseSRC5 { contract_address: target } +} + +fn setup_non_src5() -> DualCaseSRC5 { + let calldata = ArrayTrait::new(); + let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); + DualCaseSRC5 { contract_address: target } +} + +fn setup_src5_panic() -> (DualCaseSRC5, DualCaseSRC5) { + let snake_target = utils::deploy(SnakeSRC5PanicMock::TEST_CLASS_HASH, ArrayTrait::new()); + let camel_target = utils::deploy(CamelSRC5PanicMock::TEST_CLASS_HASH, ArrayTrait::new()); + ( + DualCaseSRC5 { + contract_address: snake_target + }, DualCaseSRC5 { + contract_address: camel_target + } + ) +} + +// +// snake_case target +// + +#[test] +#[available_gas(2000000)] +fn test_dual_supports_interface() { + let dispatcher = setup_snake(); + assert(dispatcher.supports_interface(ISRC5_ID), 'Should support base interface'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_supports_interface() { + let dispatcher = setup_non_src5(); + dispatcher.supports_interface(ISRC5_ID); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_supports_interface_exists_and_panics() { + let (dispatcher, _) = setup_src5_panic(); + dispatcher.supports_interface(ISRC5_ID); +} + +// +// camelCase target +// + +#[test] +#[available_gas(2000000)] +fn test_dual_supportsInterface() { + let dispatcher = setup_camel(); + assert(dispatcher.supports_interface(ISRC5_ID), 'Should support base interface'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_supportsInterface_exists_and_panics() { + let (_, dispatcher) = setup_src5_panic(); + dispatcher.supports_interface(ISRC5_ID); +} diff --git a/src/openzeppelin/tests/test_src5.cairo b/src/openzeppelin/tests/introspection/test_src5.cairo similarity index 100% rename from src/openzeppelin/tests/test_src5.cairo rename to src/openzeppelin/tests/introspection/test_src5.cairo diff --git a/src/openzeppelin/tests/mocks.cairo b/src/openzeppelin/tests/mocks.cairo index 86bd7ce90..27ad2d535 100644 --- a/src/openzeppelin/tests/mocks.cairo +++ b/src/openzeppelin/tests/mocks.cairo @@ -17,3 +17,4 @@ mod dual_ownable_mocks; mod snake721_mock; mod camel721_mock; mod non_implementing_mock; +mod src5_mocks; diff --git a/src/openzeppelin/tests/mocks/accesscontrol_panic_mock.cairo b/src/openzeppelin/tests/mocks/accesscontrol_panic_mock.cairo index a1c13cf66..10601ca78 100644 --- a/src/openzeppelin/tests/mocks/accesscontrol_panic_mock.cairo +++ b/src/openzeppelin/tests/mocks/accesscontrol_panic_mock.cairo @@ -8,6 +8,12 @@ mod SnakeAccessControlPanicMock { use starknet::ContractAddress; + #[view] + fn supports_interface(interface_id: felt252) -> bool { + panic_with_felt252('Some error'); + false + } + #[view] fn has_role(role: felt252, account: ContractAddress) -> bool { panic_with_felt252('Some error'); @@ -40,6 +46,12 @@ mod SnakeAccessControlPanicMock { mod CamelAccessControlPanicMock { use starknet::ContractAddress; + #[view] + fn supportsInterface(interfaceId: felt252) -> bool { + panic_with_felt252('Some error'); + false + } + #[view] fn hasRole(role: felt252, account: ContractAddress) -> bool { panic_with_felt252('Some error'); diff --git a/src/openzeppelin/tests/mocks/camel721_mock.cairo b/src/openzeppelin/tests/mocks/camel721_mock.cairo index ef0d95ed2..b2e02793b 100644 --- a/src/openzeppelin/tests/mocks/camel721_mock.cairo +++ b/src/openzeppelin/tests/mocks/camel721_mock.cairo @@ -16,7 +16,7 @@ mod CamelERC721Mock { #[view] fn supportsInterface(interfaceId: felt252) -> bool { - ERC721::supports_interface(interfaceId) + ERC721::supportsInterface(interfaceId) } #[view] diff --git a/src/openzeppelin/tests/mocks/camel_accesscontrol_mock.cairo b/src/openzeppelin/tests/mocks/camel_accesscontrol_mock.cairo index b1f898855..dff5ab1b6 100644 --- a/src/openzeppelin/tests/mocks/camel_accesscontrol_mock.cairo +++ b/src/openzeppelin/tests/mocks/camel_accesscontrol_mock.cairo @@ -11,6 +11,11 @@ mod CamelAccessControlMock { AccessControl::_grant_role(DEFAULT_ADMIN_ROLE, admin); } + #[view] + fn supportsInterface(interfaceId: felt252) -> bool { + AccessControl::supportsInterface(interfaceId) + } + #[view] fn hasRole(role: felt252, account: ContractAddress) -> bool { AccessControl::hasRole(role, account) diff --git a/src/openzeppelin/tests/mocks/erc721_panic_mock.cairo b/src/openzeppelin/tests/mocks/erc721_panic_mock.cairo index d7f1702dd..b824bf125 100644 --- a/src/openzeppelin/tests/mocks/erc721_panic_mock.cairo +++ b/src/openzeppelin/tests/mocks/erc721_panic_mock.cairo @@ -36,6 +36,12 @@ mod SnakeERC721PanicMock { // snake // + #[view] + fn supports_interface(interface_id: felt252) -> bool { + panic_with_felt252('Some error'); + false + } + #[view] fn token_uri(token_id: u256) -> felt252 { panic_with_felt252('Some error'); @@ -90,6 +96,12 @@ mod CamelERC721PanicMock { use starknet::ContractAddress; use zeroable::Zeroable; + #[view] + fn supportsInterface(interfaceId: felt252) -> bool { + panic_with_felt252('Some error'); + false + } + #[view] fn tokenUri(tokenId: u256) -> felt252 { panic_with_felt252('Some error'); diff --git a/src/openzeppelin/tests/mocks/snake_accesscontrol_mock.cairo b/src/openzeppelin/tests/mocks/snake_accesscontrol_mock.cairo index 76c2d9f26..ea3122a67 100644 --- a/src/openzeppelin/tests/mocks/snake_accesscontrol_mock.cairo +++ b/src/openzeppelin/tests/mocks/snake_accesscontrol_mock.cairo @@ -12,6 +12,11 @@ mod SnakeAccessControlMock { AccessControl::_grant_role(DEFAULT_ADMIN_ROLE, admin); } + #[view] + fn supports_interface(interface_id: felt252) -> bool { + AccessControl::supports_interface(interface_id) + } + #[view] fn has_role(role: felt252, account: ContractAddress) -> bool { AccessControl::has_role(role, account) diff --git a/src/openzeppelin/tests/mocks/src5_mocks.cairo b/src/openzeppelin/tests/mocks/src5_mocks.cairo new file mode 100644 index 000000000..28ccf2b6b --- /dev/null +++ b/src/openzeppelin/tests/mocks/src5_mocks.cairo @@ -0,0 +1,35 @@ +use openzeppelin::introspection::src5::SRC5; + +#[contract] +mod SnakeSRC5Mock { + #[view] + fn supports_interface(interface_id: felt252) -> bool { + super::SRC5::supports_interface(interface_id) + } +} + +#[contract] +mod CamelSRC5Mock { + #[view] + fn supportsInterface(interfaceId: felt252) -> bool { + super::SRC5::supportsInterface(interfaceId) + } +} + +#[contract] +mod SnakeSRC5PanicMock { + #[view] + fn supports_interface(interface_id: felt252) -> bool { + panic_with_felt252('Some error'); + false + } +} + +#[contract] +mod CamelSRC5PanicMock { + #[view] + fn supportsInterface(interfaceId: felt252) -> bool { + panic_with_felt252('Some error'); + false + } +} diff --git a/src/openzeppelin/tests/token/test_dual721.cairo b/src/openzeppelin/tests/token/test_dual721.cairo index 0ef8a0521..ca821a4be 100644 --- a/src/openzeppelin/tests/token/test_dual721.cairo +++ b/src/openzeppelin/tests/token/test_dual721.cairo @@ -3,6 +3,7 @@ use starknet::ContractAddress; use starknet::contract_address_const; use starknet::testing::set_caller_address; use starknet::testing::set_contract_address; +use openzeppelin::token::erc721::interface::IERC721_ID; use openzeppelin::token::erc721::interface::IERC721Dispatcher; use openzeppelin::token::erc721::interface::IERC721CamelDispatcher; use openzeppelin::token::erc721::interface::IERC721DispatcherTrait; @@ -386,6 +387,29 @@ fn test_dual_token_uri_exists_and_panics() { dispatcher.token_uri(TOKEN_ID); } +#[test] +#[available_gas(2000000)] +fn test_dual_supports_interface() { + let (dispatcher, _) = setup_snake(); + assert(dispatcher.supports_interface(IERC721_ID), 'Should support own interface'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_supports_interface() { + let dispatcher = setup_non_erc721(); + dispatcher.supports_interface(IERC721_ID); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_supports_interface_exists_and_panics() { + let (dispatcher, _) = setup_erc721_panic(); + dispatcher.supports_interface(IERC721_ID); +} + /// /// camelCase target /// @@ -519,3 +543,18 @@ fn test_dual_tokenUri_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); dispatcher.token_uri(TOKEN_ID); } + +#[test] +#[available_gas(2000000)] +fn test_dual_supportsInterface() { + let (dispatcher, _) = setup_camel(); + assert(dispatcher.supports_interface(IERC721_ID), 'Should support own interface'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_supportsInterface_exists_and_panics() { + let (_, dispatcher) = setup_erc721_panic(); + dispatcher.supports_interface(IERC721_ID); +} diff --git a/src/openzeppelin/token/erc20/dual20.cairo b/src/openzeppelin/token/erc20/dual20.cairo index 2d9028f70..99a5045a4 100644 --- a/src/openzeppelin/token/erc20/dual20.cairo +++ b/src/openzeppelin/token/erc20/dual20.cairo @@ -1,17 +1,9 @@ use array::ArrayTrait; -use array::SpanTrait; -use core::result::ResultTrait; -use integer::Felt252TryIntoU8; -use option::OptionTrait; use starknet::call_contract_syscall; use starknet::ContractAddress; -use starknet::Felt252TryIntoContractAddress; use starknet::SyscallResultTrait; -use traits::TryInto; use openzeppelin::utils::try_selector_with_fallback; -use openzeppelin::utils::Felt252TryIntoBool; -use openzeppelin::utils::BoolIntoFelt252; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; use openzeppelin::utils::UnwrapAndCast; @@ -39,17 +31,15 @@ impl DualERC20Impl of DualERC20Trait { fn name(self: @DualERC20) -> felt252 { let args = ArrayTrait::new(); - *call_contract_syscall(*self.contract_address, selectors::name, args.span()) - .unwrap_syscall() - .at(0) + call_contract_syscall(*self.contract_address, selectors::name, args.span()) + .unwrap_and_cast() } fn symbol(self: @DualERC20) -> felt252 { let args = ArrayTrait::new(); - *call_contract_syscall(*self.contract_address, selectors::symbol, args.span()) - .unwrap_syscall() - .at(0) + call_contract_syscall(*self.contract_address, selectors::symbol, args.span()) + .unwrap_and_cast() } fn decimals(self: @DualERC20) -> u8 { diff --git a/src/openzeppelin/token/erc721/dual721.cairo b/src/openzeppelin/token/erc721/dual721.cairo index 35389f357..bdd49fd03 100644 --- a/src/openzeppelin/token/erc721/dual721.cairo +++ b/src/openzeppelin/token/erc721/dual721.cairo @@ -1,16 +1,8 @@ -use core::result::ResultTrait; -use traits::Into; -use traits::TryInto; -use array::SpanTrait; use array::ArrayTrait; -use option::OptionTrait; use starknet::ContractAddress; use starknet::SyscallResultTrait; use starknet::call_contract_syscall; -use starknet::Felt252TryIntoContractAddress; use openzeppelin::utils::try_selector_with_fallback; -use openzeppelin::utils::Felt252TryIntoBool; -use openzeppelin::utils::BoolIntoFelt252; use openzeppelin::utils::selectors; use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::serde::SerializedAppend; @@ -42,34 +34,32 @@ trait DualCaseERC721Trait { token_id: u256, data: Span ); + fn supports_interface(self: @DualCaseERC721, interface_id: felt252) -> bool; } impl DualCaseERC721Impl of DualCaseERC721Trait { fn name(self: @DualCaseERC721) -> felt252 { let args = ArrayTrait::new(); - *call_contract_syscall(*self.contract_address, selectors::name, args.span()) - .unwrap_syscall() - .at(0) + call_contract_syscall(*self.contract_address, selectors::name, args.span()) + .unwrap_and_cast() } fn symbol(self: @DualCaseERC721) -> felt252 { let args = ArrayTrait::new(); - *call_contract_syscall(*self.contract_address, selectors::symbol, args.span()) - .unwrap_syscall() - .at(0) + call_contract_syscall(*self.contract_address, selectors::symbol, args.span()) + .unwrap_and_cast() } fn token_uri(self: @DualCaseERC721, token_id: u256) -> felt252 { let mut args = ArrayTrait::new(); args.append_serde(token_id); - *try_selector_with_fallback( + try_selector_with_fallback( *self.contract_address, selectors::token_uri, selectors::tokenUri, args.span() ) - .unwrap_syscall() - .at(0) + .unwrap_and_cast() } fn balance_of(self: @DualCaseERC721, account: ContractAddress) -> u256 { @@ -175,4 +165,17 @@ impl DualCaseERC721Impl of DualCaseERC721Trait { ) .unwrap_syscall(); } + + fn supports_interface(self: @DualCaseERC721, interface_id: felt252) -> bool { + let mut args = ArrayTrait::new(); + args.append_serde(interface_id); + + try_selector_with_fallback( + *self.contract_address, + selectors::supports_interface, + selectors::supportsInterface, + args.span() + ) + .unwrap_and_cast() + } } diff --git a/src/openzeppelin/token/erc721/erc721.cairo b/src/openzeppelin/token/erc721/erc721.cairo index d61bc1a6f..8f5f5088a 100644 --- a/src/openzeppelin/token/erc721/erc721.cairo +++ b/src/openzeppelin/token/erc721/erc721.cairo @@ -11,6 +11,8 @@ trait ERC721ABI { fn approve(to: ContractAddress, token_id: u256); // snake_case #[view] + fn supports_interface(interface_id: felt252) -> bool; + #[view] fn balance_of(account: ContractAddress) -> u256; #[view] fn owner_of(token_id: u256) -> ContractAddress; @@ -30,6 +32,8 @@ trait ERC721ABI { fn token_uri(token_id: u256) -> felt252; // camelCase #[view] + fn supportsInterface(interfaceId: felt252) -> bool; + #[view] fn balanceOf(account: ContractAddress) -> u256; #[view] fn ownerOf(tokenId: u256) -> ContractAddress; @@ -57,8 +61,8 @@ mod ERC721 { use openzeppelin::token::erc721; // Dispatchers - use openzeppelin::introspection::src5::ISRC5Dispatcher; - use openzeppelin::introspection::src5::ISRC5DispatcherTrait; + use openzeppelin::introspection::dual_src5::DualCaseSRC5; + use openzeppelin::introspection::dual_src5::DualCaseSRC5Trait; use super::super::interface::ERC721ReceiverABIDispatcher; use super::super::interface::ERC721ReceiverABIDispatcherTrait; @@ -95,6 +99,18 @@ mod ERC721 { initializer(name, symbol); } + impl ISRC5Impl of src5::ISRC5 { + fn supports_interface(interface_id: felt252) -> bool { + src5::SRC5::supports_interface(interface_id) + } + } + + impl ISRC5CamelImpl of src5::ISRC5Camel { + fn supportsInterface(interfaceId: felt252) -> bool { + src5::SRC5::supportsInterface(interfaceId) + } + } + impl ERC721Impl of erc721::interface::IERC721 { fn name() -> felt252 { _name::read() @@ -210,12 +226,12 @@ mod ERC721 { #[view] fn supports_interface(interface_id: felt252) -> bool { - src5::SRC5::supports_interface(interface_id) + ISRC5Impl::supports_interface(interface_id) } #[view] fn supportsInterface(interfaceId: felt252) -> bool { - src5::SRC5::supports_interface(interfaceId) + ISRC5CamelImpl::supportsInterface(interfaceId) } #[view] @@ -443,7 +459,7 @@ mod ERC721 { fn _check_on_erc721_received( from: ContractAddress, to: ContractAddress, token_id: u256, data: Span ) -> bool { - if (ISRC5Dispatcher { + if (DualCaseSRC5 { contract_address: to }.supports_interface(erc721::interface::IERC721_RECEIVER_ID)) { // todo add casing fallback mechanism @@ -454,9 +470,7 @@ mod ERC721 { get_caller_address(), from, token_id, data ) == erc721::interface::IERC721_RECEIVER_ID } else { - ISRC5Dispatcher { - contract_address: to - }.supports_interface(account::interface::ISRC6_ID) + DualCaseSRC5 { contract_address: to }.supports_interface(account::interface::ISRC6_ID) } } } From 33dbd5685d2e2415aa597c3378de4e516527383a Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Thu, 13 Jul 2023 12:39:34 -0400 Subject: [PATCH 063/246] Add dual case erc721 receiver (#649) * add 721 holder * add 721 receiver selectors * add abis * add dual receiver * add dual receiver mocks * add dual 721 receiver and holder mods * add dual 721 receiver tests * integrate dual case receiver * remove camel supportsInterface * add isrc5 impl * add isrc5 impl * fix formatting * change receiver import * remove unused mod * add isrc5 impl * remove mod * fix formatting * remove conflict * add felt impl to UnwrapAndCast * clean up mocks * add camelCase ISRC5 impl * add camelCase tests for safe methods * fix test * remove unused imports * remove unused imports * remove unused imports * Apply suggestions from code review Co-authored-by: Eric Nordelo * add transferFrom and safeTransferFrom tests * fix comments * fix comments --------- Co-authored-by: Eric Nordelo --- src/openzeppelin/tests/mocks.cairo | 1 + .../tests/mocks/dual721_receiver_mocks.cairo | 83 +++ .../tests/mocks/erc721_receiver.cairo | 31 +- src/openzeppelin/tests/token.cairo | 1 + .../tests/token/test_dual20.cairo | 30 +- .../tests/token/test_dual721.cairo | 30 +- .../tests/token/test_dual721_receiver.cairo | 146 +++++ .../tests/token/test_erc721.cairo | 554 ++++++++++++++++-- src/openzeppelin/token/erc20/erc20.cairo | 6 +- src/openzeppelin/token/erc721.cairo | 1 + .../token/erc721/dual721_receiver.cairo | 46 ++ src/openzeppelin/token/erc721/erc721.cairo | 22 +- src/openzeppelin/token/erc721/interface.cairo | 2 + src/openzeppelin/utils/selectors.cairo | 8 + 14 files changed, 873 insertions(+), 88 deletions(-) create mode 100644 src/openzeppelin/tests/mocks/dual721_receiver_mocks.cairo create mode 100644 src/openzeppelin/tests/token/test_dual721_receiver.cairo create mode 100644 src/openzeppelin/token/erc721/dual721_receiver.cairo diff --git a/src/openzeppelin/tests/mocks.cairo b/src/openzeppelin/tests/mocks.cairo index 27ad2d535..7ed138560 100644 --- a/src/openzeppelin/tests/mocks.cairo +++ b/src/openzeppelin/tests/mocks.cairo @@ -17,4 +17,5 @@ mod dual_ownable_mocks; mod snake721_mock; mod camel721_mock; mod non_implementing_mock; +mod dual721_receiver_mocks; mod src5_mocks; diff --git a/src/openzeppelin/tests/mocks/dual721_receiver_mocks.cairo b/src/openzeppelin/tests/mocks/dual721_receiver_mocks.cairo new file mode 100644 index 000000000..3ffb3df74 --- /dev/null +++ b/src/openzeppelin/tests/mocks/dual721_receiver_mocks.cairo @@ -0,0 +1,83 @@ +use openzeppelin::introspection::src5::SRC5; +use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; +use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver::IERC721_RECEIVER_ID; + +#[contract] +mod SnakeERC721ReceiverMock { + use openzeppelin::utils::serde::SpanSerde; + use starknet::ContractAddress; + use super::ERC721Receiver; + use super::IERC721_RECEIVER_ID; + use super::SRC5; + + #[constructor] + fn constructor() { + SRC5::register_interface(IERC721_RECEIVER_ID); + } + + #[view] + fn on_erc721_received( + operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span + ) -> felt252 { + ERC721Receiver::on_erc721_received(operator, from, token_id, data) + } + + #[view] + fn supports_interface(interface_id: felt252) -> bool { + ERC721Receiver::supports_interface(interface_id) + } +} + +#[contract] +mod CamelERC721ReceiverMock { + use openzeppelin::utils::serde::SpanSerde; + use starknet::ContractAddress; + use super::ERC721Receiver; + use super::IERC721_RECEIVER_ID; + use super::SRC5; + + #[constructor] + fn constructor() { + SRC5::register_interface(IERC721_RECEIVER_ID); + } + + #[view] + fn onERC721Received( + operator: ContractAddress, from: ContractAddress, tokenId: u256, data: Span + ) -> felt252 { + ERC721Receiver::on_erc721_received(operator, from, tokenId, data) + } + + #[view] + fn supportsInterface(interfaceId: felt252) -> bool { + ERC721Receiver::supportsInterface(interfaceId) + } +} + +#[contract] +mod SnakeERC721ReceiverPanicMock { + use openzeppelin::utils::serde::SpanSerde; + use starknet::ContractAddress; + + #[view] + fn on_erc721_received( + operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span + ) -> felt252 { + panic_with_felt252('Some error'); + 3 + } +} + +#[contract] +mod CamelERC721ReceiverPanicMock { + use openzeppelin::utils::serde::SpanSerde; + use starknet::ContractAddress; + + #[view] + fn onERC721Received( + operator: ContractAddress, from: ContractAddress, tokenId: u256, data: Span + ) -> felt252 { + panic_with_felt252('Some error'); + 3 + } +} diff --git a/src/openzeppelin/tests/mocks/erc721_receiver.cairo b/src/openzeppelin/tests/mocks/erc721_receiver.cairo index 4bb3556bf..aff07e7aa 100644 --- a/src/openzeppelin/tests/mocks/erc721_receiver.cairo +++ b/src/openzeppelin/tests/mocks/erc721_receiver.cairo @@ -6,11 +6,28 @@ mod ERC721Receiver { use openzeppelin::token::erc721::interface::IERC721Receiver; use openzeppelin::token::erc721::interface::IERC721ReceiverCamel; use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; - use openzeppelin::introspection::src5::SRC5; + use openzeppelin::introspection::src5; + use array::SpanTrait; use openzeppelin::utils::serde::SpanSerde; use starknet::ContractAddress; - use array::SpanTrait; + + #[constructor] + fn constructor() { + src5::SRC5::register_interface(IERC721_RECEIVER_ID); + } + + impl ISRC5Impl of src5::ISRC5 { + fn supports_interface(interface_id: felt252) -> bool { + src5::SRC5::supports_interface(interface_id) + } + } + + impl ISRC5CamelImpl of src5::ISRC5Camel { + fn supportsInterface(interfaceId: felt252) -> bool { + src5::SRC5::supportsInterface(interfaceId) + } + } impl ERC721ReceiverImpl of IERC721Receiver { fn on_erc721_received( @@ -32,14 +49,14 @@ mod ERC721Receiver { } } - #[constructor] - fn constructor() { - SRC5::register_interface(IERC721_RECEIVER_ID); + #[view] + fn supports_interface(interface_id: felt252) -> bool { + ISRC5Impl::supports_interface(interface_id) } #[view] - fn supports_interface(interface_id: felt252) -> bool { - SRC5::supports_interface(interface_id) + fn supportsInterface(interfaceId: felt252) -> bool { + ISRC5CamelImpl::supportsInterface(interfaceId) } #[external] diff --git a/src/openzeppelin/tests/token.cairo b/src/openzeppelin/tests/token.cairo index 1020d4439..ebb5dc5c4 100644 --- a/src/openzeppelin/tests/token.cairo +++ b/src/openzeppelin/tests/token.cairo @@ -2,3 +2,4 @@ mod test_dual20; mod test_dual721; mod test_erc20; mod test_erc721; +mod test_dual721_receiver; diff --git a/src/openzeppelin/tests/token/test_dual20.cairo b/src/openzeppelin/tests/token/test_dual20.cairo index 0da4ea5c7..1afa77c31 100644 --- a/src/openzeppelin/tests/token/test_dual20.cairo +++ b/src/openzeppelin/tests/token/test_dual20.cairo @@ -17,9 +17,9 @@ use openzeppelin::token::erc20::interface::IERC20DispatcherTrait; use openzeppelin::tests::utils; use openzeppelin::utils::serde::SerializedAppend; -/// -/// Constants -/// +// +// Constants +// const NAME: felt252 = 111; const SYMBOL: felt252 = 222; @@ -40,9 +40,9 @@ fn OPERATOR() -> ContractAddress { contract_address_const::<40>() } -/// -/// Setup -/// +// +// Setup +// fn setup_snake() -> (DualERC20, IERC20Dispatcher) { let mut calldata = ArrayTrait::new(); @@ -76,9 +76,9 @@ fn setup_erc20_panic() -> (DualERC20, DualERC20) { (DualERC20 { contract_address: snake_target }, DualERC20 { contract_address: camel_target }) } -/// -/// Case agnostic methods -/// +// +// Case agnostic methods +// #[test] #[available_gas(2000000)] @@ -215,9 +215,9 @@ fn test_dual_approve_exists_and_panics() { dispatcher.approve(SPENDER(), VALUE); } -/// -/// snake_case target -/// +// +// snake_case target +// #[test] #[available_gas(2000000)] @@ -293,9 +293,9 @@ fn test_dual_transfer_from_exists_and_panics() { dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); } -/// -/// camelCase target -/// +// +// camelCase target +// #[test] #[available_gas(2000000)] diff --git a/src/openzeppelin/tests/token/test_dual721.cairo b/src/openzeppelin/tests/token/test_dual721.cairo index ca821a4be..4adab5d29 100644 --- a/src/openzeppelin/tests/token/test_dual721.cairo +++ b/src/openzeppelin/tests/token/test_dual721.cairo @@ -21,9 +21,9 @@ use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils; use openzeppelin::utils::serde::SerializedAppend; -/// -/// Constants -/// +// +// Constants +// const NAME: felt252 = 111; const SYMBOL: felt252 = 222; @@ -52,9 +52,9 @@ fn DATA(success: bool) -> Span { data.span() } -/// -/// Setup -/// +// +// Setup +// fn setup_snake() -> (DualCaseERC721, IERC721Dispatcher) { let mut calldata = ArrayTrait::new(); @@ -106,9 +106,9 @@ fn setup_receiver() -> ContractAddress { utils::deploy(ERC721Receiver::TEST_CLASS_HASH, ArrayTrait::new()) } -/// -/// Case agnostic methods -/// +// +// Case agnostic methods +// #[test] #[available_gas(2000000)] @@ -190,9 +190,9 @@ fn test_dual_approve_exists_and_panics() { dispatcher.approve(SPENDER(), TOKEN_ID); } -/// -/// snake_case target -/// +// +// snake_case target +// #[test] #[available_gas(2000000)] @@ -410,9 +410,9 @@ fn test_dual_supports_interface_exists_and_panics() { dispatcher.supports_interface(IERC721_ID); } -/// -/// camelCase target -/// +// +// camelCase target +// #[test] #[available_gas(2000000)] diff --git a/src/openzeppelin/tests/token/test_dual721_receiver.cairo b/src/openzeppelin/tests/token/test_dual721_receiver.cairo new file mode 100644 index 000000000..ef02380a9 --- /dev/null +++ b/src/openzeppelin/tests/token/test_dual721_receiver.cairo @@ -0,0 +1,146 @@ +use array::ArrayTrait; +use starknet::ContractAddress; +use starknet::contract_address_const; +use openzeppelin::tests::mocks::erc721_receiver::SUCCESS; +use openzeppelin::tests::mocks::erc721_receiver::FAILURE; +use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; +use openzeppelin::token::erc721::interface::IERC721ReceiverDispatcher; +use openzeppelin::token::erc721::interface::IERC721ReceiverCamelDispatcher; +use openzeppelin::token::erc721::interface::IERC721ReceiverDispatcherTrait; +use openzeppelin::token::erc721::interface::IERC721ReceiverCamelDispatcherTrait; +use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721ReceiverTrait; +use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721Receiver; +use openzeppelin::tests::mocks::dual721_receiver_mocks::SnakeERC721ReceiverMock; +use openzeppelin::tests::mocks::dual721_receiver_mocks::CamelERC721ReceiverMock; +use openzeppelin::tests::mocks::dual721_receiver_mocks::SnakeERC721ReceiverPanicMock; +use openzeppelin::tests::mocks::dual721_receiver_mocks::CamelERC721ReceiverPanicMock; +use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; +use openzeppelin::tests::utils; + +// +// Constants +// + +const TOKEN_ID: u256 = 7; + +fn DATA(success: bool) -> Span { + let mut data = ArrayTrait::new(); + if success { + data.append(SUCCESS); + } else { + data.append(FAILURE); + } + data.span() +} + +fn OWNER() -> ContractAddress { + contract_address_const::<10>() +} + +fn OPERATOR() -> ContractAddress { + contract_address_const::<20>() +} + +// +// Setup +// + +fn setup_snake() -> (DualCaseERC721Receiver, IERC721ReceiverDispatcher) { + let mut calldata = ArrayTrait::new(); + let target = utils::deploy(SnakeERC721ReceiverMock::TEST_CLASS_HASH, calldata); + ( + DualCaseERC721Receiver { + contract_address: target + }, IERC721ReceiverDispatcher { + contract_address: target + } + ) +} + +fn setup_camel() -> (DualCaseERC721Receiver, IERC721ReceiverCamelDispatcher) { + let mut calldata = ArrayTrait::new(); + let target = utils::deploy(CamelERC721ReceiverMock::TEST_CLASS_HASH, calldata); + ( + DualCaseERC721Receiver { + contract_address: target + }, IERC721ReceiverCamelDispatcher { + contract_address: target + } + ) +} + +fn setup_non_erc721_receiver() -> DualCaseERC721Receiver { + let calldata = ArrayTrait::new(); + let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); + DualCaseERC721Receiver { contract_address: target } +} + +fn setup_erc721_receiver_panic() -> (DualCaseERC721Receiver, DualCaseERC721Receiver) { + let snake_target = utils::deploy( + SnakeERC721ReceiverPanicMock::TEST_CLASS_HASH, ArrayTrait::new() + ); + let camel_target = utils::deploy( + CamelERC721ReceiverPanicMock::TEST_CLASS_HASH, ArrayTrait::new() + ); + ( + DualCaseERC721Receiver { + contract_address: snake_target + }, DualCaseERC721Receiver { + contract_address: camel_target + } + ) +} + +// +// snake_case target +// + +#[test] +#[available_gas(2000000)] +fn test_dual_on_erc721_received() { + let (dispatcher, _) = setup_snake(); + assert( + dispatcher + .on_erc721_received(OPERATOR(), OWNER(), TOKEN_ID, DATA(true)) == IERC721_RECEIVER_ID, + 'Should return interface id' + ); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_dual_no_on_erc721_received() { + let dispatcher = setup_non_erc721_receiver(); + dispatcher.on_erc721_received(OPERATOR(), OWNER(), TOKEN_ID, DATA(true)); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_on_erc721_received_exists_and_panics() { + let (dispatcher, _) = setup_erc721_receiver_panic(); + dispatcher.on_erc721_received(OPERATOR(), OWNER(), TOKEN_ID, DATA(true)); +} + +// +// camelCase target +// + +#[test] +#[available_gas(2000000)] +fn test_dual_onERC721Received() { + let (dispatcher, _) = setup_camel(); + assert( + dispatcher + .on_erc721_received(OPERATOR(), OWNER(), TOKEN_ID, DATA(true)) == IERC721_RECEIVER_ID, + 'Should return interface id' + ); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +fn test_dual_onERC721Received_exists_and_panics() { + let (_, dispatcher) = setup_erc721_receiver_panic(); + dispatcher.on_erc721_received(OPERATOR(), OWNER(), TOKEN_ID, DATA(true)); +} diff --git a/src/openzeppelin/tests/token/test_erc721.cairo b/src/openzeppelin/tests/token/test_erc721.cairo index 4f968c0ee..5037ae251 100644 --- a/src/openzeppelin/tests/token/test_erc721.cairo +++ b/src/openzeppelin/tests/token/test_erc721.cairo @@ -8,6 +8,8 @@ use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; use openzeppelin::tests::mocks::erc721_receiver::ERC721NonReceiver; use openzeppelin::tests::mocks::erc721_receiver::SUCCESS; use openzeppelin::tests::mocks::erc721_receiver::FAILURE; +use openzeppelin::tests::mocks::camel_account_mock::CamelAccountMock; +use openzeppelin::tests::mocks::dual721_receiver_mocks::CamelERC721ReceiverMock; use starknet::contract_address_const; use starknet::ContractAddress; @@ -22,6 +24,7 @@ const NAME: felt252 = 111; const SYMBOL: felt252 = 222; const URI: felt252 = 333; const TOKEN_ID: u256 = 7; +const PUBKEY: felt252 = 444; fn ZERO() -> ContractAddress { Zeroable::zero() @@ -52,9 +55,9 @@ fn DATA(success: bool) -> Span { data.span() } -/// -/// Setup -/// +// +// Setup +// fn setup() { ERC721::initializer(NAME, SYMBOL); @@ -65,16 +68,25 @@ fn setup_receiver() -> ContractAddress { utils::deploy(ERC721Receiver::TEST_CLASS_HASH, ArrayTrait::new()) } +fn setup_camel_receiver() -> ContractAddress { + utils::deploy(CamelERC721ReceiverMock::TEST_CLASS_HASH, ArrayTrait::new()) +} + fn setup_account() -> ContractAddress { let mut calldata = ArrayTrait::new(); - let public_key: felt252 = 1234678; - calldata.append(public_key); + calldata.append(PUBKEY); utils::deploy(Account::TEST_CLASS_HASH, calldata) } -/// -/// Initializers -/// +fn setup_camel_account() -> ContractAddress { + let mut calldata = ArrayTrait::new(); + calldata.append(PUBKEY); + utils::deploy(CamelAccountMock::TEST_CLASS_HASH, calldata) +} + +// +// Initializers +// #[test] #[available_gas(2000000)] @@ -108,9 +120,9 @@ fn test_initialize() { assert(ERC721::supports_interface(src5::ISRC5_ID), 'missing interface ID'); } -/// -/// Getters -/// +// +// Getters +// #[test] #[available_gas(2000000)] @@ -185,9 +197,9 @@ fn test__exists() { assert(ERC721::_owners::read(token_id) == zero, 'Invalid owner'); } -/// -/// approve & _approve -/// +// +// approve & _approve +// #[test] #[available_gas(2000000)] @@ -264,9 +276,9 @@ fn test__approve_nonexistent() { ERC721::_approve(SPENDER(), TOKEN_ID); } -/// -/// set_approval_for_all & _set_approval_for_all -/// +// +// set_approval_for_all & _set_approval_for_all +// #[test] #[available_gas(2000000)] @@ -323,9 +335,9 @@ fn test__set_approval_for_all_owner_equal_operator_false() { ERC721::_set_approval_for_all(OWNER(), OWNER(), false); } -/// -/// transfer_from -/// +// +// transfer_from & transferFrom +// #[test] #[available_gas(2000000)] @@ -346,6 +358,25 @@ fn test_transfer_from_owner() { assert_state_after_transfer(token_id, owner, recipient); } +#[test] +#[available_gas(2000000)] +fn test_transferFrom_owner() { + setup(); + let token_id = TOKEN_ID; + let owner = OWNER(); + let recipient = RECIPIENT(); + // set approval to check reset + ERC721::_approve(OTHER(), token_id); + + assert_state_before_transfer(token_id, owner, recipient); + assert(ERC721::get_approved(token_id) == OTHER(), 'Approval not implicitly reset'); + + set_caller_address(owner); + ERC721::transferFrom(owner, recipient, token_id); + + assert_state_after_transfer(token_id, owner, recipient); +} + #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] @@ -353,6 +384,13 @@ fn test_transfer_from_nonexistent() { ERC721::transfer_from(ZERO(), RECIPIENT(), TOKEN_ID); } +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: invalid token ID', ))] +fn test_transferFrom_nonexistent() { + ERC721::transferFrom(ZERO(), RECIPIENT(), TOKEN_ID); +} + #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC721: invalid receiver', ))] @@ -363,6 +401,16 @@ fn test_transfer_from_to_zero() { ERC721::transfer_from(OWNER(), ZERO(), TOKEN_ID); } +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: invalid receiver', ))] +fn test_transferFrom_to_zero() { + setup(); + + set_caller_address(OWNER()); + ERC721::transferFrom(OWNER(), ZERO(), TOKEN_ID); +} + #[test] #[available_gas(2000000)] fn test_transfer_from_to_owner() { @@ -378,6 +426,21 @@ fn test_transfer_from_to_owner() { assert(ERC721::balance_of(OWNER()) == 1, 'Balance of owner after'); } +#[test] +#[available_gas(2000000)] +fn test_transferFrom_to_owner() { + setup(); + + assert(ERC721::owner_of(TOKEN_ID) == OWNER(), 'Ownership before'); + assert(ERC721::balance_of(OWNER()) == 1, 'Balance of owner before'); + + set_caller_address(OWNER()); + ERC721::transferFrom(OWNER(), OWNER(), TOKEN_ID); + + assert(ERC721::owner_of(TOKEN_ID) == OWNER(), 'Ownership after'); + assert(ERC721::balance_of(OWNER()) == 1, 'Balance of owner after'); +} + #[test] #[available_gas(2000000)] fn test_transfer_from_approved() { @@ -396,6 +459,24 @@ fn test_transfer_from_approved() { assert_state_after_transfer(token_id, owner, recipient); } +#[test] +#[available_gas(2000000)] +fn test_transferFrom_approved() { + setup(); + let token_id = TOKEN_ID; + let owner = OWNER(); + let recipient = RECIPIENT(); + assert_state_before_transfer(token_id, owner, recipient); + + set_caller_address(owner); + ERC721::approve(OPERATOR(), token_id); + + set_caller_address(OPERATOR()); + ERC721::transferFrom(owner, recipient, token_id); + + assert_state_after_transfer(token_id, owner, recipient); +} + #[test] #[available_gas(2000000)] fn test_transfer_from_approved_for_all() { @@ -415,6 +496,25 @@ fn test_transfer_from_approved_for_all() { assert_state_after_transfer(token_id, owner, recipient); } +#[test] +#[available_gas(2000000)] +fn test_transferFrom_approved_for_all() { + setup(); + let token_id = TOKEN_ID; + let owner = OWNER(); + let recipient = RECIPIENT(); + + assert_state_before_transfer(token_id, owner, recipient); + + set_caller_address(owner); + ERC721::set_approval_for_all(OPERATOR(), true); + + set_caller_address(OPERATOR()); + ERC721::transferFrom(owner, recipient, token_id); + + assert_state_after_transfer(token_id, owner, recipient); +} + #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC721: unauthorized caller', ))] @@ -425,8 +525,18 @@ fn test_transfer_from_unauthorized() { ERC721::transfer_from(OWNER(), RECIPIENT(), TOKEN_ID); } +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: unauthorized caller', ))] +fn test_transferFrom_unauthorized() { + setup(); + + set_caller_address(OTHER()); + ERC721::transferFrom(OWNER(), RECIPIENT(), TOKEN_ID); +} + // -// safe_transfer_from +// safe_transfer_from & safeTransferFrom // #[test] @@ -445,6 +555,54 @@ fn test_safe_transfer_from_to_account() { assert_state_after_transfer(token_id, owner, account); } +#[test] +#[available_gas(2000000)] +fn test_safeTransferFrom_to_account() { + setup(); + let account = setup_account(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(token_id, owner, account); + + set_caller_address(owner); + ERC721::safeTransferFrom(owner, account, token_id, DATA(true)); + + assert_state_after_transfer(token_id, owner, account); +} + +#[test] +#[available_gas(2000000)] +fn test_safe_transfer_from_to_account_camel() { + setup(); + let account = setup_camel_account(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(token_id, owner, account); + + set_caller_address(owner); + ERC721::safe_transfer_from(owner, account, token_id, DATA(true)); + + assert_state_after_transfer(token_id, owner, account); +} + +#[test] +#[available_gas(2000000)] +fn test_safeTransferFrom_to_account_camel() { + setup(); + let account = setup_camel_account(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(token_id, owner, account); + + set_caller_address(owner); + ERC721::safeTransferFrom(owner, account, token_id, DATA(true)); + + assert_state_after_transfer(token_id, owner, account); +} + #[test] #[available_gas(2000000)] fn test_safe_transfer_from_to_receiver() { @@ -461,6 +619,54 @@ fn test_safe_transfer_from_to_receiver() { assert_state_after_transfer(token_id, owner, receiver); } +#[test] +#[available_gas(2000000)] +fn test_safeTransferFrom_to_receiver() { + setup(); + let receiver = setup_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(token_id, owner, receiver); + + set_caller_address(owner); + ERC721::safeTransferFrom(owner, receiver, token_id, DATA(true)); + + assert_state_after_transfer(token_id, owner, receiver); +} + +#[test] +#[available_gas(2000000)] +fn test_safe_transfer_from_to_receiver_camel() { + setup(); + let receiver = setup_camel_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(token_id, owner, receiver); + + set_caller_address(owner); + ERC721::safe_transfer_from(owner, receiver, token_id, DATA(true)); + + assert_state_after_transfer(token_id, owner, receiver); +} + +#[test] +#[available_gas(2000000)] +fn test_safeTransferFrom_to_receiver_camel() { + setup(); + let receiver = setup_camel_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(token_id, owner, receiver); + + set_caller_address(owner); + ERC721::safeTransferFrom(owner, receiver, token_id, DATA(true)); + + assert_state_after_transfer(token_id, owner, receiver); +} + #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC721: safe transfer failed', ))] @@ -474,6 +680,45 @@ fn test_safe_transfer_from_to_receiver_failure() { ERC721::safe_transfer_from(owner, receiver, token_id, DATA(false)); } +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: safe transfer failed', ))] +fn test_safeTransferFrom_to_receiver_failure() { + setup(); + let receiver = setup_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + set_caller_address(owner); + ERC721::safeTransferFrom(owner, receiver, token_id, DATA(false)); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: safe transfer failed', ))] +fn test_safe_transfer_from_to_receiver_failure_camel() { + setup(); + let receiver = setup_camel_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + set_caller_address(owner); + ERC721::safe_transfer_from(owner, receiver, token_id, DATA(false)); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: safe transfer failed', ))] +fn test_safeTransferFrom_to_receiver_failure_camel() { + setup(); + let receiver = setup_camel_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + set_caller_address(owner); + ERC721::safeTransferFrom(owner, receiver, token_id, DATA(false)); +} + #[test] #[available_gas(2000000)] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] @@ -487,6 +732,19 @@ fn test_safe_transfer_from_to_non_receiver() { ERC721::safe_transfer_from(owner, recipient, token_id, DATA(true)); } +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_safeTransferFrom_to_non_receiver() { + setup(); + let recipient = utils::deploy(ERC721NonReceiver::TEST_CLASS_HASH, ArrayTrait::new()); + let token_id = TOKEN_ID; + let owner = OWNER(); + + set_caller_address(owner); + ERC721::safeTransferFrom(owner, recipient, token_id, DATA(true)); +} + #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] @@ -494,6 +752,13 @@ fn test_safe_transfer_from_nonexistent() { ERC721::safe_transfer_from(ZERO(), RECIPIENT(), TOKEN_ID, DATA(true)); } +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: invalid token ID', ))] +fn test_safeTransferFrom_nonexistent() { + ERC721::safeTransferFrom(ZERO(), RECIPIENT(), TOKEN_ID, DATA(true)); +} + #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC721: invalid receiver', ))] @@ -504,6 +769,16 @@ fn test_safe_transfer_from_to_zero() { ERC721::safe_transfer_from(OWNER(), ZERO(), TOKEN_ID, DATA(true)); } +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: invalid receiver', ))] +fn test_safeTransferFrom_to_zero() { + setup(); + + set_caller_address(OWNER()); + ERC721::safeTransferFrom(OWNER(), ZERO(), TOKEN_ID, DATA(true)); +} + #[test] #[available_gas(2000000)] fn test_safe_transfer_from_to_owner() { @@ -522,6 +797,60 @@ fn test_safe_transfer_from_to_owner() { assert(ERC721::balance_of(owner) == 1, 'Balance of owner after'); } +#[test] +#[available_gas(2000000)] +fn test_safeTransferFrom_to_owner() { + let token_id = TOKEN_ID; + let owner = setup_receiver(); + ERC721::initializer(NAME, SYMBOL); + ERC721::_mint(owner, token_id); + + assert(ERC721::owner_of(token_id) == owner, 'Ownership before'); + assert(ERC721::balance_of(owner) == 1, 'Balance of owner before'); + + set_caller_address(owner); + ERC721::safeTransferFrom(owner, owner, token_id, DATA(true)); + + assert(ERC721::owner_of(token_id) == owner, 'Ownership after'); + assert(ERC721::balance_of(owner) == 1, 'Balance of owner after'); +} + +#[test] +#[available_gas(2000000)] +fn test_safe_transfer_from_to_owner_camel() { + let token_id = TOKEN_ID; + let owner = setup_camel_receiver(); + ERC721::initializer(NAME, SYMBOL); + ERC721::_mint(owner, token_id); + + assert(ERC721::owner_of(token_id) == owner, 'Ownership before'); + assert(ERC721::balance_of(owner) == 1, 'Balance of owner before'); + + set_caller_address(owner); + ERC721::safe_transfer_from(owner, owner, token_id, DATA(true)); + + assert(ERC721::owner_of(token_id) == owner, 'Ownership after'); + assert(ERC721::balance_of(owner) == 1, 'Balance of owner after'); +} + +#[test] +#[available_gas(2000000)] +fn test_safeTransferFrom_to_owner_camel() { + let token_id = TOKEN_ID; + let owner = setup_camel_receiver(); + ERC721::initializer(NAME, SYMBOL); + ERC721::_mint(owner, token_id); + + assert(ERC721::owner_of(token_id) == owner, 'Ownership before'); + assert(ERC721::balance_of(owner) == 1, 'Balance of owner before'); + + set_caller_address(owner); + ERC721::safeTransferFrom(owner, owner, token_id, DATA(true)); + + assert(ERC721::owner_of(token_id) == owner, 'Ownership after'); + assert(ERC721::balance_of(owner) == 1, 'Balance of owner after'); +} + #[test] #[available_gas(2000000)] fn test_safe_transfer_from_approved() { @@ -541,6 +870,63 @@ fn test_safe_transfer_from_approved() { assert_state_after_transfer(token_id, owner, receiver); } +#[test] +#[available_gas(2000000)] +fn test_safeTransferFrom_approved() { + setup(); + let receiver = setup_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(token_id, owner, receiver); + + set_caller_address(owner); + ERC721::approve(OPERATOR(), token_id); + + set_caller_address(OPERATOR()); + ERC721::safeTransferFrom(owner, receiver, token_id, DATA(true)); + + assert_state_after_transfer(token_id, owner, receiver); +} + +#[test] +#[available_gas(2000000)] +fn test_safe_transfer_from_approved_camel() { + setup(); + let receiver = setup_camel_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(token_id, owner, receiver); + + set_caller_address(owner); + ERC721::approve(OPERATOR(), token_id); + + set_caller_address(OPERATOR()); + ERC721::safe_transfer_from(owner, receiver, token_id, DATA(true)); + + assert_state_after_transfer(token_id, owner, receiver); +} + +#[test] +#[available_gas(2000000)] +fn test_safeTransferFrom_approved_camel() { + setup(); + let receiver = setup_camel_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(token_id, owner, receiver); + + set_caller_address(owner); + ERC721::approve(OPERATOR(), token_id); + + set_caller_address(OPERATOR()); + ERC721::safeTransferFrom(owner, receiver, token_id, DATA(true)); + + assert_state_after_transfer(token_id, owner, receiver); +} + #[test] #[available_gas(2000000)] fn test_safe_transfer_from_approved_for_all() { @@ -560,6 +946,63 @@ fn test_safe_transfer_from_approved_for_all() { assert_state_after_transfer(token_id, owner, receiver); } +#[test] +#[available_gas(2000000)] +fn test_safeTransferFrom_approved_for_all() { + setup(); + let receiver = setup_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(token_id, owner, receiver); + + set_caller_address(owner); + ERC721::set_approval_for_all(OPERATOR(), true); + + set_caller_address(OPERATOR()); + ERC721::safeTransferFrom(owner, receiver, token_id, DATA(true)); + + assert_state_after_transfer(token_id, owner, receiver); +} + +#[test] +#[available_gas(2000000)] +fn test_safe_transfer_from_approved_for_all_camel() { + setup(); + let receiver = setup_camel_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(token_id, owner, receiver); + + set_caller_address(owner); + ERC721::set_approval_for_all(OPERATOR(), true); + + set_caller_address(OPERATOR()); + ERC721::safe_transfer_from(owner, receiver, token_id, DATA(true)); + + assert_state_after_transfer(token_id, owner, receiver); +} + +#[test] +#[available_gas(2000000)] +fn test_safeTransferFrom_approved_for_all_camel() { + setup(); + let receiver = setup_camel_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(token_id, owner, receiver); + + set_caller_address(owner); + ERC721::set_approval_for_all(OPERATOR(), true); + + set_caller_address(OPERATOR()); + ERC721::safeTransferFrom(owner, receiver, token_id, DATA(true)); + + assert_state_after_transfer(token_id, owner, receiver); +} + #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC721: unauthorized caller', ))] @@ -569,8 +1012,17 @@ fn test_safe_transfer_from_unauthorized() { ERC721::safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); } +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: unauthorized caller', ))] +fn test_safeTransferFrom_unauthorized() { + setup(); + set_caller_address(OTHER()); + ERC721::safeTransferFrom(OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); +} + // -// __transfer +// _transfer // #[test] @@ -611,9 +1063,9 @@ fn test__transfer_from_invalid_owner() { ERC721::_transfer(RECIPIENT(), OWNER(), TOKEN_ID); } -/// -/// Mint -/// +// +// _mint +// #[test] #[available_gas(2000000)] @@ -641,9 +1093,9 @@ fn test__mint_already_exist() { ERC721::_mint(RECIPIENT(), TOKEN_ID); } -/// -/// _safe_mint -/// +// +// _safe_mint +// #[test] #[available_gas(2000000)] @@ -656,6 +1108,17 @@ fn test__safe_mint_to_receiver() { assert_state_after_mint(token_id, recipient); } +#[test] +#[available_gas(2000000)] +fn test__safe_mint_to_receiver_camel() { + let recipient = setup_camel_receiver(); + let token_id = TOKEN_ID; + + assert_state_before_mint(recipient); + ERC721::_safe_mint(recipient, token_id, DATA(true)); + assert_state_after_mint(token_id, recipient); +} + #[test] #[available_gas(2000000)] fn test__safe_mint_to_account() { @@ -667,6 +1130,17 @@ fn test__safe_mint_to_account() { assert_state_after_mint(token_id, account); } +#[test] +#[available_gas(2000000)] +fn test__safe_mint_to_account_camel() { + let account = setup_camel_account(); + let token_id = TOKEN_ID; + + assert_state_before_mint(account); + ERC721::_safe_mint(account, token_id, DATA(true)); + assert_state_after_mint(token_id, account); +} + #[test] #[available_gas(2000000)] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] @@ -691,6 +1165,18 @@ fn test__safe_mint_to_receiver_failure() { assert_state_after_mint(token_id, recipient); } +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ERC721: safe mint failed', ))] +fn test__safe_mint_to_receiver_failure_camel() { + let recipient = setup_camel_receiver(); + let token_id = TOKEN_ID; + + assert_state_before_mint(recipient); + ERC721::_safe_mint(recipient, token_id, DATA(false)); + assert_state_after_mint(token_id, recipient); +} + #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC721: invalid receiver', ))] @@ -706,9 +1192,9 @@ fn test__safe_mint_already_exist() { ERC721::_safe_mint(RECIPIENT(), TOKEN_ID, DATA(true)); } -/// -/// Burn -/// +// +// _burn +// #[test] #[available_gas(2000000)] @@ -735,9 +1221,9 @@ fn test__burn_nonexistent() { ERC721::_burn(TOKEN_ID); } -/// -/// _set_token_uri -/// +// +// _set_token_uri +// #[test] #[available_gas(2000000)] diff --git a/src/openzeppelin/token/erc20/erc20.cairo b/src/openzeppelin/token/erc20/erc20.cairo index 3342742ba..f778a0d47 100644 --- a/src/openzeppelin/token/erc20/erc20.cairo +++ b/src/openzeppelin/token/erc20/erc20.cairo @@ -247,9 +247,9 @@ mod ERC20 { decrease_allowance(spender, subtractedValue) } - /// - /// Internals - /// + // + // Internals + // #[internal] fn initializer(name_: felt252, symbol_: felt252) { diff --git a/src/openzeppelin/token/erc721.cairo b/src/openzeppelin/token/erc721.cairo index 9b6c34399..0fab567a8 100644 --- a/src/openzeppelin/token/erc721.cairo +++ b/src/openzeppelin/token/erc721.cairo @@ -2,3 +2,4 @@ mod erc721; use erc721::ERC721; mod interface; mod dual721; +mod dual721_receiver; diff --git a/src/openzeppelin/token/erc721/dual721_receiver.cairo b/src/openzeppelin/token/erc721/dual721_receiver.cairo new file mode 100644 index 000000000..3dc682b87 --- /dev/null +++ b/src/openzeppelin/token/erc721/dual721_receiver.cairo @@ -0,0 +1,46 @@ +use array::ArrayTrait; +use starknet::ContractAddress; +use starknet::SyscallResultTrait; +use openzeppelin::utils::try_selector_with_fallback; +use openzeppelin::utils::selectors; +use openzeppelin::utils::serde::SerializedAppend; +use openzeppelin::utils::UnwrapAndCast; + +#[derive(Copy, Drop)] +struct DualCaseERC721Receiver { + contract_address: ContractAddress +} + +trait DualCaseERC721ReceiverTrait { + fn on_erc721_received( + self: @DualCaseERC721Receiver, + operator: ContractAddress, + from: ContractAddress, + token_id: u256, + data: Span + ) -> felt252; +} + +impl DualCaseERC721ReceiverImpl of DualCaseERC721ReceiverTrait { + fn on_erc721_received( + self: @DualCaseERC721Receiver, + operator: ContractAddress, + from: ContractAddress, + token_id: u256, + data: Span + ) -> felt252 { + let mut args = ArrayTrait::new(); + args.append_serde(operator); + args.append_serde(from); + args.append_serde(token_id); + args.append_serde(data); + + try_selector_with_fallback( + *self.contract_address, + selectors::on_erc721_received, + selectors::onERC721Received, + args.span() + ) + .unwrap_and_cast() + } +} diff --git a/src/openzeppelin/token/erc721/erc721.cairo b/src/openzeppelin/token/erc721/erc721.cairo index 8f5f5088a..3cda6ff3f 100644 --- a/src/openzeppelin/token/erc721/erc721.cairo +++ b/src/openzeppelin/token/erc721/erc721.cairo @@ -55,25 +55,20 @@ trait ERC721ABI { #[contract] mod ERC721 { - // OZ modules use openzeppelin::account; - use openzeppelin::introspection::src5; - use openzeppelin::token::erc721; - - // Dispatchers use openzeppelin::introspection::dual_src5::DualCaseSRC5; use openzeppelin::introspection::dual_src5::DualCaseSRC5Trait; - use super::super::interface::ERC721ReceiverABIDispatcher; - use super::super::interface::ERC721ReceiverABIDispatcherTrait; + use openzeppelin::introspection::src5; + use openzeppelin::token::erc721; + use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721Receiver; + use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721ReceiverTrait; + use openzeppelin::utils::serde::SpanSerde; - // Other + use array::SpanTrait; + use option::OptionTrait; use starknet::ContractAddress; use starknet::get_caller_address; use zeroable::Zeroable; - use option::OptionTrait; - use array::SpanTrait; - use traits::Into; - use openzeppelin::utils::serde::SpanSerde; struct Storage { _name: felt252, @@ -462,8 +457,7 @@ mod ERC721 { if (DualCaseSRC5 { contract_address: to }.supports_interface(erc721::interface::IERC721_RECEIVER_ID)) { - // todo add casing fallback mechanism - ERC721ReceiverABIDispatcher { + DualCaseERC721Receiver { contract_address: to } .on_erc721_received( diff --git a/src/openzeppelin/token/erc721/interface.cairo b/src/openzeppelin/token/erc721/interface.cairo index 53292dd94..aa0acc454 100644 --- a/src/openzeppelin/token/erc721/interface.cairo +++ b/src/openzeppelin/token/erc721/interface.cairo @@ -58,12 +58,14 @@ trait ERC721ReceiverABI { ) -> felt252; } +#[abi] trait IERC721Receiver { fn on_erc721_received( operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span ) -> felt252; } +#[abi] trait IERC721ReceiverCamel { fn onERC721Received( operator: ContractAddress, from: ContractAddress, tokenId: u256, data: Span diff --git a/src/openzeppelin/utils/selectors.cairo b/src/openzeppelin/utils/selectors.cairo index 6541e28ab..ee89772f9 100644 --- a/src/openzeppelin/utils/selectors.cairo +++ b/src/openzeppelin/utils/selectors.cairo @@ -53,6 +53,14 @@ const safe_transfer_from: felt252 = 0x16f0218b33b5cf273196787d7cf139a9ad13d58e6674dcdce722b3bf8389863; const safeTransferFrom: felt252 = 0x19d59d013d4aa1a8b1ce4c8299086f070733b453c02d0dc46e735edc04d6444; +// +// ERC721Receiver +// + +const on_erc721_received: felt252 = + 0x38c7ee9f0855dfe219aea022b141d9b2ec0f6b68395d221c3f331c7ca4fb608; +const onERC721Received: felt252 = 0xfa119a8fafc6f1a02deb36fe5efbcc4929ef2021e50cf1cb6d1a780ccd009b; + // // ERC20 // From 65d03ae58c4aba9d1a9e9dc892ab132a2d1d9e1f Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 13 Jul 2023 20:41:41 +0200 Subject: [PATCH 064/246] Add owner param to ownable initializer (#660) * feat: add owner param to ownable initializer * feat: apply review updates * fix: remove unnecessary mut * Update src/openzeppelin/tests/access/test_dual_ownable.cairo Co-authored-by: Andrew Fleming --------- Co-authored-by: Andrew Fleming --- src/openzeppelin/access/ownable/ownable.cairo | 5 ++--- src/openzeppelin/tests/access/test_dual_ownable.cairo | 7 +++---- src/openzeppelin/tests/access/test_ownable.cairo | 2 +- src/openzeppelin/tests/mocks/dual_ownable_mocks.cairo | 8 ++++---- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/openzeppelin/access/ownable/ownable.cairo b/src/openzeppelin/access/ownable/ownable.cairo index 1027d4ca7..29e21dde0 100644 --- a/src/openzeppelin/access/ownable/ownable.cairo +++ b/src/openzeppelin/access/ownable/ownable.cairo @@ -71,9 +71,8 @@ mod Ownable { // Internals #[internal] - fn initializer() { - let caller: ContractAddress = get_caller_address(); - _transfer_ownership(caller); + fn initializer(owner: ContractAddress) { + _transfer_ownership(owner); } #[internal] diff --git a/src/openzeppelin/tests/access/test_dual_ownable.cairo b/src/openzeppelin/tests/access/test_dual_ownable.cairo index c093838fc..87841fe8d 100644 --- a/src/openzeppelin/tests/access/test_dual_ownable.cairo +++ b/src/openzeppelin/tests/access/test_dual_ownable.cairo @@ -17,6 +17,7 @@ use openzeppelin::tests::mocks::dual_ownable_mocks::SnakeOwnablePanicMock; use openzeppelin::tests::mocks::dual_ownable_mocks::CamelOwnablePanicMock; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils; +use openzeppelin::utils::serde::SerializedAppend; // // Constants @@ -36,14 +37,14 @@ fn NEW_OWNER() -> ContractAddress { fn setup_snake() -> (DualCaseOwnable, IOwnableDispatcher) { let mut calldata = ArrayTrait::new(); - set_caller_address(OWNER()); + calldata.append_serde(OWNER()); let target = utils::deploy(SnakeOwnableMock::TEST_CLASS_HASH, calldata); (DualCaseOwnable { contract_address: target }, IOwnableDispatcher { contract_address: target }) } fn setup_camel() -> (DualCaseOwnable, IOwnableCamelDispatcher) { let mut calldata = ArrayTrait::new(); - set_caller_address(OWNER()); + calldata.append_serde(OWNER()); let target = utils::deploy(CamelOwnableMock::TEST_CLASS_HASH, calldata); ( DualCaseOwnable { @@ -56,13 +57,11 @@ fn setup_camel() -> (DualCaseOwnable, IOwnableCamelDispatcher) { fn setup_non_ownable() -> DualCaseOwnable { let calldata = ArrayTrait::new(); - set_caller_address(OWNER()); let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); DualCaseOwnable { contract_address: target } } fn setup_ownable_panic() -> (DualCaseOwnable, DualCaseOwnable) { - set_caller_address(OWNER()); let snake_target = utils::deploy(SnakeOwnablePanicMock::TEST_CLASS_HASH, ArrayTrait::new()); let camel_target = utils::deploy(CamelOwnablePanicMock::TEST_CLASS_HASH, ArrayTrait::new()); ( diff --git a/src/openzeppelin/tests/access/test_ownable.cairo b/src/openzeppelin/tests/access/test_ownable.cairo index 7f3980699..a3a4f0af9 100644 --- a/src/openzeppelin/tests/access/test_ownable.cairo +++ b/src/openzeppelin/tests/access/test_ownable.cairo @@ -19,7 +19,7 @@ fn OTHER() -> ContractAddress { fn setup() { testing::set_caller_address(OWNER()); - Ownable::initializer(); + Ownable::initializer(OWNER()); } // diff --git a/src/openzeppelin/tests/mocks/dual_ownable_mocks.cairo b/src/openzeppelin/tests/mocks/dual_ownable_mocks.cairo index f84bc195e..407d63e3b 100644 --- a/src/openzeppelin/tests/mocks/dual_ownable_mocks.cairo +++ b/src/openzeppelin/tests/mocks/dual_ownable_mocks.cairo @@ -4,8 +4,8 @@ mod SnakeOwnableMock { use openzeppelin::access::ownable::Ownable; #[constructor] - fn constructor() { - Ownable::initializer(); + fn constructor(owner: ContractAddress) { + Ownable::initializer(owner); } #[view] @@ -30,8 +30,8 @@ mod CamelOwnableMock { use openzeppelin::access::ownable::Ownable; #[constructor] - fn constructor() { - Ownable::initializer(); + fn constructor(owner: ContractAddress) { + Ownable::initializer(owner); } #[view] From a863859f585bd5a7554c21b4ad2ea1ee36a2ceac Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Tue, 18 Jul 2023 21:56:06 +0200 Subject: [PATCH 065/246] Migrate SRC5 to Cairo 2 (#664) * feat: migrate files * feat: add interface.cairo * feat: apply review updates * refactor: improve readability --- Cargo.lock | 1020 +++++++++++------ Cargo.toml | 57 +- cairo | 2 +- src/openzeppelin/introspection.cairo | 1 + .../introspection/dual_src5.cairo | 4 +- .../introspection/interface.cairo | 11 + src/openzeppelin/introspection/src5.cairo | 60 +- src/openzeppelin/lib.cairo | 8 +- src/openzeppelin/tests.cairo | 12 +- .../access/test_dual_accesscontrol.cairo | 21 +- .../tests/access/test_dual_ownable.cairo | 14 +- .../tests/account/test_dual_account.cairo | 21 +- .../tests/introspection/test_dual_src5.cairo | 27 +- .../tests/introspection/test_src5.cairo | 27 +- src/openzeppelin/tests/mocks.cairo | 38 +- .../tests/mocks/non_implementing_mock.cairo | 9 +- src/openzeppelin/tests/mocks/src5_mocks.cairo | 42 +- .../tests/token/test_dual721.cairo | 14 +- .../tests/token/test_dual721_receiver.cairo | 21 +- src/openzeppelin/token/erc721/erc721.cairo | 9 +- src/openzeppelin/utils/serde.cairo | 16 - 21 files changed, 850 insertions(+), 584 deletions(-) create mode 100644 src/openzeppelin/introspection/interface.cairo diff --git a/Cargo.lock b/Cargo.lock index a5ca15c63..278c09912 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,17 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.3" @@ -22,6 +33,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "anyhow" version = "1.0.70" @@ -29,129 +55,139 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] -name = "ark-ff" -version = "0.3.0" +name = "ark-ec" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" dependencies = [ - "ark-ff-asm 0.3.0", - "ark-ff-macros 0.3.0", - "ark-serialize 0.3.0", - "ark-std 0.3.0", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", "derivative", - "num-bigint", + "hashbrown 0.13.2", + "itertools 0.10.5", "num-traits 0.2.15", - "paste", - "rustc_version 0.3.3", "zeroize", ] [[package]] name = "ark-ff" -version = "0.4.0-alpha.7" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cf4b42c978bd9b967f6a2ba54a4cc57f34780f9d0cf86916f8f7e922b4f80fe" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" dependencies = [ - "ark-ff-asm 0.4.0-alpha.7", - "ark-ff-macros 0.4.0-alpha.7", - "ark-serialize 0.4.0-alpha.7", - "ark-std 0.4.0-alpha", + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", "derivative", - "digest 0.10.6", - "itertools", + "digest", + "itertools 0.10.5", "num-bigint", "num-traits 0.2.15", "paste", - "rustc_version 0.4.0", + "rustc_version", "zeroize", ] [[package]] name = "ark-ff-asm" -version = "0.3.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ "quote", "syn 1.0.103", ] [[package]] -name = "ark-ff-asm" -version = "0.4.0-alpha.7" +name = "ark-ff-macros" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3ba0a3cf584a8ede533a1749b86040e9018cf752265fc39a71c69fe1fafaba5" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ + "num-bigint", + "num-traits 0.2.15", + "proc-macro2", "quote", "syn 1.0.103", ] [[package]] -name = "ark-ff-macros" -version = "0.3.0" +name = "ark-poly" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" dependencies = [ - "num-bigint", - "num-traits 0.2.15", - "quote", - "syn 1.0.103", + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", ] [[package]] -name = "ark-ff-macros" -version = "0.4.0-alpha.7" +name = "ark-secp256k1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9989a01ec42d2f31232796831077ef730d4cd0d0931f5ef6962a43ebddd215fa" +checksum = "4c02e954eaeb4ddb29613fee20840c2bbc85ca4396d53e33837e11905363c5f2" dependencies = [ - "num-bigint", - "num-traits 0.2.15", - "proc-macro2", - "quote", - "syn 1.0.103", + "ark-ec", + "ark-ff", + "ark-std", ] [[package]] -name = "ark-serialize" -version = "0.3.0" +name = "ark-secp256r1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +checksum = "3975a01b0a6e3eae0f72ec7ca8598a6620fc72fa5981f6f5cca33b7cd788f633" dependencies = [ - "ark-std 0.3.0", - "digest 0.9.0", + "ark-ec", + "ark-ff", + "ark-std", ] [[package]] name = "ark-serialize" -version = "0.4.0-alpha.7" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84aeb60f7c6792ae71e9e2225412ecd59d7c85cfec4183a8c497f2a3a4cdb36e" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" dependencies = [ - "ark-std 0.4.0-alpha", - "digest 0.10.6", + "ark-serialize-derive", + "ark-std", + "digest", "num-bigint", ] [[package]] -name = "ark-std" -version = "0.3.0" +name = "ark-serialize-derive" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ - "num-traits 0.2.15", - "rand", + "proc-macro2", + "quote", + "syn 1.0.103", ] [[package]] name = "ark-std" -version = "0.4.0-alpha" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5b16d734e9b2e43886ff586755219df7fb9639cc04ab00c7e636708a5ba06a" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ "num-traits 0.2.15", "rand", ] +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "ascii-canvas" version = "3.0.0" @@ -191,9 +227,9 @@ dependencies = [ [[package]] name = "auto_impl" -version = "0.5.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7862e21c893d65a1650125d157eaeec691439379a1cee17ee49031b79236ada4" +checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ "proc-macro-error", "proc-macro2", @@ -215,9 +251,9 @@ checksum = "bc0455254eb5c6964c4545d8bac815e1a1be4f3afe0ae695ea539c12d728d44b" [[package]] name = "bincode" -version = "2.0.0-rc.2" +version = "2.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bb50c5a2ef4b9b1e7ae73e3a73b52ea24b20312d629f9c4df28260b7ad2c3c4" +checksum = "f11ea1a0346b94ef188834a65c068a03aec181c94896d481d7a0a40d85b0ce95" dependencies = [ "serde", ] @@ -280,17 +316,34 @@ version = "3.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + [[package]] name = "bytes" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +[[package]] +name = "cairo-compile" +version = "2.0.2" +dependencies = [ + "anyhow", + "cairo-lang-compiler", + "cairo-lang-utils", + "clap", + "log", +] + [[package]] name = "cairo-felt" -version = "0.3.0-rc1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a93dedd19b8edf685798f1f12e4e0ac21ac196ea5262c300783f69f3fa0cb28b" +checksum = "0f8de851723a7d13ed8b0b588a78ffa6b38d8e1f3eb4b6e71a96376510e5504a" dependencies = [ "lazy_static", "num-bigint", @@ -299,17 +352,33 @@ dependencies = [ "serde", ] +[[package]] +name = "cairo-format" +version = "2.0.2" +dependencies = [ + "anyhow", + "cairo-lang-formatter", + "cairo-lang-utils", + "clap", + "colored", + "ignore", + "log", +] + [[package]] name = "cairo-lang-casm" -version = "1.1.1" +version = "2.0.2" dependencies = [ "cairo-lang-utils", "env_logger", "indoc", - "itertools", + "itertools 0.11.0", "num-bigint", "num-traits 0.2.15", + "parity-scale-codec", + "parity-scale-codec-derive", "pretty_assertions", + "schemars", "serde", "test-case", "test-log", @@ -318,7 +387,7 @@ dependencies = [ [[package]] name = "cairo-lang-compiler" -version = "1.1.1" +version = "2.0.2" dependencies = [ "anyhow", "cairo-lang-defs", @@ -333,7 +402,6 @@ dependencies = [ "cairo-lang-sierra-generator", "cairo-lang-syntax", "cairo-lang-utils", - "clap", "log", "salsa", "smol_str", @@ -343,7 +411,7 @@ dependencies = [ [[package]] name = "cairo-lang-debug" -version = "1.1.1" +version = "2.0.2" dependencies = [ "cairo-lang-proc-macros", "cairo-lang-utils", @@ -354,7 +422,7 @@ dependencies = [ [[package]] name = "cairo-lang-defs" -version = "1.1.1" +version = "2.0.2" dependencies = [ "cairo-lang-debug", "cairo-lang-diagnostics", @@ -364,9 +432,9 @@ dependencies = [ "cairo-lang-test-utils", "cairo-lang-utils", "env_logger", - "indexmap", + "indexmap 2.0.0", "indoc", - "itertools", + "itertools 0.11.0", "pretty_assertions", "salsa", "smol_str", @@ -375,14 +443,15 @@ dependencies = [ [[package]] name = "cairo-lang-diagnostics" -version = "1.1.1" +version = "2.0.2" dependencies = [ + "cairo-lang-debug", "cairo-lang-filesystem", "cairo-lang-proc-macros", "cairo-lang-utils", "env_logger", "indoc", - "itertools", + "itertools 0.11.0", "pretty_assertions", "salsa", "test-log", @@ -390,21 +459,21 @@ dependencies = [ [[package]] name = "cairo-lang-eq-solver" -version = "1.1.1" +version = "2.0.2" dependencies = [ "cairo-lang-utils", "env_logger", "good_lp", - "indexmap", + "indexmap 2.0.0", "indoc", - "itertools", + "itertools 0.11.0", "test-case", "test-log", ] [[package]] name = "cairo-lang-filesystem" -version = "1.1.1" +version = "2.0.2" dependencies = [ "cairo-lang-debug", "cairo-lang-utils", @@ -419,7 +488,7 @@ dependencies = [ [[package]] name = "cairo-lang-formatter" -version = "1.1.1" +version = "2.0.2" dependencies = [ "anyhow", "cairo-lang-diagnostics", @@ -427,11 +496,10 @@ dependencies = [ "cairo-lang-parser", "cairo-lang-syntax", "cairo-lang-utils", - "clap", "colored", "diffy", "ignore", - "itertools", + "itertools 0.11.0", "log", "pretty_assertions", "salsa", @@ -442,7 +510,7 @@ dependencies = [ [[package]] name = "cairo-lang-language-server" -version = "1.1.1" +version = "2.0.2" dependencies = [ "anyhow", "cairo-lang-compiler", @@ -474,7 +542,7 @@ dependencies = [ [[package]] name = "cairo-lang-lowering" -version = "1.1.1" +version = "2.0.2" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -489,9 +557,9 @@ dependencies = [ "cairo-lang-utils", "env_logger", "id-arena", - "indexmap", + "indexmap 2.0.0", "indoc", - "itertools", + "itertools 0.11.0", "log", "num-bigint", "num-traits 0.2.15", @@ -503,7 +571,7 @@ dependencies = [ [[package]] name = "cairo-lang-parser" -version = "1.1.1" +version = "2.0.2" dependencies = [ "cairo-lang-diagnostics", "cairo-lang-filesystem", @@ -513,7 +581,7 @@ dependencies = [ "cairo-lang-utils", "colored", "env_logger", - "itertools", + "itertools 0.11.0", "log", "num-bigint", "num-traits 0.2.15", @@ -527,7 +595,7 @@ dependencies = [ [[package]] name = "cairo-lang-plugins" -version = "1.1.1" +version = "2.0.2" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -540,7 +608,8 @@ dependencies = [ "cairo-lang-utils", "env_logger", "indoc", - "itertools", + "itertools 0.11.0", + "num-bigint", "salsa", "serde_json", "smol_str", @@ -550,18 +619,19 @@ dependencies = [ [[package]] name = "cairo-lang-proc-macros" -version = "1.1.1" +version = "2.0.2" dependencies = [ "cairo-lang-debug", "quote", - "syn 1.0.103", + "syn 2.0.26", ] [[package]] name = "cairo-lang-project" -version = "1.1.1" +version = "2.0.2" dependencies = [ "cairo-lang-filesystem", + "cairo-lang-utils", "indoc", "serde", "smol_str", @@ -572,11 +642,13 @@ dependencies = [ [[package]] name = "cairo-lang-runner" -version = "1.1.1" +version = "2.0.2" dependencies = [ "anyhow", - "ark-ff 0.4.0-alpha.7", - "ark-std 0.3.0", + "ark-ff", + "ark-secp256k1", + "ark-secp256r1", + "ark-std", "cairo-felt", "cairo-lang-casm", "cairo-lang-compiler", @@ -590,11 +662,11 @@ dependencies = [ "cairo-lang-sierra-gas", "cairo-lang-sierra-generator", "cairo-lang-sierra-to-casm", + "cairo-lang-sierra-type-size", "cairo-lang-starknet", "cairo-lang-utils", "cairo-vm", - "clap", - "itertools", + "itertools 0.11.0", "keccak", "num-bigint", "num-integer", @@ -607,7 +679,7 @@ dependencies = [ [[package]] name = "cairo-lang-semantic" -version = "1.1.1" +version = "2.0.2" dependencies = [ "assert_matches", "cairo-lang-debug", @@ -623,7 +695,7 @@ dependencies = [ "env_logger", "id-arena", "indoc", - "itertools", + "itertools 0.11.0", "log", "num-bigint", "num-traits 0.2.15", @@ -636,7 +708,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra" -version = "1.1.1" +version = "2.0.2" dependencies = [ "assert_matches", "bimap", @@ -646,7 +718,7 @@ dependencies = [ "derivative", "env_logger", "indoc", - "itertools", + "itertools 0.11.0", "lalrpop", "lalrpop-util", "num-bigint", @@ -664,14 +736,15 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-ap-change" -version = "1.1.1" +version = "2.0.2" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", + "cairo-lang-sierra-type-size", "cairo-lang-utils", "env_logger", "indoc", - "itertools", + "itertools 0.11.0", "test-case", "test-log", "thiserror", @@ -679,15 +752,16 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-gas" -version = "1.1.1" +version = "2.0.2" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", + "cairo-lang-sierra-type-size", "cairo-lang-test-utils", "cairo-lang-utils", "env_logger", "indoc", - "itertools", + "itertools 0.11.0", "pretty_assertions", "test-case", "test-log", @@ -696,7 +770,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-generator" -version = "1.1.1" +version = "2.0.2" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -713,9 +787,9 @@ dependencies = [ "cairo-lang-utils", "env_logger", "id-arena", - "indexmap", + "indexmap 2.0.0", "indoc", - "itertools", + "itertools 0.11.0", "log", "num-bigint", "pretty_assertions", @@ -727,20 +801,19 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-to-casm" -version = "1.1.1" +version = "2.0.2" dependencies = [ - "anyhow", "assert_matches", "cairo-felt", "cairo-lang-casm", "cairo-lang-sierra", "cairo-lang-sierra-ap-change", "cairo-lang-sierra-gas", + "cairo-lang-sierra-type-size", "cairo-lang-utils", - "clap", "env_logger", "indoc", - "itertools", + "itertools 0.11.0", "log", "num-bigint", "num-traits 0.2.15", @@ -750,9 +823,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cairo-lang-sierra-type-size" +version = "2.0.2" +dependencies = [ + "cairo-lang-sierra", + "cairo-lang-utils", +] + [[package]] name = "cairo-lang-starknet" -version = "1.1.1" +version = "2.0.2" dependencies = [ "anyhow", "cairo-felt", @@ -773,12 +854,12 @@ dependencies = [ "cairo-lang-syntax", "cairo-lang-test-utils", "cairo-lang-utils", - "clap", "convert_case", "env_logger", "genco", + "indent", "indoc", - "itertools", + "itertools 0.11.0", "log", "num-bigint", "num-integer", @@ -796,7 +877,7 @@ dependencies = [ [[package]] name = "cairo-lang-syntax" -version = "1.1.1" +version = "2.0.2" dependencies = [ "cairo-lang-debug", "cairo-lang-filesystem", @@ -814,19 +895,17 @@ dependencies = [ [[package]] name = "cairo-lang-syntax-codegen" -version = "1.1.1" +version = "2.0.2" dependencies = [ - "cairo-lang-utils", "env_logger", "genco", - "log", "test-log", "xshell", ] [[package]] name = "cairo-lang-test-runner" -version = "1.1.1" +version = "2.0.2" dependencies = [ "anyhow", "cairo-felt", @@ -848,9 +927,8 @@ dependencies = [ "cairo-lang-syntax", "cairo-lang-utils", "cairo-vm", - "clap", "colored", - "itertools", + "itertools 0.11.0", "num-bigint", "num-traits 0.2.15", "rayon", @@ -860,7 +938,7 @@ dependencies = [ [[package]] name = "cairo-lang-test-utils" -version = "1.1.1" +version = "2.0.2" dependencies = [ "cairo-lang-utils", "env_logger", @@ -871,15 +949,17 @@ dependencies = [ [[package]] name = "cairo-lang-utils" -version = "1.1.1" +version = "2.0.2" dependencies = [ "env_logger", - "indexmap", - "itertools", + "indexmap 2.0.0", + "itertools 0.11.0", "log", "num-bigint", "num-integer", "num-traits 0.2.15", + "parity-scale-codec", + "schemars", "serde", "serde_json", "test-case", @@ -888,27 +968,52 @@ dependencies = [ ] [[package]] -name = "cairo-take_until_unbalanced" -version = "0.24.2-rc1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e174056df7cfe9b579376f32de405722e1090c74deb2540bb0cd9e7931772d" +name = "cairo-language-server" +version = "2.0.2" dependencies = [ - "nom", + "cairo-lang-language-server", + "cairo-lang-utils", + "log", + "tokio", +] + +[[package]] +name = "cairo-run" +version = "2.0.2" +dependencies = [ + "anyhow", + "cairo-lang-compiler", + "cairo-lang-diagnostics", + "cairo-lang-runner", + "cairo-lang-sierra", + "cairo-lang-sierra-generator", + "cairo-lang-starknet", + "cairo-lang-utils", + "clap", +] + +[[package]] +name = "cairo-test" +version = "2.0.2" +dependencies = [ + "anyhow", + "cairo-lang-compiler", + "cairo-lang-test-runner", + "clap", ] [[package]] name = "cairo-vm" -version = "0.3.0-rc1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f4af8c3e7be5ac46b7da4a3ab551ee4c8c8e2fdb501a4dda4dceef4c66dbcde" +checksum = "656c25c13b6ffcc75e081292f3c23f27e429b3d4b8d69ecdc327a441798e91f4" dependencies = [ "anyhow", "bincode", "bitvec", "cairo-felt", - "cairo-take_until_unbalanced", "generic-array", - "hashbrown 0.13.2", + "hashbrown 0.14.0", "hex", "keccak", "lazy_static", @@ -916,15 +1021,14 @@ dependencies = [ "nom", "num-bigint", "num-integer", + "num-prime", "num-traits 0.2.15", - "rand_core", + "rand", "serde", - "serde_bytes", "serde_json", "sha2", "sha3", "starknet-crypto", - "thiserror", "thiserror-no-std", ] @@ -1072,9 +1176,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.4.9" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" dependencies = [ "generic-array", "subtle", @@ -1101,41 +1205,6 @@ dependencies = [ "syn 1.0.103", ] -[[package]] -name = "darling" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 1.0.103", -] - -[[package]] -name = "darling_macro" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" -dependencies = [ - "darling_core", - "quote", - "syn 1.0.103", -] - [[package]] name = "dashmap" version = "5.4.0" @@ -1160,37 +1229,6 @@ dependencies = [ "syn 1.0.103", ] -[[package]] -name = "derive_builder" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" -dependencies = [ - "derive_builder_macro", -] - -[[package]] -name = "derive_builder_core" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 1.0.103", -] - -[[package]] -name = "derive_builder_macro" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" -dependencies = [ - "derive_builder_core", - "syn 1.0.103", -] - [[package]] name = "diff" version = "0.1.13" @@ -1208,18 +1246,9 @@ dependencies = [ [[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.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", @@ -1247,6 +1276,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "dyn-clone" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" + [[package]] name = "either" version = "1.8.0" @@ -1264,17 +1299,23 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.9.3" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" dependencies = [ - "atty", "humantime", + "is-terminal", "log", "regex", "termcolor", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.2.8" @@ -1440,11 +1481,20 @@ dependencies = [ "syn 1.0.103", ] +[[package]] +name = "generate-syntax" +version = "2.0.2" +dependencies = [ + "cairo-lang-syntax-codegen", + "cairo-lang-utils", + "log", +] + [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -1452,9 +1502,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "js-sys", @@ -1463,13 +1513,19 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "globset" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ - "aho-corasick", + "aho-corasick 0.7.20", "bstr", "fnv", "log", @@ -1491,6 +1547,9 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.6", +] [[package]] name = "hashbrown" @@ -1498,7 +1557,17 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash", + "ahash 0.8.3", +] + +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +dependencies = [ + "ahash 0.8.3", + "allocator-api2", "serde", ] @@ -1544,7 +1613,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.6", + "digest", ] [[package]] @@ -1565,12 +1634,6 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "idna" version = "0.3.0" @@ -1598,6 +1661,23 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.103", +] + +[[package]] +name = "indent" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f1a0777d972970f204fdf8ef319f1f4f8459131636d7e3c96c5d59570d0fa6" + [[package]] name = "indexmap" version = "1.9.2" @@ -1609,6 +1689,17 @@ dependencies = [ "serde", ] +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", + "serde", +] + [[package]] name = "indoc" version = "2.0.1" @@ -1656,6 +1747,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.4" @@ -1673,25 +1773,25 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" dependencies = [ "cpufeatures", ] [[package]] name = "lalrpop" -version = "0.19.9" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f34313ec00c2eb5c3c87ca6732ea02dcf3af99c3ff7a8fb622ffb99c9d860a87" +checksum = "da4081d44f4611b66c6dd725e6de3169f9f63905421e8626fcb86b6a898998b8" dependencies = [ "ascii-canvas", "bit-set", "diff", "ena", "is-terminal", - "itertools", + "itertools 0.10.5", "lalrpop-util", "petgraph", "pico-args", @@ -1705,9 +1805,9 @@ dependencies = [ [[package]] name = "lalrpop-util" -version = "0.19.9" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5c1f7869c94d214466c5fd432dfed12c379fd87786768d36455892d46b18edd" +checksum = "3f35c735096c0293d313e8f2a641627472b83d01b937177fe76e5e2708d31e0d" dependencies = [ "regex", ] @@ -1723,15 +1823,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.137" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libmimalloc-sys" -version = "0.1.28" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04d1c67deb83e6b75fa4fe3309e09cfeade12e7721d95322af500d3814ea60c9" +checksum = "f4ac0e912c8ef1b735e92369695618dc5b1819f5a7bf3f167301a3ba1cea515e" dependencies = [ "cc", "libc", @@ -1762,11 +1862,20 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "lru" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" +dependencies = [ + "hashbrown 0.12.3", +] + [[package]] name = "lsp-types" -version = "0.93.2" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be6e9c7e2d18f651974370d7aff703f9513e0df6e464fd795660edc77e6ca51" +checksum = "0b63735a13a1f9cd4f4835223d828ed9c2e35c8c5e61837774399f558b6a1237" dependencies = [ "bitflags", "serde", @@ -1801,9 +1910,9 @@ dependencies = [ [[package]] name = "mimalloc" -version = "0.1.32" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2374e2999959a7b583e1811a1ddbf1d3a4b9496eceb9746f1192a59d871eca" +checksum = "4e2894987a3459f3ffb755608bd82188f8ed00d0ae077f1edea29c068d639d98" dependencies = [ "libmimalloc-sys", ] @@ -1884,6 +1993,7 @@ dependencies = [ "autocfg", "num-integer", "num-traits 0.2.15", + "rand", "serde", ] @@ -1907,6 +2017,33 @@ dependencies = [ "num-traits 0.2.15", ] +[[package]] +name = "num-modular" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a5fe11d4135c3bcdf3a95b18b194afa9608a5f6ff034f5d857bc9a27fb0119" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits 0.2.15", +] + +[[package]] +name = "num-prime" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4e3bc495f6e95bc15a6c0c55ac00421504a5a43d09e3cc455d1fea7015581d" +dependencies = [ + "bitvec", + "either", + "lru", + "num-bigint", + "num-integer", + "num-modular", + "num-traits 0.2.15", + "rand", +] + [[package]] name = "num-traits" version = "0.1.43" @@ -1977,6 +2114,31 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "parity-scale-codec" +version = "3.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756d439303e94fae44f288ba881ad29670c65b0c4b0e05674ca81061bb65f2c5" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d884d78fcf214d70b1e239fcd1c6e5e95aa3be1881918da2e488cc946c7a476" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.103", +] + [[package]] name = "parking_lot" version = "0.11.2" @@ -2033,9 +2195,9 @@ checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" [[package]] name = "path-clean" -version = "0.1.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecba01bf2678719532c5e3059e0b5f0811273d94b397088b82e3bd0a78c78fdd" +checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef" [[package]] name = "percent-encoding" @@ -2043,16 +2205,6 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" -[[package]] -name = "pest" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a528564cc62c19a7acac4d81e01f39e53e25e17b934878f4c6d25cc2836e62f8" -dependencies = [ - "thiserror", - "ucd-trie", -] - [[package]] name = "petgraph" version = "0.6.2" @@ -2060,7 +2212,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 1.9.2", ] [[package]] @@ -2074,9 +2226,9 @@ dependencies = [ [[package]] name = "pico-args" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" @@ -2134,6 +2286,16 @@ dependencies = [ "yansi", ] +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -2160,18 +2322,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.52" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" dependencies = [ "proc-macro2", ] @@ -2188,6 +2350,7 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ + "libc", "rand_chacha", "rand_core", ] @@ -2207,6 +2370,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] [[package]] name = "rawpointer" @@ -2258,61 +2424,75 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.0" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick 1.0.2", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" dependencies = [ - "aho-corasick", + "aho-corasick 1.0.2", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "relative-path" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df32d82cedd1499386877b062ebe8721f806de80b08d183c70184ef17dd1d42" +checksum = "4bf2521270932c3c7bed1a59151222bd7643c79310f2916f01925e1e16255698" [[package]] name = "rfc6979" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "crypto-bigint", "hmac", - "zeroize", + "subtle", ] [[package]] name = "rstest" -version = "0.16.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b07f2d176c472198ec1e6551dc7da28f1c089652f66a7b722676c2238ebc0edf" +checksum = "2b96577ca10cb3eade7b337eb46520108a67ca2818a24d0b63f41fd62bc9651c" dependencies = [ "futures", "futures-timer", "rstest_macros", - "rustc_version 0.4.0", + "rustc_version", ] [[package]] name = "rstest_macros" -version = "0.16.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7229b505ae0706e64f37ffc54a9c163e11022a6636d58fe1f3f52018257ff9f7" +checksum = "225e674cf31712b8bb15fdbca3ec0c1b9d825c5a24407ff2b7e005fb6a29ba03" dependencies = [ "cfg-if", + "glob", "proc-macro2", "quote", - "rustc_version 0.4.0", - "syn 1.0.103", + "regex", + "relative-path", + "rustc_version", + "syn 2.0.26", "unicode-ident", ] @@ -2322,22 +2502,13 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustc_version" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" -dependencies = [ - "semver 0.11.0", -] - [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.17", + "semver", ] [[package]] @@ -2373,7 +2544,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b84d9f96071f3f3be0dc818eae3327625d8ebc95b58da37d6850724f31d3403" dependencies = [ "crossbeam-utils", - "indexmap", + "indexmap 1.9.2", "lock_api", "log", "oorandom", @@ -2406,33 +2577,48 @@ dependencies = [ [[package]] name = "scarb-metadata" -version = "1.0.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2480a9a896395a1414feebcfe8550b35f4dce0e6a437eeaa0c3d79ec938ddf42" +checksum = "26ba10c59bd4ebde82de954e3a3d6d503eb17bd634106ef144af1356b28b8f0f" dependencies = [ "camino", - "derive_builder", - "semver 1.0.17", + "semver", "serde", "serde_json", "thiserror", ] [[package]] -name = "scopeguard" -version = "1.1.0" +name = "schemars" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" +dependencies = [ + "dyn-clone", + "indexmap 1.9.2", + "schemars_derive", + "serde", + "serde_json", +] [[package]] -name = "semver" -version = "0.11.0" +name = "schemars_derive" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" dependencies = [ - "semver-parser", + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.103", ] +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "semver" version = "1.0.17" @@ -2442,49 +2628,42 @@ dependencies = [ "serde", ] -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - [[package]] name = "serde" -version = "1.0.158" +version = "1.0.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" dependencies = [ "serde_derive", ] [[package]] -name = "serde_bytes" -version = "0.11.9" +name = "serde_derive" +version = "1.0.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" +checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" dependencies = [ - "serde", + "proc-macro2", + "quote", + "syn 2.0.26", ] [[package]] -name = "serde_derive" -version = "1.0.158" +name = "serde_derive_internals" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.3", + "syn 1.0.103", ] [[package]] name = "serde_json" -version = "1.0.94" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b" dependencies = [ "itoa", "ryu", @@ -2502,27 +2681,49 @@ dependencies = [ "syn 1.0.103", ] +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", +] + [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest", ] [[package]] name = "sha3" -version = "0.10.6" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest 0.10.6", + "digest", "keccak", ] +[[package]] +name = "sierra-compile" +version = "2.0.2" +dependencies = [ + "anyhow", + "cairo-lang-sierra", + "cairo-lang-sierra-to-casm", + "cairo-lang-utils", + "clap", + "indoc", + "log", +] + [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -2589,11 +2790,21 @@ dependencies = [ "num-traits 0.1.43", ] +[[package]] +name = "starknet-compile" +version = "2.0.2" +dependencies = [ + "anyhow", + "cairo-lang-compiler", + "cairo-lang-starknet", + "clap", +] + [[package]] name = "starknet-crypto" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d49eb65d58fa98a164ad2cd4d04775885386b83bdad6060f168a38ede77c9aed" +checksum = "693e6362f150f9276e429a910481fb7f3bcb8d6aa643743f587cfece0b374874" dependencies = [ "crypto-bigint", "hex", @@ -2604,43 +2815,65 @@ dependencies = [ "rfc6979", "sha2", "starknet-crypto-codegen", - "starknet-curve", + "starknet-curve 0.3.0", "starknet-ff", "zeroize", ] [[package]] name = "starknet-crypto-codegen" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af6527b845423542c8a16e060ea1bc43f67229848e7cd4c4d80be994a84220ce" +dependencies = [ + "starknet-curve 0.4.0", + "starknet-ff", + "syn 2.0.26", +] + +[[package]] +name = "starknet-curve" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff08f74f3ac785ac34ac05c68c5bd4df280107ab35df69dbcbde35183d89eba" +checksum = "252610baff59e4c4332ce3569f7469c5d3f9b415a2240d698fb238b2b4fc0942" dependencies = [ - "starknet-curve", "starknet-ff", - "syn 1.0.103", ] [[package]] name = "starknet-curve" -version = "0.2.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0dbde7ef14d54c2117bc6d2efb68c2383005f1cd749b277c11df874d07b7af" +checksum = "a68a0d87ae56572abf83ddbfd44259a7c90dbeeee1629a1ffe223e7f9a8f3052" dependencies = [ "starknet-ff", ] [[package]] name = "starknet-ff" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78d484109da192f3a8cd58f674861c2d5e4b3e1765a466362c6f350ef213dfd1" +checksum = "db2cb1d9c0a50380cddab99cb202c6bfb3332728a2769bd0ca2ee80b0b390dd4" dependencies = [ - "ark-ff 0.3.0", + "ark-ff", "crypto-bigint", "getrandom", "hex", ] +[[package]] +name = "starknet-sierra-compile" +version = "2.0.2" +dependencies = [ + "anyhow", + "cairo-lang-sierra", + "cairo-lang-starknet", + "cairo-lang-utils", + "clap", + "serde", + "serde_json", +] + [[package]] name = "string_cache" version = "0.8.4" @@ -2679,9 +2912,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.3" +version = "2.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8234ae35e70582bfa0f1fedffa6daa248e41dd045310b19800c4a36382c8f60" +checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970" dependencies = [ "proc-macro2", "quote", @@ -2728,18 +2961,18 @@ dependencies = [ [[package]] name = "test-case" -version = "2.2.2" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21d6cf5a7dffb3f9dceec8e6b8ca528d9bd71d36c9f074defb548ce161f598c0" +checksum = "2a1d6e7bde536b0412f20765b76e921028059adfd1b90d8974d33fd3c91b25df" dependencies = [ "test-case-macros", ] [[package]] -name = "test-case-macros" -version = "2.2.2" +name = "test-case-core" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e45b7bf6e19353ddd832745c8fcf77a17a93171df7151187f26623f2b75b5b26" +checksum = "d10394d5d1e27794f772b6fc854c7e91a2dc26e2cbf807ad523370c2a59c0cee" dependencies = [ "cfg-if", "proc-macro-error", @@ -2748,6 +2981,19 @@ dependencies = [ "syn 1.0.103", ] +[[package]] +name = "test-case-macros" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeb9a44b1c6a54c1ba58b152797739dba2a83ca74e18168a68c980eb142f9404" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.103", + "test-case-core", +] + [[package]] name = "test-log" version = "0.2.11" @@ -2761,7 +3007,7 @@ dependencies = [ [[package]] name = "tests" -version = "1.1.1" +version = "2.0.2" dependencies = [ "assert_matches", "cairo-felt", @@ -2783,7 +3029,7 @@ dependencies = [ "cairo-lang-test-utils", "cairo-lang-utils", "env_logger", - "itertools", + "itertools 0.11.0", "log", "num-bigint", "once_cell", @@ -2811,7 +3057,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.3", + "syn 2.0.26", ] [[package]] @@ -2943,11 +3189,36 @@ dependencies = [ [[package]] name = "toml" -version = "0.4.10" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ + "indexmap 2.0.0", "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] @@ -2972,9 +3243,9 @@ checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] name = "tower-lsp" -version = "0.17.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43e094780b4447366c59f79acfd65b1375ecaa84e61dddbde1421aa506334024" +checksum = "9b38fb0e6ce037835174256518aace3ca621c4f96383c56bb846cfc11b341910" dependencies = [ "async-trait", "auto_impl", @@ -2982,7 +3253,6 @@ dependencies = [ "dashmap", "futures", "httparse", - "log", "lsp-types", "memchr", "serde", @@ -2991,13 +3261,14 @@ dependencies = [ "tokio-util", "tower", "tower-lsp-macros", + "tracing", ] [[package]] name = "tower-lsp-macros" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebd99eec668d0a450c177acbc4d05e0d0d13b1f8d3db13cd706c52cbec4ac04" +checksum = "34723c06344244474fdde365b76aebef8050bf6be61a935b91ee9ff7c4e91157" dependencies = [ "proc-macro2", "quote", @@ -3018,9 +3289,21 @@ checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + [[package]] name = "tracing-core" version = "0.1.30" @@ -3036,12 +3319,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "ucd-trie" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" - [[package]] name = "unescaper" version = "0.1.1" @@ -3285,6 +3562,15 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "winnow" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" +dependencies = [ + "memchr", +] + [[package]] name = "wyz" version = "0.5.1" @@ -3317,9 +3603,9 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zeroize" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" dependencies = [ "zeroize_derive", ] diff --git a/Cargo.toml b/Cargo.toml index 3927b6991..753694083 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,6 @@ +[profile.release] +overflow-checks = true # Enable integer overflow checks. + [workspace] members = [ @@ -22,16 +25,26 @@ members = [ "cairo/crates/cairo-lang-sierra-gas", "cairo/crates/cairo-lang-sierra-generator", "cairo/crates/cairo-lang-sierra-to-casm", + "cairo/crates/cairo-lang-sierra-type-size", "cairo/crates/cairo-lang-starknet", "cairo/crates/cairo-lang-syntax", "cairo/crates/cairo-lang-syntax-codegen", "cairo/crates/cairo-lang-test-runner", "cairo/crates/cairo-lang-utils", + "cairo/crates/bin/cairo-language-server", + "cairo/crates/bin/cairo-compile", + "cairo/crates/bin/cairo-format", + "cairo/crates/bin/cairo-test", + "cairo/crates/bin/cairo-run", + "cairo/crates/bin/sierra-compile", + "cairo/crates/bin/starknet-compile", + "cairo/crates/bin/starknet-sierra-compile", + "cairo/crates/bin/generate-syntax", "cairo/tests", ] [workspace.package] -version = "1.1.1" +version = "2.0.2" edition = "2021" repository = "https://github.com/starkware-libs/cairo/" license = "Apache-2.0" @@ -40,53 +53,63 @@ license-file = "LICENSE" [workspace.dependencies] anyhow = "1.0.66" ark-ff = "0.4.0-alpha.7" -ark-std = "0.3.0" +ark-secp256k1 = "0.4.0" +ark-secp256r1 = "0.4.0" +ark-std = "0.4.0" assert_matches = "1.5" bimap = "0.6.2" -cairo-felt = "0.3.0-rc1" -cairo-vm = "0.3.0-rc1" +cairo-felt = "0.8.2" +cairo-vm = "0.8.2" clap = { version = "4.0", features = ["derive"] } colored = "2" const-fnv1a-hash = "1.1.0" convert_case = "0.6.0" derivative = "2.2.0" diffy = "0.3.0" -env_logger = "0.9.3" +env_logger = "0.10.0" genco = "0.17.0" good_lp = { version = "1.3.2", features = ["minilp"], default-features = false } id-arena = "2.2.1" ignore = "0.4.20" -indexmap = { version = "1.9.1", features = ["serde"] } +indent = "0.1.1" +indexmap = { version = "2.0.0", features = ["serde"] } indoc = "2.0.1" -itertools = "0.10.3" +itertools = "0.11.0" keccak = "0.1.3" -lalrpop-util = { version = "0.19.9", features = ["lexer"] } +lalrpop-util = { version = "0.20.0", features = ["lexer"] } log = "0.4" -lsp = { version = "0.93", package = "lsp-types" } +lsp = { version = "0.94", package = "lsp-types" } num-bigint = "0.4" num-integer = "0.1" num-traits = "0.2" once_cell = "1.17.1" -path-clean = "0.1.0" +parity-scale-codec = "3.5.0" +parity-scale-codec-derive = "3.1.4" +path-clean = "1.0.1" pretty_assertions = "1.2.1" proc-macro2 = "1.0" quote = "1.0.21" rayon = "1.7.0" -rstest = "0.16.0" +rstest = "0.18.1" salsa = "0.16.1" -scarb-metadata = "1.0.1" +scarb-metadata = "1.4.2" +schemars = { version = "0.8.12", features = ["preserve_order"] } serde = { version = "1.0.130", features = ["derive"] } serde_json = "1.0" sha3 = "0.10.6" smol_str = { version = "0.2.0", features = ["serde"] } -syn = { version = "1.0.99", features = ["full", "extra-traits"] } -test-case = "2.2.2" +syn = { version = "2.0.25", features = ["full", "extra-traits"] } +test-case = "3.1.0" test-case-macros = "2.2.2" test-log = "0.2.11" thiserror = "1.0.32" -time = { version = "0.3.20", features = ["formatting", "macros", "local-offset"] } +time = { version = "0.3.20", features = [ + "formatting", + "macros", + "local-offset", +] } tokio = { version = "1.18.2", features = ["full", "sync"] } -toml = "0.4.2" -tower-lsp = "0.17.0" +toml = "0.7.6" +tower-lsp = "0.19.0" unescaper = "0.1.1" xshell = "0.2.2" diff --git a/cairo b/cairo index c6b003cc4..06722bd86 160000 --- a/cairo +++ b/cairo @@ -1 +1 @@ -Subproject commit c6b003cc425907ac5fc846375a48737d5125f5b5 +Subproject commit 06722bd868b675500f61e87125cc5db519592e63 diff --git a/src/openzeppelin/introspection.cairo b/src/openzeppelin/introspection.cairo index 0819e4257..184f921e7 100644 --- a/src/openzeppelin/introspection.cairo +++ b/src/openzeppelin/introspection.cairo @@ -1,2 +1,3 @@ mod src5; mod dual_src5; +mod interface; diff --git a/src/openzeppelin/introspection/dual_src5.cairo b/src/openzeppelin/introspection/dual_src5.cairo index f693aac45..ca6868cae 100644 --- a/src/openzeppelin/introspection/dual_src5.cairo +++ b/src/openzeppelin/introspection/dual_src5.cairo @@ -2,7 +2,6 @@ use array::ArrayTrait; use starknet::ContractAddress; use openzeppelin::utils::selectors; -use openzeppelin::utils::serde::SerializedAppend; use openzeppelin::utils::try_selector_with_fallback; use openzeppelin::utils::UnwrapAndCast; @@ -17,8 +16,7 @@ trait DualCaseSRC5Trait { impl DualCaseSRC5Impl of DualCaseSRC5Trait { fn supports_interface(self: @DualCaseSRC5, interface_id: felt252) -> bool { - let mut args = ArrayTrait::new(); - args.append_serde(interface_id); + let mut args = array![interface_id]; try_selector_with_fallback( *self.contract_address, diff --git a/src/openzeppelin/introspection/interface.cairo b/src/openzeppelin/introspection/interface.cairo new file mode 100644 index 000000000..68909245c --- /dev/null +++ b/src/openzeppelin/introspection/interface.cairo @@ -0,0 +1,11 @@ +const ISRC5_ID: felt252 = 0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055; + +#[starknet::interface] +trait ISRC5 { + fn supports_interface(self: @TState, interface_id: felt252) -> bool; +} + +#[starknet::interface] +trait ISRC5Camel { + fn supportsInterface(self: @TState, interfaceId: felt252) -> bool; +} diff --git a/src/openzeppelin/introspection/src5.cairo b/src/openzeppelin/introspection/src5.cairo index 2b0384098..2f9a9389e 100644 --- a/src/openzeppelin/introspection/src5.cairo +++ b/src/openzeppelin/introspection/src5.cairo @@ -1,56 +1,38 @@ -const ISRC5_ID: felt252 = 0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055; - -#[abi] -trait ISRC5 { - fn supports_interface(interface_id: felt252) -> bool; -} - -#[abi] -trait ISRC5Camel { - fn supportsInterface(interfaceId: felt252) -> bool; -} - -#[contract] +#[starknet::contract] mod SRC5 { - use openzeppelin::introspection::src5; + use openzeppelin::introspection::interface; + #[storage] struct Storage { supported_interfaces: LegacyMap } - impl SRC5Impl of src5::ISRC5 { - fn supports_interface(interface_id: felt252) -> bool { - if interface_id == src5::ISRC5_ID { + #[external(v0)] + impl SRC5Impl of interface::ISRC5 { + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { + if interface_id == interface::ISRC5_ID { return true; } - supported_interfaces::read(interface_id) + self.supported_interfaces.read(interface_id) } } - impl SRC5CamelImpl of src5::ISRC5Camel { - fn supportsInterface(interfaceId: felt252) -> bool { - SRC5Impl::supports_interface(interfaceId) + #[external(v0)] + impl SRC5CamelImpl of interface::ISRC5Camel { + fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { + SRC5Impl::supports_interface(self, interfaceId) } } - #[view] - fn supports_interface(interface_id: felt252) -> bool { - SRC5Impl::supports_interface(interface_id) - } - - #[view] - fn supportsInterface(interfaceId: felt252) -> bool { - SRC5CamelImpl::supportsInterface(interfaceId) - } - - #[internal] - fn register_interface(interface_id: felt252) { - supported_interfaces::write(interface_id, true); - } + #[generate_trait] + impl InternalImpl of InternalTrait { + fn register_interface(ref self: ContractState, interface_id: felt252) { + self.supported_interfaces.write(interface_id, true); + } - #[internal] - fn deregister_interface(interface_id: felt252) { - assert(interface_id != src5::ISRC5_ID, 'SRC5: invalid id'); - supported_interfaces::write(interface_id, false); + fn deregister_interface(ref self: ContractState, interface_id: felt252) { + assert(interface_id != interface::ISRC5_ID, 'SRC5: invalid id'); + self.supported_interfaces.write(interface_id, false); + } } } diff --git a/src/openzeppelin/lib.cairo b/src/openzeppelin/lib.cairo index 47d73c605..03d22fd58 100644 --- a/src/openzeppelin/lib.cairo +++ b/src/openzeppelin/lib.cairo @@ -1,7 +1,7 @@ -mod access; +// mod access; mod introspection; -mod security; -mod account; -mod token; +// mod security; +// mod account; +// mod token; mod tests; mod utils; diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index 0110b5abc..7f8a46378 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -1,9 +1,9 @@ -mod access; -mod test_reentrancyguard; +// mod access; +// mod test_reentrancyguard; mod introspection; -mod account; -mod token; -mod test_initializable; -mod test_pausable; +// mod account; +// mod token; +// mod test_initializable; +// mod test_pausable; mod mocks; mod utils; diff --git a/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo b/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo index 83908f95d..7a229576e 100644 --- a/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo +++ b/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo @@ -42,11 +42,8 @@ fn setup_snake() -> (DualCaseAccessControl, IAccessControlDispatcher) { calldata.append_serde(ADMIN()); let target = utils::deploy(SnakeAccessControlMock::TEST_CLASS_HASH, calldata); ( - DualCaseAccessControl { - contract_address: target - }, IAccessControlDispatcher { - contract_address: target - } + DualCaseAccessControl { contract_address: target }, + IAccessControlDispatcher { contract_address: target } ) } @@ -55,11 +52,8 @@ fn setup_camel() -> (DualCaseAccessControl, IAccessControlCamelDispatcher) { calldata.append_serde(ADMIN()); let target = utils::deploy(CamelAccessControlMock::TEST_CLASS_HASH, calldata); ( - DualCaseAccessControl { - contract_address: target - }, IAccessControlCamelDispatcher { - contract_address: target - } + DualCaseAccessControl { contract_address: target }, + IAccessControlCamelDispatcher { contract_address: target } ) } @@ -77,11 +71,8 @@ fn setup_accesscontrol_panic() -> (DualCaseAccessControl, DualCaseAccessControl) CamelAccessControlPanicMock::TEST_CLASS_HASH, ArrayTrait::new() ); ( - DualCaseAccessControl { - contract_address: snake_target - }, DualCaseAccessControl { - contract_address: camel_target - } + DualCaseAccessControl { contract_address: snake_target }, + DualCaseAccessControl { contract_address: camel_target } ) } diff --git a/src/openzeppelin/tests/access/test_dual_ownable.cairo b/src/openzeppelin/tests/access/test_dual_ownable.cairo index 87841fe8d..aa86d3874 100644 --- a/src/openzeppelin/tests/access/test_dual_ownable.cairo +++ b/src/openzeppelin/tests/access/test_dual_ownable.cairo @@ -47,11 +47,8 @@ fn setup_camel() -> (DualCaseOwnable, IOwnableCamelDispatcher) { calldata.append_serde(OWNER()); let target = utils::deploy(CamelOwnableMock::TEST_CLASS_HASH, calldata); ( - DualCaseOwnable { - contract_address: target - }, IOwnableCamelDispatcher { - contract_address: target - } + DualCaseOwnable { contract_address: target }, + IOwnableCamelDispatcher { contract_address: target } ) } @@ -65,11 +62,8 @@ fn setup_ownable_panic() -> (DualCaseOwnable, DualCaseOwnable) { let snake_target = utils::deploy(SnakeOwnablePanicMock::TEST_CLASS_HASH, ArrayTrait::new()); let camel_target = utils::deploy(CamelOwnablePanicMock::TEST_CLASS_HASH, ArrayTrait::new()); ( - DualCaseOwnable { - contract_address: snake_target - }, DualCaseOwnable { - contract_address: camel_target - } + DualCaseOwnable { contract_address: snake_target }, + DualCaseOwnable { contract_address: camel_target } ) } diff --git a/src/openzeppelin/tests/account/test_dual_account.cairo b/src/openzeppelin/tests/account/test_dual_account.cairo index 946119a6c..1d4e23840 100644 --- a/src/openzeppelin/tests/account/test_dual_account.cairo +++ b/src/openzeppelin/tests/account/test_dual_account.cairo @@ -33,11 +33,8 @@ fn setup_snake() -> (DualCaseAccount, AccountABIDispatcher) { calldata.append_serde(PUBLIC_KEY); let target = utils::deploy(SnakeAccountMock::TEST_CLASS_HASH, calldata); ( - DualCaseAccount { - contract_address: target - }, AccountABIDispatcher { - contract_address: target - } + DualCaseAccount { contract_address: target }, + AccountABIDispatcher { contract_address: target } ) } @@ -46,11 +43,8 @@ fn setup_camel() -> (DualCaseAccount, AccountABICamelDispatcher) { calldata.append_serde(PUBLIC_KEY); let target = utils::deploy(CamelAccountMock::TEST_CLASS_HASH, calldata); ( - DualCaseAccount { - contract_address: target - }, AccountABICamelDispatcher { - contract_address: target - } + DualCaseAccount { contract_address: target }, + AccountABICamelDispatcher { contract_address: target } ) } @@ -64,11 +58,8 @@ fn setup_account_panic() -> (DualCaseAccount, DualCaseAccount) { let snake_target = utils::deploy(SnakeAccountPanicMock::TEST_CLASS_HASH, ArrayTrait::new()); let camel_target = utils::deploy(CamelAccountPanicMock::TEST_CLASS_HASH, ArrayTrait::new()); ( - DualCaseAccount { - contract_address: snake_target - }, DualCaseAccount { - contract_address: camel_target - } + DualCaseAccount { contract_address: snake_target }, + DualCaseAccount { contract_address: camel_target } ) } diff --git a/src/openzeppelin/tests/introspection/test_dual_src5.cairo b/src/openzeppelin/tests/introspection/test_dual_src5.cairo index 5693b941c..4e47773cc 100644 --- a/src/openzeppelin/tests/introspection/test_dual_src5.cairo +++ b/src/openzeppelin/tests/introspection/test_dual_src5.cairo @@ -1,9 +1,9 @@ use array::ArrayTrait; -use openzeppelin::introspection::src5::ISRC5_ID; -use openzeppelin::introspection::src5::ISRC5Dispatcher; -use openzeppelin::introspection::src5::ISRC5DispatcherTrait; -use openzeppelin::introspection::src5::ISRC5CamelDispatcher; -use openzeppelin::introspection::src5::ISRC5CamelDispatcherTrait; +use openzeppelin::introspection::interface::ISRC5_ID; +use openzeppelin::introspection::interface::ISRC5Dispatcher; +use openzeppelin::introspection::interface::ISRC5DispatcherTrait; +use openzeppelin::introspection::interface::ISRC5CamelDispatcher; +use openzeppelin::introspection::interface::ISRC5CamelDispatcherTrait; use openzeppelin::introspection::dual_src5::DualCaseSRC5; use openzeppelin::introspection::dual_src5::DualCaseSRC5Trait; use openzeppelin::tests::mocks::src5_mocks::SnakeSRC5Mock; @@ -18,32 +18,29 @@ use openzeppelin::tests::utils; // fn setup_snake() -> DualCaseSRC5 { - let mut calldata = ArrayTrait::new(); + let mut calldata = array![]; let target = utils::deploy(SnakeSRC5Mock::TEST_CLASS_HASH, calldata); DualCaseSRC5 { contract_address: target } } fn setup_camel() -> DualCaseSRC5 { - let mut calldata = ArrayTrait::new(); + let mut calldata = array![]; let target = utils::deploy(CamelSRC5Mock::TEST_CLASS_HASH, calldata); DualCaseSRC5 { contract_address: target } } fn setup_non_src5() -> DualCaseSRC5 { - let calldata = ArrayTrait::new(); + let calldata = array![]; let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); DualCaseSRC5 { contract_address: target } } fn setup_src5_panic() -> (DualCaseSRC5, DualCaseSRC5) { - let snake_target = utils::deploy(SnakeSRC5PanicMock::TEST_CLASS_HASH, ArrayTrait::new()); - let camel_target = utils::deploy(CamelSRC5PanicMock::TEST_CLASS_HASH, ArrayTrait::new()); + let snake_target = utils::deploy(SnakeSRC5PanicMock::TEST_CLASS_HASH, array![]); + let camel_target = utils::deploy(CamelSRC5PanicMock::TEST_CLASS_HASH, array![]); ( - DualCaseSRC5 { - contract_address: snake_target - }, DualCaseSRC5 { - contract_address: camel_target - } + DualCaseSRC5 { contract_address: snake_target }, + DualCaseSRC5 { contract_address: camel_target } ) } diff --git a/src/openzeppelin/tests/introspection/test_src5.cairo b/src/openzeppelin/tests/introspection/test_src5.cairo index 9b43bf894..7b0ea5418 100644 --- a/src/openzeppelin/tests/introspection/test_src5.cairo +++ b/src/openzeppelin/tests/introspection/test_src5.cairo @@ -1,36 +1,44 @@ use openzeppelin::introspection::src5::SRC5; -use openzeppelin::introspection::src5::ISRC5_ID; +use openzeppelin::introspection::src5::SRC5::SRC5Impl; +use openzeppelin::introspection::src5::SRC5::InternalImpl; +use openzeppelin::introspection::interface::ISRC5_ID; const OTHER_ID: felt252 = 0x12345678; +fn STATE() -> SRC5::ContractState { + SRC5::contract_state_for_testing() +} + #[test] #[available_gas(2000000)] fn test_default_behavior() { - let supports_default_interface: bool = SRC5::supports_interface(ISRC5_ID); + let supports_default_interface = SRC5Impl::supports_interface(@STATE(), ISRC5_ID); assert(supports_default_interface, 'Should support base interface'); } #[test] #[available_gas(2000000)] fn test_not_registered_interface() { - let supports_unregistered_interface: bool = SRC5::supports_interface(OTHER_ID); + let supports_unregistered_interface = SRC5Impl::supports_interface(@STATE(), OTHER_ID); assert(!supports_unregistered_interface, 'Should not support unregistered'); } #[test] #[available_gas(2000000)] fn test_register_interface() { - SRC5::register_interface(OTHER_ID); - let supports_new_interface: bool = SRC5::supports_interface(OTHER_ID); + let mut state = STATE(); + InternalImpl::register_interface(ref state, OTHER_ID); + let supports_new_interface = SRC5Impl::supports_interface(@state, OTHER_ID); assert(supports_new_interface, 'Should support new interface'); } #[test] #[available_gas(2000000)] fn test_deregister_interface() { - SRC5::register_interface(OTHER_ID); - SRC5::deregister_interface(OTHER_ID); - let supports_old_interface: bool = SRC5::supports_interface(OTHER_ID); + let mut state = STATE(); + InternalImpl::register_interface(ref state, OTHER_ID); + InternalImpl::deregister_interface(ref state, OTHER_ID); + let supports_old_interface = SRC5Impl::supports_interface(@state, OTHER_ID); assert(!supports_old_interface, 'Should not support interface'); } @@ -38,5 +46,6 @@ fn test_deregister_interface() { #[available_gas(2000000)] #[should_panic(expected: ('SRC5: invalid id', ))] fn test_deregister_default_interface() { - SRC5::deregister_interface(ISRC5_ID); + let mut state = STATE(); + InternalImpl::deregister_interface(ref state, ISRC5_ID); } diff --git a/src/openzeppelin/tests/mocks.cairo b/src/openzeppelin/tests/mocks.cairo index 7ed138560..d8bdaa92b 100644 --- a/src/openzeppelin/tests/mocks.cairo +++ b/src/openzeppelin/tests/mocks.cairo @@ -1,21 +1,21 @@ -mod reentrancy_attacker_mock; -mod reentrancy_mock; -mod erc721_receiver; -mod erc721_panic_mock; -mod mock_pausable; -mod camel20_mock; -mod snake20_mock; -mod erc20_panic; -mod non721_mock; -mod accesscontrol_panic_mock; -mod account_panic_mock; -mod camel_accesscontrol_mock; -mod camel_account_mock; -mod snake_accesscontrol_mock; -mod snake_account_mock; -mod dual_ownable_mocks; -mod snake721_mock; -mod camel721_mock; +// mod reentrancy_attacker_mock; +// mod reentrancy_mock; +// mod erc721_receiver; +// mod erc721_panic_mock; +// mod mock_pausable; +// mod camel20_mock; +// mod snake20_mock; +// mod erc20_panic; +// mod non721_mock; +// mod accesscontrol_panic_mock; +// mod account_panic_mock; +// mod camel_accesscontrol_mock; +// mod camel_account_mock; +// mod snake_accesscontrol_mock; +// mod snake_account_mock; +// mod dual_ownable_mocks; +// mod snake721_mock; +// mod camel721_mock; mod non_implementing_mock; -mod dual721_receiver_mocks; +// mod dual721_receiver_mocks; mod src5_mocks; diff --git a/src/openzeppelin/tests/mocks/non_implementing_mock.cairo b/src/openzeppelin/tests/mocks/non_implementing_mock.cairo index d614d1da7..ffa12f857 100644 --- a/src/openzeppelin/tests/mocks/non_implementing_mock.cairo +++ b/src/openzeppelin/tests/mocks/non_implementing_mock.cairo @@ -1,7 +1,10 @@ -#[contract] +#[starknet::contract] mod NonImplementingMock { - #[view] - fn nope() -> bool { + #[storage] + struct Storage {} + + #[external(v0)] + fn nope(self: @ContractState) -> bool { false } } diff --git a/src/openzeppelin/tests/mocks/src5_mocks.cairo b/src/openzeppelin/tests/mocks/src5_mocks.cairo index 28ccf2b6b..cebdd0910 100644 --- a/src/openzeppelin/tests/mocks/src5_mocks.cairo +++ b/src/openzeppelin/tests/mocks/src5_mocks.cairo @@ -1,34 +1,48 @@ use openzeppelin::introspection::src5::SRC5; -#[contract] +#[starknet::contract] mod SnakeSRC5Mock { - #[view] - fn supports_interface(interface_id: felt252) -> bool { - super::SRC5::supports_interface(interface_id) + #[storage] + struct Storage {} + + #[external(v0)] + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { + let unsafe_state = super::SRC5::unsafe_new_contract_state(); + super::SRC5::SRC5Impl::supports_interface(@unsafe_state, interface_id) } } -#[contract] +#[starknet::contract] mod CamelSRC5Mock { - #[view] - fn supportsInterface(interfaceId: felt252) -> bool { - super::SRC5::supportsInterface(interfaceId) + #[storage] + struct Storage {} + + #[external(v0)] + fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { + let unsafe_state = super::SRC5::unsafe_new_contract_state(); + super::SRC5::SRC5CamelImpl::supportsInterface(@unsafe_state, interfaceId) } } -#[contract] +#[starknet::contract] mod SnakeSRC5PanicMock { - #[view] - fn supports_interface(interface_id: felt252) -> bool { + #[storage] + struct Storage {} + + #[external(v0)] + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { panic_with_felt252('Some error'); false } } -#[contract] +#[starknet::contract] mod CamelSRC5PanicMock { - #[view] - fn supportsInterface(interfaceId: felt252) -> bool { + #[storage] + struct Storage {} + + #[external(v0)] + fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { panic_with_felt252('Some error'); false } diff --git a/src/openzeppelin/tests/token/test_dual721.cairo b/src/openzeppelin/tests/token/test_dual721.cairo index 4adab5d29..b0e45e568 100644 --- a/src/openzeppelin/tests/token/test_dual721.cairo +++ b/src/openzeppelin/tests/token/test_dual721.cairo @@ -76,11 +76,8 @@ fn setup_camel() -> (DualCaseERC721, IERC721CamelDispatcher) { set_caller_address(OWNER()); let target = utils::deploy(CamelERC721Mock::TEST_CLASS_HASH, calldata); ( - DualCaseERC721 { - contract_address: target - }, IERC721CamelDispatcher { - contract_address: target - } + DualCaseERC721 { contract_address: target }, + IERC721CamelDispatcher { contract_address: target } ) } @@ -94,11 +91,8 @@ fn setup_erc721_panic() -> (DualCaseERC721, DualCaseERC721) { let snake_target = utils::deploy(SnakeERC721PanicMock::TEST_CLASS_HASH, ArrayTrait::new()); let camel_target = utils::deploy(CamelERC721PanicMock::TEST_CLASS_HASH, ArrayTrait::new()); ( - DualCaseERC721 { - contract_address: snake_target - }, DualCaseERC721 { - contract_address: camel_target - } + DualCaseERC721 { contract_address: snake_target }, + DualCaseERC721 { contract_address: camel_target } ) } diff --git a/src/openzeppelin/tests/token/test_dual721_receiver.cairo b/src/openzeppelin/tests/token/test_dual721_receiver.cairo index ef02380a9..a3197dddc 100644 --- a/src/openzeppelin/tests/token/test_dual721_receiver.cairo +++ b/src/openzeppelin/tests/token/test_dual721_receiver.cairo @@ -49,11 +49,8 @@ fn setup_snake() -> (DualCaseERC721Receiver, IERC721ReceiverDispatcher) { let mut calldata = ArrayTrait::new(); let target = utils::deploy(SnakeERC721ReceiverMock::TEST_CLASS_HASH, calldata); ( - DualCaseERC721Receiver { - contract_address: target - }, IERC721ReceiverDispatcher { - contract_address: target - } + DualCaseERC721Receiver { contract_address: target }, + IERC721ReceiverDispatcher { contract_address: target } ) } @@ -61,11 +58,8 @@ fn setup_camel() -> (DualCaseERC721Receiver, IERC721ReceiverCamelDispatcher) { let mut calldata = ArrayTrait::new(); let target = utils::deploy(CamelERC721ReceiverMock::TEST_CLASS_HASH, calldata); ( - DualCaseERC721Receiver { - contract_address: target - }, IERC721ReceiverCamelDispatcher { - contract_address: target - } + DualCaseERC721Receiver { contract_address: target }, + IERC721ReceiverCamelDispatcher { contract_address: target } ) } @@ -83,11 +77,8 @@ fn setup_erc721_receiver_panic() -> (DualCaseERC721Receiver, DualCaseERC721Recei CamelERC721ReceiverPanicMock::TEST_CLASS_HASH, ArrayTrait::new() ); ( - DualCaseERC721Receiver { - contract_address: snake_target - }, DualCaseERC721Receiver { - contract_address: camel_target - } + DualCaseERC721Receiver { contract_address: snake_target }, + DualCaseERC721Receiver { contract_address: camel_target } ) } diff --git a/src/openzeppelin/token/erc721/erc721.cairo b/src/openzeppelin/token/erc721/erc721.cairo index 3cda6ff3f..339c63ebd 100644 --- a/src/openzeppelin/token/erc721/erc721.cairo +++ b/src/openzeppelin/token/erc721/erc721.cairo @@ -454,12 +454,9 @@ mod ERC721 { fn _check_on_erc721_received( from: ContractAddress, to: ContractAddress, token_id: u256, data: Span ) -> bool { - if (DualCaseSRC5 { - contract_address: to - }.supports_interface(erc721::interface::IERC721_RECEIVER_ID)) { - DualCaseERC721Receiver { - contract_address: to - } + if (DualCaseSRC5 { contract_address: to } + .supports_interface(erc721::interface::IERC721_RECEIVER_ID)) { + DualCaseERC721Receiver { contract_address: to } .on_erc721_received( get_caller_address(), from, token_id, data ) == erc721::interface::IERC721_RECEIVER_ID diff --git a/src/openzeppelin/utils/serde.cairo b/src/openzeppelin/utils/serde.cairo index aca966485..45b33c8e4 100644 --- a/src/openzeppelin/utils/serde.cairo +++ b/src/openzeppelin/utils/serde.cairo @@ -1,22 +1,6 @@ use array::ArrayTrait; use array::SpanTrait; use serde::Serde; -use serde::serialize_array_helper; -use serde::deserialize_array_helper; - -impl SpanSerde< - T, impl TSerde: Serde, impl TCopy: Copy, impl TDrop: Drop -> of Serde> { - fn serialize(self: @Span, ref output: Array) { - (*self).len().serialize(ref output); - serialize_array_helper(*self, ref output); - } - fn deserialize(ref serialized: Span) -> Option> { - let length = *serialized.pop_front()?; - let mut arr = ArrayTrait::new(); - Option::Some(deserialize_array_helper(ref serialized, arr, length)?.span()) - } -} trait SerializedAppend { fn append_serde(ref self: Array, value: T); From c4590b98e8d3cf529cb708b519d75b875f8289df Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Tue, 18 Jul 2023 16:34:52 -0400 Subject: [PATCH 066/246] Migrate initializable to cairo2 (#661) * bump to cairo v2.0.2 * update Cargo * comment out mods * update initializable syntax * move security tests * update initializable tests * fix formatting * update cairo * update Cargo * simplify and clarify tests * fix formatting * Apply suggestions from code review Co-authored-by: Eric Nordelo * fix tests --------- Co-authored-by: Eric Nordelo --- src/openzeppelin/lib.cairo | 2 +- src/openzeppelin/security.cairo | 4 ++-- src/openzeppelin/security/initializable.cairo | 20 +++++++++------- src/openzeppelin/tests.cairo | 4 +--- src/openzeppelin/tests/security.cairo | 5 ++++ .../tests/security/test_initializable.cairo | 24 +++++++++++++++++++ .../tests/{ => security}/test_pausable.cairo | 0 .../{ => security}/test_reentrancyguard.cairo | 0 .../tests/test_initializable.cairo | 17 ------------- 9 files changed, 44 insertions(+), 32 deletions(-) create mode 100644 src/openzeppelin/tests/security.cairo create mode 100644 src/openzeppelin/tests/security/test_initializable.cairo rename src/openzeppelin/tests/{ => security}/test_pausable.cairo (100%) rename src/openzeppelin/tests/{ => security}/test_reentrancyguard.cairo (100%) delete mode 100644 src/openzeppelin/tests/test_initializable.cairo diff --git a/src/openzeppelin/lib.cairo b/src/openzeppelin/lib.cairo index 03d22fd58..6a2a521b4 100644 --- a/src/openzeppelin/lib.cairo +++ b/src/openzeppelin/lib.cairo @@ -1,6 +1,6 @@ // mod access; mod introspection; -// mod security; +mod security; // mod account; // mod token; mod tests; diff --git a/src/openzeppelin/security.cairo b/src/openzeppelin/security.cairo index 45a9c998a..1e38ab41a 100644 --- a/src/openzeppelin/security.cairo +++ b/src/openzeppelin/security.cairo @@ -1,3 +1,3 @@ -mod reentrancyguard; -mod pausable; +//mod reentrancyguard; +//mod pausable; mod initializable; diff --git a/src/openzeppelin/security/initializable.cairo b/src/openzeppelin/security/initializable.cairo index 8485238bb..1be5ab059 100644 --- a/src/openzeppelin/security/initializable.cairo +++ b/src/openzeppelin/security/initializable.cairo @@ -1,17 +1,19 @@ -#[contract] +#[starknet::contract] mod Initializable { + #[storage] struct Storage { initialized: bool } - #[internal] - fn is_initialized() -> bool { - initialized::read() - } + #[generate_trait] + impl InternalImpl of InternalTrait { + fn is_initialized(self: @ContractState) -> bool { + self.initialized.read() + } - #[internal] - fn initialize() { - assert(!is_initialized(), 'Initializable: is initialized'); - initialized::write(true); + fn initialize(ref self: ContractState) { + assert(!self.is_initialized(), 'Initializable: is initialized'); + self.initialized.write(true); + } } } diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index 7f8a46378..01fb135b7 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -1,9 +1,7 @@ // mod access; -// mod test_reentrancyguard; mod introspection; +mod security; // mod account; // mod token; -// mod test_initializable; -// mod test_pausable; mod mocks; mod utils; diff --git a/src/openzeppelin/tests/security.cairo b/src/openzeppelin/tests/security.cairo new file mode 100644 index 000000000..56019a336 --- /dev/null +++ b/src/openzeppelin/tests/security.cairo @@ -0,0 +1,5 @@ +mod test_initializable; +//mod test_pausable; +//mod test_reentrancyguard; + + diff --git a/src/openzeppelin/tests/security/test_initializable.cairo b/src/openzeppelin/tests/security/test_initializable.cairo new file mode 100644 index 000000000..86bf68fbc --- /dev/null +++ b/src/openzeppelin/tests/security/test_initializable.cairo @@ -0,0 +1,24 @@ +use openzeppelin::security::initializable::Initializable; +use openzeppelin::security::initializable::Initializable::InternalImpl; + +fn STATE() -> Initializable::ContractState { + Initializable::contract_state_for_testing() +} + +#[test] +#[available_gas(2000000)] +fn test_initialize() { + let mut state = STATE(); + assert(!InternalImpl::is_initialized(@state), 'Should not be initialized'); + InternalImpl::initialize(ref state); + assert(InternalImpl::is_initialized(@state), 'Should be initialized'); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Initializable: is initialized', ))] +fn test_initialize_when_initialized() { + let mut state = STATE(); + InternalImpl::initialize(ref state); + InternalImpl::initialize(ref state); +} diff --git a/src/openzeppelin/tests/test_pausable.cairo b/src/openzeppelin/tests/security/test_pausable.cairo similarity index 100% rename from src/openzeppelin/tests/test_pausable.cairo rename to src/openzeppelin/tests/security/test_pausable.cairo diff --git a/src/openzeppelin/tests/test_reentrancyguard.cairo b/src/openzeppelin/tests/security/test_reentrancyguard.cairo similarity index 100% rename from src/openzeppelin/tests/test_reentrancyguard.cairo rename to src/openzeppelin/tests/security/test_reentrancyguard.cairo diff --git a/src/openzeppelin/tests/test_initializable.cairo b/src/openzeppelin/tests/test_initializable.cairo deleted file mode 100644 index cfd7ec003..000000000 --- a/src/openzeppelin/tests/test_initializable.cairo +++ /dev/null @@ -1,17 +0,0 @@ -use openzeppelin::security::initializable::Initializable; - -#[test] -#[available_gas(2000000)] -fn test_initialize() { - assert(!Initializable::is_initialized(), 'Should not be initialized'); - Initializable::initialize(); - assert(Initializable::is_initialized(), 'Should be initialized'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('Initializable: is initialized', ))] -fn test_initialize_when_initialized() { - Initializable::initialize(); - Initializable::initialize(); -} From f18fcdbeeb0bd9b2b3ea799fb14f89fd96e92a30 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 20 Jul 2023 12:20:02 +0200 Subject: [PATCH 067/246] Migrate Account to Cairo 2 (#666) * feat: migrate files * feat: add interface.cairo * feat: apply review updates * refactor: improve readability * fix: dual account tests * fix: account tests * feat: apply review updates --- src/openzeppelin/account.cairo | 4 +- src/openzeppelin/account/account.cairo | 278 ++++++------- src/openzeppelin/account/dual_account.cairo | 15 +- src/openzeppelin/account/interface.cairo | 29 +- src/openzeppelin/lib.cairo | 4 +- src/openzeppelin/tests.cairo | 4 +- .../tests/account/test_account.cairo | 368 +++++++++--------- .../tests/account/test_dual_account.cairo | 45 +-- src/openzeppelin/tests/mocks.cairo | 6 +- .../tests/mocks/account_panic_mock.cairo | 44 ++- .../tests/mocks/camel_account_mock.cairo | 40 +- .../tests/mocks/snake_account_mock.cairo | 62 ++- 12 files changed, 425 insertions(+), 474 deletions(-) diff --git a/src/openzeppelin/account.cairo b/src/openzeppelin/account.cairo index d995b6329..47e57df8a 100644 --- a/src/openzeppelin/account.cairo +++ b/src/openzeppelin/account.cairo @@ -1,7 +1,7 @@ mod account; use account::{ - Account, AccountABIDispatcher, AccountABIDispatcherTrait, AccountABICamelDispatcher, - AccountABICamelDispatcherTrait, TRANSACTION_VERSION, QUERY_VERSION + Account, AccountCamelABIDispatcher, AccountCamelABIDispatcherTrait, AccountABIDispatcher, + AccountABIDispatcherTrait, TRANSACTION_VERSION, QUERY_VERSION }; mod dual_account; diff --git a/src/openzeppelin/account/account.cairo b/src/openzeppelin/account/account.cairo index 3c7a860ea..714f1f89f 100644 --- a/src/openzeppelin/account/account.cairo +++ b/src/openzeppelin/account/account.cairo @@ -2,96 +2,92 @@ use array::ArrayTrait; use array::SpanTrait; use option::OptionTrait; use serde::Serde; +use starknet::account::Call; use starknet::ContractAddress; -use openzeppelin::account::interface::Call; -use openzeppelin::utils::serde::SpanSerde; const TRANSACTION_VERSION: felt252 = 1; + // 2**128 + TRANSACTION_VERSION const QUERY_VERSION: felt252 = 340282366920938463463374607431768211457; -#[abi] -trait AccountABI { - #[external] - fn __execute__(calls: Array) -> Array>; - #[external] - fn __validate__(calls: Array) -> felt252; - #[external] - fn __validate_declare__(class_hash: felt252) -> felt252; - #[external] +#[starknet::interface] +trait AccountABI { + fn __execute__(self: @TState, calls: Array) -> Array>; + fn __validate__(self: @TState, calls: Array) -> felt252; + fn __validate_declare__(self: @TState, class_hash: felt252) -> felt252; fn __validate_deploy__( - class_hash: felt252, contract_address_salt: felt252, _public_key: felt252 + self: @TState, class_hash: felt252, contract_address_salt: felt252, _public_key: felt252 ) -> felt252; - #[external] - fn set_public_key(new_public_key: felt252); - #[view] - fn get_public_key() -> felt252; - #[view] - fn is_valid_signature(hash: felt252, signature: Array) -> felt252; - #[view] - fn supports_interface(interface_id: felt252) -> bool; + fn set_public_key(ref self: TState, new_public_key: felt252); + fn get_public_key(self: @TState) -> felt252; + fn is_valid_signature(self: @TState, hash: felt252, signature: Array) -> felt252; + fn supports_interface(self: @TState, interface_id: felt252) -> bool; } // Entry points case-convention is enforced by the protocol -#[abi] -trait AccountABICamel { - #[external] - fn __execute__(calls: Array) -> Array>; - #[external] - fn __validate__(calls: Array) -> felt252; - #[external] - fn __validate_declare__(classHash: felt252) -> felt252; - #[external] +#[starknet::interface] +trait AccountCamelABI { + fn __execute__(self: @TState, calls: Array) -> Array>; + fn __validate__(self: @TState, calls: Array) -> felt252; + fn __validate_declare__(self: @TState, classHash: felt252) -> felt252; fn __validate_deploy__( - classHash: felt252, contractAddressSalt: felt252, _publicKey: felt252 + self: @TState, classHash: felt252, contractAddressSalt: felt252, _publicKey: felt252 ) -> felt252; - #[external] - fn setPublicKey(newPublicKey: felt252); - #[view] - fn getPublicKey() -> felt252; - #[view] - fn isValidSignature(hash: felt252, signature: Array) -> felt252; - #[view] - fn supportsInterface(interfaceId: felt252) -> bool; + fn setPublicKey(ref self: TState, newPublicKey: felt252); + fn getPublicKey(self: @TState) -> felt252; + fn isValidSignature(self: @TState, hash: felt252, signature: Array) -> felt252; + fn supportsInterface(self: @TState, interfaceId: felt252) -> bool; +} + +trait PublicKeyTrait { + fn set_public_key(ref self: TState, new_public_key: felt252); + fn get_public_key(self: @TState) -> felt252; } -#[account_contract] +trait PublicKeyCamelTrait { + fn setPublicKey(ref self: TState, newPublicKey: felt252); + fn getPublicKey(self: @TState) -> felt252; +} + +#[starknet::contract] mod Account { use array::SpanTrait; use array::ArrayTrait; use box::BoxTrait; use ecdsa::check_ecdsa_signature; use option::OptionTrait; - use serde::ArraySerde; use starknet::get_tx_info; use starknet::get_caller_address; use starknet::get_contract_address; use zeroable::Zeroable; - use openzeppelin::account::interface::IDeclarer; - use openzeppelin::account::interface::ISRC6; - use openzeppelin::account::interface::ISRC6Camel; - use openzeppelin::account::interface::ISRC6_ID; - use openzeppelin::introspection::src5::ISRC5; + use openzeppelin::account::interface; + use openzeppelin::introspection::interface::ISRC5; + use openzeppelin::introspection::interface::ISRC5Camel; use openzeppelin::introspection::src5::SRC5; use super::Call; use super::QUERY_VERSION; - use super::SpanSerde; use super::TRANSACTION_VERSION; + #[storage] struct Storage { public_key: felt252 } #[constructor] - fn constructor(_public_key: felt252) { - initializer(_public_key); + fn constructor(ref self: ContractState, _public_key: felt252) { + self.initializer(_public_key); } - impl SRC6Impl of ISRC6 { - fn __execute__(mut calls: Array) -> Array> { + // + // External + // + + #[external(v0)] + impl SRC6Impl of interface::ISRC6 { + fn __execute__(self: @ContractState, mut calls: Array) -> Array> { // Avoid calls from other contracts // https://github.com/OpenZeppelin/cairo-contracts/issues/344 let sender = get_caller_address(); @@ -107,12 +103,14 @@ mod Account { _execute_calls(calls) } - fn __validate__(mut calls: Array) -> felt252 { - validate_transaction() + fn __validate__(self: @ContractState, mut calls: Array) -> felt252 { + self.validate_transaction() } - fn is_valid_signature(hash: felt252, signature: Array) -> felt252 { - if _is_valid_signature(hash, signature.span()) { + fn is_valid_signature( + self: @ContractState, hash: felt252, signature: Array + ) -> felt252 { + if self._is_valid_signature(hash, signature.span()) { starknet::VALIDATED } else { 0 @@ -120,111 +118,105 @@ mod Account { } } - impl SRC6CamelImpl of ISRC6Camel { - fn __execute__(mut calls: Array) -> Array> { - SRC6Impl::__execute__(calls) - } - - fn __validate__(mut calls: Array) -> felt252 { - SRC6Impl::__validate__(calls) + #[external(v0)] + impl SRC6CamelOnlyImpl of interface::ISRC6CamelOnly { + fn isValidSignature( + self: @ContractState, hash: felt252, signature: Array + ) -> felt252 { + SRC6Impl::is_valid_signature(self, hash, signature) } + } - fn isValidSignature(hash: felt252, signature: Array) -> felt252 { - SRC6Impl::is_valid_signature(hash, signature) + #[external(v0)] + impl DeclarerImpl of interface::IDeclarer { + fn __validate_declare__(self: @ContractState, class_hash: felt252) -> felt252 { + self.validate_transaction() } } - impl DeclarerImpl of IDeclarer { - fn __validate_declare__(class_hash: felt252) -> felt252 { - validate_transaction() + #[external(v0)] + impl SRC5Impl of ISRC5 { + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { + let unsafe_state = SRC5::unsafe_new_contract_state(); + SRC5::SRC5Impl::supports_interface(@unsafe_state, interface_id) } } - impl SRC5Impl of ISRC5 { - fn supports_interface(interface_id: felt252) -> bool { - SRC5::supports_interface(interface_id) + #[external(v0)] + impl SRC5CamelImpl of ISRC5Camel { + fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { + let unsafe_state = SRC5::unsafe_new_contract_state(); + SRC5::SRC5CamelImpl::supportsInterface(@unsafe_state, interfaceId) } } - // - // Externals - // + #[external(v0)] + impl PublicKeyImpl of super::PublicKeyTrait { + fn get_public_key(self: @ContractState) -> felt252 { + self.public_key.read() + } - #[external] - fn __execute__(mut calls: Array) -> Array> { - SRC6Impl::__execute__(calls) + fn set_public_key(ref self: ContractState, new_public_key: felt252) { + assert_only_self(); + self.public_key.write(new_public_key); + } } - #[external] - fn __validate__(mut calls: Array) -> felt252 { - SRC6Impl::__validate__(calls) - } + #[external(v0)] + impl PublicKeyCamelImpl of super::PublicKeyCamelTrait { + fn getPublicKey(self: @ContractState) -> felt252 { + self.public_key.read() + } - #[external] - fn __validate_declare__(class_hash: felt252) -> felt252 { - DeclarerImpl::__validate_declare__(class_hash) + fn setPublicKey(ref self: ContractState, newPublicKey: felt252) { + assert_only_self(); + self.public_key.write(newPublicKey); + } } - #[external] + #[external(v0)] fn __validate_deploy__( - class_hash: felt252, contract_address_salt: felt252, _public_key: felt252 + self: @ContractState, + class_hash: felt252, + contract_address_salt: felt252, + _public_key: felt252 ) -> felt252 { - validate_transaction() - } - - #[external] - fn set_public_key(new_public_key: felt252) { - assert_only_self(); - public_key::write(new_public_key); - } - - #[external] - fn setPublicKey(newPublicKey: felt252) { - set_public_key(newPublicKey); + self.validate_transaction() } // - // View + // Internal // - #[view] - fn get_public_key() -> felt252 { - public_key::read() - } - - #[view] - fn getPublicKey() -> felt252 { - get_public_key() - } - - #[view] - fn is_valid_signature(hash: felt252, signature: Array) -> felt252 { - SRC6Impl::is_valid_signature(hash, signature) - } - - #[view] - fn isValidSignature(hash: felt252, signature: Array) -> felt252 { - SRC6CamelImpl::isValidSignature(hash, signature) - } - - #[view] - fn supports_interface(interface_id: felt252) -> bool { - SRC5Impl::supports_interface(interface_id) - } + #[generate_trait] + impl InternalImpl of InternalTrait { + fn initializer(ref self: ContractState, _public_key: felt252) { + let mut unsafe_state = SRC5::unsafe_new_contract_state(); + SRC5::InternalImpl::register_interface(ref unsafe_state, interface::ISRC6_ID); + self.public_key.write(_public_key); + } - #[view] - fn supportsInterface(interfaceId: felt252) -> bool { - supports_interface(interfaceId) - } + fn validate_transaction(self: @ContractState) -> felt252 { + let tx_info = get_tx_info().unbox(); + let tx_hash = tx_info.transaction_hash; + let signature = tx_info.signature; + assert(self._is_valid_signature(tx_hash, signature), 'Account: invalid signature'); + starknet::VALIDATED + } - // - // Internals - // + fn _is_valid_signature( + self: @ContractState, hash: felt252, signature: Span + ) -> bool { + let valid_length = signature.len() == 2_u32; - #[internal] - fn initializer(_public_key: felt252) { - SRC5::register_interface(ISRC6_ID); - public_key::write(_public_key); + if valid_length { + check_ecdsa_signature( + hash, self.public_key.read(), *signature.at(0_u32), *signature.at(1_u32) + ) + } else { + false + } + } } #[internal] @@ -234,28 +226,6 @@ mod Account { assert(self == caller, 'Account: unauthorized'); } - #[internal] - fn validate_transaction() -> felt252 { - let tx_info = get_tx_info().unbox(); - let tx_hash = tx_info.transaction_hash; - let signature = tx_info.signature; - assert(_is_valid_signature(tx_hash, signature), 'Account: invalid signature'); - starknet::VALIDATED - } - - #[internal] - fn _is_valid_signature(hash: felt252, signature: Span) -> bool { - let valid_length = signature.len() == 2_u32; - - if valid_length { - check_ecdsa_signature( - hash, public_key::read(), *signature.at(0_u32), *signature.at(1_u32) - ) - } else { - false - } - } - #[internal] fn _execute_calls(mut calls: Array) -> Array> { let mut res = ArrayTrait::new(); diff --git a/src/openzeppelin/account/dual_account.cairo b/src/openzeppelin/account/dual_account.cairo index ad6903659..ee4e1c0b6 100644 --- a/src/openzeppelin/account/dual_account.cairo +++ b/src/openzeppelin/account/dual_account.cairo @@ -13,7 +13,7 @@ struct DualCaseAccount { contract_address: ContractAddress } -trait DualCaseAccountTrait { +trait DualCaseAccountABI { fn set_public_key(self: @DualCaseAccount, new_public_key: felt252); fn get_public_key(self: @DualCaseAccount) -> felt252; fn is_valid_signature( @@ -22,10 +22,9 @@ trait DualCaseAccountTrait { fn supports_interface(self: @DualCaseAccount, interface_id: felt252) -> bool; } -impl DualCaseAccountImpl of DualCaseAccountTrait { +impl DualCaseAccountImpl of DualCaseAccountABI { fn set_public_key(self: @DualCaseAccount, new_public_key: felt252) { - let mut args = ArrayTrait::new(); - args.append_serde(new_public_key); + let mut args = array![new_public_key]; try_selector_with_fallback( *self.contract_address, selectors::set_public_key, selectors::setPublicKey, args.span() @@ -34,7 +33,7 @@ impl DualCaseAccountImpl of DualCaseAccountTrait { } fn get_public_key(self: @DualCaseAccount) -> felt252 { - let mut args = ArrayTrait::new(); + let mut args = array![]; try_selector_with_fallback( *self.contract_address, selectors::get_public_key, selectors::getPublicKey, args.span() @@ -45,8 +44,7 @@ impl DualCaseAccountImpl of DualCaseAccountTrait { fn is_valid_signature( self: @DualCaseAccount, hash: felt252, signature: Array ) -> felt252 { - let mut args = ArrayTrait::new(); - args.append_serde(hash); + let mut args = array![hash]; args.append_serde(signature); try_selector_with_fallback( @@ -59,8 +57,7 @@ impl DualCaseAccountImpl of DualCaseAccountTrait { } fn supports_interface(self: @DualCaseAccount, interface_id: felt252) -> bool { - let mut args = ArrayTrait::new(); - args.append_serde(interface_id); + let mut args = array![interface_id]; try_selector_with_fallback( *self.contract_address, diff --git a/src/openzeppelin/account/interface.cairo b/src/openzeppelin/account/interface.cairo index af454163c..2027c0c99 100644 --- a/src/openzeppelin/account/interface.cairo +++ b/src/openzeppelin/account/interface.cairo @@ -1,28 +1,23 @@ use array::ArrayTrait; use array::SpanTrait; +use starknet::account::Call; use starknet::ContractAddress; const ISRC6_ID: felt252 = 0x2ceccef7f994940b3962a6c67e0ba4fcd37df7d131417c604f91e03caecc1cd; -#[derive(Serde, Drop)] -struct Call { - to: ContractAddress, - selector: felt252, - calldata: Array +#[starknet::interface] +trait ISRC6 { + fn __execute__(self: @TState, calls: Array) -> Array>; + fn __validate__(self: @TState, calls: Array) -> felt252; + fn is_valid_signature(self: @TState, hash: felt252, signature: Array) -> felt252; } -trait ISRC6 { - fn __execute__(calls: Array) -> Array>; - fn __validate__(calls: Array) -> felt252; - fn is_valid_signature(hash: felt252, signature: Array) -> felt252; +#[starknet::interface] +trait ISRC6CamelOnly { + fn isValidSignature(self: @TState, hash: felt252, signature: Array) -> felt252; } -trait ISRC6Camel { - fn __execute__(calls: Array) -> Array>; - fn __validate__(calls: Array) -> felt252; - fn isValidSignature(hash: felt252, signature: Array) -> felt252; -} - -trait IDeclarer { - fn __validate_declare__(class_hash: felt252) -> felt252; +#[starknet::interface] +trait IDeclarer { + fn __validate_declare__(self: @TState, class_hash: felt252) -> felt252; } diff --git a/src/openzeppelin/lib.cairo b/src/openzeppelin/lib.cairo index 6a2a521b4..5c658386e 100644 --- a/src/openzeppelin/lib.cairo +++ b/src/openzeppelin/lib.cairo @@ -1,7 +1,7 @@ // mod access; +mod account; mod introspection; mod security; -// mod account; -// mod token; mod tests; +// mod token; mod utils; diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index 01fb135b7..440fdfc2f 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -1,7 +1,7 @@ // mod access; +mod account; mod introspection; +mod mocks; mod security; -// mod account; // mod token; -mod mocks; mod utils; diff --git a/src/openzeppelin/tests/account/test_account.cairo b/src/openzeppelin/tests/account/test_account.cairo index 6ece0c5e9..48ab02685 100644 --- a/src/openzeppelin/tests/account/test_account.cairo +++ b/src/openzeppelin/tests/account/test_account.cairo @@ -2,6 +2,7 @@ use array::ArrayTrait; use core::traits::Into; use option::OptionTrait; use serde::Serde; +use starknet::account::Call; use starknet::contract_address_const; use starknet::ContractAddress; use starknet::testing; @@ -9,15 +10,14 @@ use starknet::testing; use openzeppelin::account::Account; use openzeppelin::account::AccountABIDispatcher; use openzeppelin::account::AccountABIDispatcherTrait; -use openzeppelin::account::interface::Call; use openzeppelin::account::interface::ISRC6_ID; use openzeppelin::account::QUERY_VERSION; use openzeppelin::account::TRANSACTION_VERSION; -use openzeppelin::introspection::src5::ISRC5_ID; +use openzeppelin::introspection::interface::ISRC5_ID; use openzeppelin::tests::utils; -use openzeppelin::token::erc20::ERC20; -use openzeppelin::token::erc20::interface::IERC20Dispatcher; -use openzeppelin::token::erc20::interface::IERC20DispatcherTrait; +// use openzeppelin::token::erc20::ERC20; +// use openzeppelin::token::erc20::interface::IERC20Dispatcher; +// use openzeppelin::token::erc20::interface::IERC20DispatcherTrait; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; @@ -38,6 +38,9 @@ struct SignedTransactionData { s: felt252 } +fn STATE() -> Account::ContractState { + Account::contract_state_for_testing() +} fn CLASS_HASH() -> felt252 { Account::TEST_CLASS_HASH } @@ -59,44 +62,35 @@ fn SIGNED_TX_DATA() -> SignedTransactionData { // fn setup_dispatcher(data: Option<@SignedTransactionData>) -> AccountABIDispatcher { - // Set the transaction version testing::set_version(TRANSACTION_VERSION); - // Deploy the account contract - let mut calldata = ArrayTrait::new(); - + let mut calldata = array![]; if data.is_some() { let data = data.unwrap(); - - // Set the signature and transaction hash - let mut signature = ArrayTrait::new(); - signature.append(*data.r); - signature.append(*data.s); - testing::set_signature(signature.span()); + testing::set_signature(array![*data.r, *data.s].span()); testing::set_transaction_hash(*data.transaction_hash); calldata.append(*data.public_key); } else { calldata.append(PUBLIC_KEY); } - let address = utils::deploy(CLASS_HASH(), calldata); AccountABIDispatcher { contract_address: address } } -fn deploy_erc20(recipient: ContractAddress, initial_supply: u256) -> IERC20Dispatcher { - let name = 0; - let symbol = 0; - let mut calldata = ArrayTrait::new(); +// fn deploy_erc20(recipient: ContractAddress, initial_supply: u256) -> IERC20Dispatcher { +// let name = 0; +// let symbol = 0; +// let mut calldata = array![]; - calldata.append_serde(name); - calldata.append_serde(symbol); - calldata.append_serde(initial_supply); - calldata.append_serde(recipient); +// calldata.append_serde(name); +// calldata.append_serde(symbol); +// calldata.append_serde(initial_supply); +// calldata.append_serde(recipient); - let address = utils::deploy(ERC20::TEST_CLASS_HASH, calldata); - IERC20Dispatcher { contract_address: address } -} +// let address = utils::deploy(ERC20::TEST_CLASS_HASH, calldata); +// IERC20Dispatcher { contract_address: address } +// } // // constructor @@ -105,8 +99,11 @@ fn deploy_erc20(recipient: ContractAddress, initial_supply: u256) -> IERC20Dispa #[test] #[available_gas(2000000)] fn test_constructor() { - Account::constructor(PUBLIC_KEY); - assert(Account::get_public_key() == PUBLIC_KEY, 'Should return public key'); + let mut state = STATE(); + Account::constructor(ref state, PUBLIC_KEY); + assert( + Account::PublicKeyImpl::get_public_key(@state) == PUBLIC_KEY, 'Should return public key' + ); } // @@ -116,24 +113,26 @@ fn test_constructor() { #[test] #[available_gas(2000000)] fn test_supports_interface() { - Account::constructor(PUBLIC_KEY); + let mut state = STATE(); + Account::constructor(ref state, PUBLIC_KEY); - let supports_default_interface = Account::supports_interface(ISRC5_ID); + let supports_default_interface = Account::SRC5Impl::supports_interface(@state, ISRC5_ID); assert(supports_default_interface, 'Should support base interface'); - let supports_account_interface = Account::supports_interface(ISRC6_ID); + let supports_account_interface = Account::SRC5Impl::supports_interface(@state, ISRC6_ID); assert(supports_account_interface, 'Should support account id'); } #[test] #[available_gas(2000000)] fn test_supportsInterface() { - Account::constructor(PUBLIC_KEY); + let mut state = STATE(); + Account::constructor(ref state, PUBLIC_KEY); - let supports_default_interface = Account::supportsInterface(ISRC5_ID); + let supports_default_interface = Account::SRC5CamelImpl::supportsInterface(@state, ISRC5_ID); assert(supports_default_interface, 'Should support base interface'); - let supports_account_interface = Account::supportsInterface(ISRC6_ID); + let supports_account_interface = Account::SRC5CamelImpl::supportsInterface(@state, ISRC6_ID); assert(supports_account_interface, 'Should support account id'); } @@ -144,46 +143,38 @@ fn test_supportsInterface() { #[test] #[available_gas(2000000)] fn test_is_valid_signature() { + let mut state = STATE(); let data = SIGNED_TX_DATA(); let hash = data.transaction_hash; - let mut good_signature = ArrayTrait::new(); - good_signature.append(data.r); - good_signature.append(data.s); + let mut good_signature = array![data.r, data.s]; + let mut bad_signature = array![0x987, 0x564]; - let mut bad_signature = ArrayTrait::new(); - bad_signature.append(0x987); - bad_signature.append(0x564); + Account::PublicKeyImpl::set_public_key(ref state, data.public_key); - Account::set_public_key(data.public_key); - - let is_valid = Account::is_valid_signature(hash, good_signature); + let is_valid = Account::SRC6Impl::is_valid_signature(@state, hash, good_signature); assert(is_valid == starknet::VALIDATED, 'Should accept valid signature'); - let is_valid = Account::is_valid_signature(hash, bad_signature); + let is_valid = Account::SRC6Impl::is_valid_signature(@state, hash, bad_signature); assert(is_valid == 0, 'Should reject invalid signature'); } #[test] #[available_gas(2000000)] fn test_isValidSignature() { + let mut state = STATE(); let data = SIGNED_TX_DATA(); let hash = data.transaction_hash; - let mut good_signature = ArrayTrait::new(); - good_signature.append(data.r); - good_signature.append(data.s); - - let mut bad_signature = ArrayTrait::new(); - bad_signature.append(0x987); - bad_signature.append(0x564); + let mut good_signature = array![data.r, data.s]; + let mut bad_signature = array![0x987, 0x564]; - Account::set_public_key(data.public_key); + Account::PublicKeyImpl::set_public_key(ref state, data.public_key); - let is_valid = Account::is_valid_signature(hash, good_signature); + let is_valid = Account::SRC6CamelOnlyImpl::isValidSignature(@state, hash, good_signature); assert(is_valid == starknet::VALIDATED, 'Should accept valid signature'); - let is_valid = Account::is_valid_signature(hash, bad_signature); + let is_valid = Account::SRC6CamelOnlyImpl::isValidSignature(@state, hash, bad_signature); assert(is_valid == 0, 'Should reject invalid signature'); } @@ -221,7 +212,7 @@ fn test_validate_deploy_invalid_signature_data() { #[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] fn test_validate_deploy_invalid_signature_length() { let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); - let mut signature = ArrayTrait::new(); + let mut signature = array![]; signature.append(0x1); testing::set_signature(signature.span()); @@ -234,7 +225,7 @@ fn test_validate_deploy_invalid_signature_length() { #[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] fn test_validate_deploy_empty_signature() { let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); - let empty_sig = ArrayTrait::new(); + let empty_sig = array![]; testing::set_signature(empty_sig.span()); account.__validate_deploy__(CLASS_HASH(), SALT, PUBLIC_KEY); @@ -270,7 +261,7 @@ fn test_validate_declare_invalid_signature_data() { #[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] fn test_validate_declare_invalid_signature_length() { let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); - let mut signature = ArrayTrait::new(); + let mut signature = array![]; signature.append(0x1); testing::set_signature(signature.span()); @@ -283,71 +274,71 @@ fn test_validate_declare_invalid_signature_length() { #[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] fn test_validate_declare_empty_signature() { let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); - let empty_sig = ArrayTrait::new(); + let empty_sig = array![]; testing::set_signature(empty_sig.span()); account.__validate_declare__(CLASS_HASH()); } -fn test_execute_with_version(version: Option) { - let data = SIGNED_TX_DATA(); - let account = setup_dispatcher(Option::Some(@data)); - let erc20 = deploy_erc20(account.contract_address, 1000); - let recipient = contract_address_const::<0x123>(); - - // Craft call and add to calls array - let mut calldata = ArrayTrait::new(); - let amount: u256 = 200; - calldata.append_serde(recipient); - calldata.append_serde(amount); - let call = Call { - to: erc20.contract_address, selector: selectors::transfer, calldata: calldata - }; - let mut calls = ArrayTrait::new(); - calls.append(call); - - // Handle version for test - if version.is_some() { - testing::set_version(version.unwrap()); - } - - // Execute - let ret = account.__execute__(calls); - - // Assert that the transfer was successful - assert(erc20.balance_of(account.contract_address) == 800, 'Should have remainder'); - assert(erc20.balance_of(recipient) == amount, 'Should have transferred'); - - // Test return value - let mut call_serialized_retval = *ret.at(0); - let call_retval = Serde::::deserialize(ref call_serialized_retval); - assert(call_retval.unwrap(), 'Should have succeeded'); -} - -#[test] -#[available_gas(2000000)] -fn test_execute() { - test_execute_with_version(Option::None(())); -} - -#[test] -#[available_gas(2000000)] -fn test_execute_query_version() { - test_execute_with_version(Option::Some(QUERY_VERSION)); -} - -#[test] -#[available_gas(2000000)] -#[should_panic(expected: ('Account: invalid tx version', 'ENTRYPOINT_FAILED'))] -fn test_execute_invalid_version() { - test_execute_with_version(Option::Some(TRANSACTION_VERSION - 1)); -} +// fn test_execute_with_version(version: Option) { +// let data = SIGNED_TX_DATA(); +// let account = setup_dispatcher(Option::Some(@data)); +// let erc20 = deploy_erc20(account.contract_address, 1000); +// let recipient = contract_address_const::<0x123>(); + +// // Craft call and add to calls array +// let mut calldata = array![]; +// let amount: u256 = 200; +// calldata.append_serde(recipient); +// calldata.append_serde(amount); +// let call = Call { +// to: erc20.contract_address, selector: selectors::transfer, calldata: calldata +// }; +// let mut calls = array![]; +// calls.append(call); + +// // Handle version for test +// if version.is_some() { +// testing::set_version(version.unwrap()); +// } + +// // Execute +// let ret = account.__execute__(calls); + +// // Assert that the transfer was successful +// assert(erc20.balance_of(account.contract_address) == 800, 'Should have remainder'); +// assert(erc20.balance_of(recipient) == amount, 'Should have transferred'); + +// // Test return value +// let mut call_serialized_retval = *ret.at(0); +// let call_retval = Serde::::deserialize(ref call_serialized_retval); +// assert(call_retval.unwrap(), 'Should have succeeded'); +// } + +// #[test] +// #[available_gas(2000000)] +// fn test_execute() { +// test_execute_with_version(Option::None(())); +// } + +// #[test] +// #[available_gas(2000000)] +// fn test_execute_query_version() { +// test_execute_with_version(Option::Some(QUERY_VERSION)); +// } + +// #[test] +// #[available_gas(2000000)] +// #[should_panic(expected: ('Account: invalid tx version', 'ENTRYPOINT_FAILED'))] +// fn test_execute_invalid_version() { +// test_execute_with_version(Option::Some(TRANSACTION_VERSION - 1)); +// } #[test] #[available_gas(2000000)] fn test_validate() { - let calls = ArrayTrait::new(); + let calls = array![]; let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); assert(account.__validate__(calls) == starknet::VALIDATED, 'Should validate correctly'); @@ -357,7 +348,7 @@ fn test_validate() { #[available_gas(2000000)] #[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] fn test_validate_invalid() { - let calls = ArrayTrait::new(); + let calls = array![]; let mut data = SIGNED_TX_DATA(); data.transaction_hash += 1; let account = setup_dispatcher(Option::Some(@data)); @@ -365,57 +356,57 @@ fn test_validate_invalid() { account.__validate__(calls); } -#[test] -#[available_gas(2000000)] -fn test_multicall() { - let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); - let erc20 = deploy_erc20(account.contract_address, 1000); - let recipient1 = contract_address_const::<0x123>(); - let recipient2 = contract_address_const::<0x456>(); - let mut calls = ArrayTrait::new(); - - // Craft call1 - let mut calldata1 = ArrayTrait::new(); - let amount1: u256 = 300; - calldata1.append_serde(recipient1); - calldata1.append_serde(amount1); - let call1 = Call { - to: erc20.contract_address, selector: selectors::transfer, calldata: calldata1 - }; - - // Craft call2 - let mut calldata2 = ArrayTrait::new(); - let amount2: u256 = 500; - calldata2.append_serde(recipient2); - calldata2.append_serde(amount2); - let call2 = Call { - to: erc20.contract_address, selector: selectors::transfer, calldata: calldata2 - }; - - // Bundle calls and exeute - calls.append(call1); - calls.append(call2); - let ret = account.__execute__(calls); - - // Assert that the transfers were successful - assert(erc20.balance_of(account.contract_address) == 200, 'Should have remainder'); - assert(erc20.balance_of(recipient1) == 300, 'Should have transferred'); - assert(erc20.balance_of(recipient2) == 500, 'Should have transferred'); - - // Test return value - let mut call1_serialized_retval = *ret.at(0); - let mut call2_serialized_retval = *ret.at(1); - let call1_retval = Serde::::deserialize(ref call1_serialized_retval); - let call2_retval = Serde::::deserialize(ref call2_serialized_retval); - assert(call1_retval.unwrap(), 'Should have succeeded'); - assert(call2_retval.unwrap(), 'Should have succeeded'); -} +// #[test] +// #[available_gas(2000000)] +// fn test_multicall() { +// let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); +// let erc20 = deploy_erc20(account.contract_address, 1000); +// let recipient1 = contract_address_const::<0x123>(); +// let recipient2 = contract_address_const::<0x456>(); +// let mut calls = array![]; + +// // Craft call1 +// let mut calldata1 = array![]; +// let amount1: u256 = 300; +// calldata1.append_serde(recipient1); +// calldata1.append_serde(amount1); +// let call1 = Call { +// to: erc20.contract_address, selector: selectors::transfer, calldata: calldata1 +// }; + +// // Craft call2 +// let mut calldata2 = array![]; +// let amount2: u256 = 500; +// calldata2.append_serde(recipient2); +// calldata2.append_serde(amount2); +// let call2 = Call { +// to: erc20.contract_address, selector: selectors::transfer, calldata: calldata2 +// }; + +// // Bundle calls and exeute +// calls.append(call1); +// calls.append(call2); +// let ret = account.__execute__(calls); + +// // Assert that the transfers were successful +// assert(erc20.balance_of(account.contract_address) == 200, 'Should have remainder'); +// assert(erc20.balance_of(recipient1) == 300, 'Should have transferred'); +// assert(erc20.balance_of(recipient2) == 500, 'Should have transferred'); + +// // Test return value +// let mut call1_serialized_retval = *ret.at(0); +// let mut call2_serialized_retval = *ret.at(1); +// let call1_retval = Serde::::deserialize(ref call1_serialized_retval); +// let call2_retval = Serde::::deserialize(ref call2_serialized_retval); +// assert(call1_retval.unwrap(), 'Should have succeeded'); +// assert(call2_retval.unwrap(), 'Should have succeeded'); +// } #[test] #[available_gas(2000000)] fn test_multicall_zero_calls() { let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); - let mut calls = ArrayTrait::new(); + let mut calls = array![]; let ret = account.__execute__(calls); @@ -427,11 +418,13 @@ fn test_multicall_zero_calls() { #[available_gas(2000000)] #[should_panic(expected: ('Account: invalid caller', ))] fn test_account_called_from_contract() { - let calls = ArrayTrait::new(); + let calls = array![]; let caller = contract_address_const::<0x123>(); + testing::set_contract_address(ACCOUNT_ADDRESS()); testing::set_caller_address(caller); - Account::__execute__(calls); + + Account::SRC6Impl::__execute__(@STATE(), calls); } // @@ -441,11 +434,13 @@ fn test_account_called_from_contract() { #[test] #[available_gas(2000000)] fn test_public_key_setter_and_getter() { + let mut state = STATE(); testing::set_contract_address(ACCOUNT_ADDRESS()); testing::set_caller_address(ACCOUNT_ADDRESS()); - Account::set_public_key(NEW_PUBKEY); - let public_key = Account::get_public_key(); + Account::PublicKeyImpl::set_public_key(ref state, NEW_PUBKEY); + + let public_key = Account::PublicKeyImpl::get_public_key(@state); assert(public_key == NEW_PUBKEY, 'Should update key'); } @@ -453,10 +448,12 @@ fn test_public_key_setter_and_getter() { #[available_gas(2000000)] #[should_panic(expected: ('Account: unauthorized', ))] fn test_public_key_setter_different_account() { + let mut state = STATE(); let caller = contract_address_const::<0x123>(); testing::set_contract_address(ACCOUNT_ADDRESS()); testing::set_caller_address(caller); - Account::set_public_key(NEW_PUBKEY); + + Account::PublicKeyImpl::set_public_key(ref state, NEW_PUBKEY); } // @@ -466,11 +463,13 @@ fn test_public_key_setter_different_account() { #[test] #[available_gas(2000000)] fn test_public_key_setter_and_getter_camel() { + let mut state = STATE(); testing::set_contract_address(ACCOUNT_ADDRESS()); testing::set_caller_address(ACCOUNT_ADDRESS()); - Account::setPublicKey(NEW_PUBKEY); - let public_key = Account::getPublicKey(); + Account::PublicKeyCamelImpl::setPublicKey(ref state, NEW_PUBKEY); + + let public_key = Account::PublicKeyCamelImpl::getPublicKey(@state); assert(public_key == NEW_PUBKEY, 'Should update key'); } @@ -478,10 +477,12 @@ fn test_public_key_setter_and_getter_camel() { #[available_gas(2000000)] #[should_panic(expected: ('Account: unauthorized', ))] fn test_public_key_setter_different_account_camel() { + let mut state = STATE(); let caller = contract_address_const::<0x123>(); testing::set_contract_address(ACCOUNT_ADDRESS()); testing::set_caller_address(caller); - Account::setPublicKey(NEW_PUBKEY); + + Account::PublicKeyCamelImpl::setPublicKey(ref state, NEW_PUBKEY); } // @@ -491,8 +492,11 @@ fn test_public_key_setter_different_account_camel() { #[test] #[available_gas(2000000)] fn test_initializer() { - Account::initializer(PUBLIC_KEY); - assert(Account::get_public_key() == PUBLIC_KEY, 'Should return public key'); + let mut state = STATE(); + Account::InternalImpl::initializer(ref state, PUBLIC_KEY); + assert( + Account::PublicKeyImpl::get_public_key(@state) == PUBLIC_KEY, 'Should return public key' + ); } #[test] @@ -516,28 +520,24 @@ fn test_assert_only_self_false() { #[test] #[available_gas(2000000)] fn test__is_valid_signature() { + let mut state = STATE(); let data = SIGNED_TX_DATA(); let hash = data.transaction_hash; - let mut good_signature = ArrayTrait::new(); - good_signature.append(data.r); - good_signature.append(data.s); + let mut good_signature = array![data.r, data.s]; + let mut bad_signature = array![0x987, 0x564]; + let mut invalid_length_signature = array![0x987]; - let mut bad_signature = ArrayTrait::new(); - bad_signature.append(0x987); - bad_signature.append(0x564); + Account::PublicKeyImpl::set_public_key(ref state, data.public_key); - let mut invalid_length_signature = ArrayTrait::new(); - invalid_length_signature.append(0x987); - - Account::set_public_key(data.public_key); - - let is_valid = Account::_is_valid_signature(hash, good_signature.span()); + let is_valid = Account::InternalImpl::_is_valid_signature(@state, hash, good_signature.span()); assert(is_valid, 'Should accept valid signature'); - let is_valid = Account::_is_valid_signature(hash, bad_signature.span()); + let is_valid = Account::InternalImpl::_is_valid_signature(@state, hash, bad_signature.span()); assert(!is_valid, 'Should reject invalid signature'); - let is_valid = Account::_is_valid_signature(hash, invalid_length_signature.span()); + let is_valid = Account::InternalImpl::_is_valid_signature( + @state, hash, invalid_length_signature.span() + ); assert(!is_valid, 'Should reject invalid length'); } diff --git a/src/openzeppelin/tests/account/test_dual_account.cairo b/src/openzeppelin/tests/account/test_dual_account.cairo index 1d4e23840..76ea9fb99 100644 --- a/src/openzeppelin/tests/account/test_dual_account.cairo +++ b/src/openzeppelin/tests/account/test_dual_account.cairo @@ -1,13 +1,12 @@ -use array::ArrayTrait; use starknet::testing; use openzeppelin::account::dual_account::DualCaseAccount; -use openzeppelin::account::dual_account::DualCaseAccountTrait; -use openzeppelin::account::AccountABICamelDispatcher; -use openzeppelin::account::AccountABICamelDispatcherTrait; +use openzeppelin::account::dual_account::DualCaseAccountABI; +use openzeppelin::account::AccountCamelABIDispatcher; +use openzeppelin::account::AccountCamelABIDispatcherTrait; use openzeppelin::account::AccountABIDispatcher; use openzeppelin::account::AccountABIDispatcherTrait; -use openzeppelin::introspection::src5::ISRC5_ID; +use openzeppelin::introspection::interface::ISRC5_ID; use openzeppelin::tests::account::test_account::SIGNED_TX_DATA; use openzeppelin::tests::mocks::account_panic_mock::CamelAccountPanicMock; use openzeppelin::tests::mocks::account_panic_mock::SnakeAccountPanicMock; @@ -15,7 +14,6 @@ use openzeppelin::tests::mocks::camel_account_mock::CamelAccountMock; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::mocks::snake_account_mock::SnakeAccountMock; use openzeppelin::tests::utils; -use openzeppelin::utils::serde::SerializedAppend; // // Constants @@ -29,8 +27,7 @@ const NEW_PUBLIC_KEY: felt252 = 0x444444; // fn setup_snake() -> (DualCaseAccount, AccountABIDispatcher) { - let mut calldata = ArrayTrait::new(); - calldata.append_serde(PUBLIC_KEY); + let mut calldata = array![PUBLIC_KEY]; let target = utils::deploy(SnakeAccountMock::TEST_CLASS_HASH, calldata); ( DualCaseAccount { contract_address: target }, @@ -38,25 +35,24 @@ fn setup_snake() -> (DualCaseAccount, AccountABIDispatcher) { ) } -fn setup_camel() -> (DualCaseAccount, AccountABICamelDispatcher) { - let mut calldata = ArrayTrait::new(); - calldata.append_serde(PUBLIC_KEY); +fn setup_camel() -> (DualCaseAccount, AccountCamelABIDispatcher) { + let mut calldata = array![PUBLIC_KEY]; let target = utils::deploy(CamelAccountMock::TEST_CLASS_HASH, calldata); ( DualCaseAccount { contract_address: target }, - AccountABICamelDispatcher { contract_address: target } + AccountCamelABIDispatcher { contract_address: target } ) } fn setup_non_account() -> DualCaseAccount { - let calldata = ArrayTrait::new(); + let calldata = array![]; let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); DualCaseAccount { contract_address: target } } fn setup_account_panic() -> (DualCaseAccount, DualCaseAccount) { - let snake_target = utils::deploy(SnakeAccountPanicMock::TEST_CLASS_HASH, ArrayTrait::new()); - let camel_target = utils::deploy(CamelAccountPanicMock::TEST_CLASS_HASH, ArrayTrait::new()); + let snake_target = utils::deploy(SnakeAccountPanicMock::TEST_CLASS_HASH, array![]); + let camel_target = utils::deploy(CamelAccountPanicMock::TEST_CLASS_HASH, array![]); ( DualCaseAccount { contract_address: snake_target }, DualCaseAccount { contract_address: camel_target } @@ -124,10 +120,7 @@ fn test_dual_is_valid_signature() { let data = SIGNED_TX_DATA(); let hash = data.transaction_hash; - - let mut signature = ArrayTrait::new(); - signature.append(data.r); - signature.append(data.s); + let mut signature = array![data.r, data.s]; testing::set_contract_address(snake_dispatcher.contract_address); target.set_public_key(data.public_key); @@ -141,7 +134,7 @@ fn test_dual_is_valid_signature() { #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] fn test_dual_no_is_valid_signature() { let hash = 0x0; - let signature = ArrayTrait::::new(); + let signature = array![]; let dispatcher = setup_non_account(); dispatcher.is_valid_signature(hash, signature); @@ -152,13 +145,12 @@ fn test_dual_no_is_valid_signature() { #[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] fn test_dual_is_valid_signature_exists_and_panics() { let hash = 0x0; - let signature = ArrayTrait::::new(); + let signature = array![]; let (dispatcher, _) = setup_account_panic(); dispatcher.is_valid_signature(hash, signature); } - #[test] #[available_gas(2000000)] fn test_dual_supports_interface() { @@ -227,10 +219,7 @@ fn test_dual_isValidSignature() { let data = SIGNED_TX_DATA(); let hash = data.transaction_hash; - - let mut signature = ArrayTrait::new(); - signature.append(data.r); - signature.append(data.s); + let mut signature = array![data.r, data.s]; testing::set_contract_address(camel_dispatcher.contract_address); target.setPublicKey(data.public_key); @@ -244,13 +233,12 @@ fn test_dual_isValidSignature() { #[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] fn test_dual_isValidSignature_exists_and_panics() { let hash = 0x0; - let signature = ArrayTrait::::new(); + let signature = array![]; let (_, dispatcher) = setup_account_panic(); dispatcher.is_valid_signature(hash, signature); } - #[test] #[available_gas(2000000)] fn test_dual_supportsInterface() { @@ -265,3 +253,4 @@ fn test_dual_supportsInterface_exists_and_panics() { let (_, dispatcher) = setup_account_panic(); dispatcher.supports_interface(ISRC5_ID); } + diff --git a/src/openzeppelin/tests/mocks.cairo b/src/openzeppelin/tests/mocks.cairo index d8bdaa92b..3c1483fb3 100644 --- a/src/openzeppelin/tests/mocks.cairo +++ b/src/openzeppelin/tests/mocks.cairo @@ -8,11 +8,11 @@ // mod erc20_panic; // mod non721_mock; // mod accesscontrol_panic_mock; -// mod account_panic_mock; +mod account_panic_mock; // mod camel_accesscontrol_mock; -// mod camel_account_mock; +mod camel_account_mock; // mod snake_accesscontrol_mock; -// mod snake_account_mock; +mod snake_account_mock; // mod dual_ownable_mocks; // mod snake721_mock; // mod camel721_mock; diff --git a/src/openzeppelin/tests/mocks/account_panic_mock.cairo b/src/openzeppelin/tests/mocks/account_panic_mock.cairo index f3809b807..0a85c890c 100644 --- a/src/openzeppelin/tests/mocks/account_panic_mock.cairo +++ b/src/openzeppelin/tests/mocks/account_panic_mock.cairo @@ -4,53 +4,61 @@ // 3 for felt252 // false for bool -#[account_contract] +#[starknet::contract] mod SnakeAccountPanicMock { - #[external] - fn set_public_key(new_public_key: felt252) { + #[storage] + struct Storage {} + + #[external(v0)] + fn set_public_key(ref self: ContractState, new_public_key: felt252) { panic_with_felt252('Some error'); } - #[view] - fn get_public_key() -> felt252 { + #[external(v0)] + fn get_public_key(self: @ContractState) -> felt252 { panic_with_felt252('Some error'); 3 } - #[view] - fn is_valid_signature(hash: felt252, signature: Array) -> felt252 { + #[external(v0)] + fn is_valid_signature( + self: @ContractState, hash: felt252, signature: Array + ) -> felt252 { panic_with_felt252('Some error'); 3 } - #[view] - fn supports_interface(interface_id: felt252) -> bool { + #[external(v0)] + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { panic_with_felt252('Some error'); false } } -#[account_contract] +#[starknet::contract] mod CamelAccountPanicMock { - #[external] - fn setPublicKey(newPublicKey: felt252) { + #[storage] + struct Storage {} + + #[external(v0)] + fn setPublicKey(ref self: ContractState, newPublicKey: felt252) { panic_with_felt252('Some error'); } - #[view] - fn getPublicKey() -> felt252 { + #[external(v0)] + fn getPublicKey(self: @ContractState) -> felt252 { panic_with_felt252('Some error'); 3 } - #[view] - fn isValidSignature(hash: felt252, signature: Array) -> felt252 { + #[external(v0)] + fn isValidSignature(self: @ContractState, hash: felt252, signature: Array) -> felt252 { panic_with_felt252('Some error'); 3 } - #[view] - fn supportsInterface(interfaceId: felt252) -> bool { + #[external(v0)] + fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { panic_with_felt252('Some error'); false } diff --git a/src/openzeppelin/tests/mocks/camel_account_mock.cairo b/src/openzeppelin/tests/mocks/camel_account_mock.cairo index 01ee5b53e..19cf1725c 100644 --- a/src/openzeppelin/tests/mocks/camel_account_mock.cairo +++ b/src/openzeppelin/tests/mocks/camel_account_mock.cairo @@ -1,31 +1,37 @@ -#[account_contract] +#[starknet::contract] mod CamelAccountMock { - use openzeppelin::account::interface::Call; use openzeppelin::account::Account; - use openzeppelin::utils::serde::SpanSerde; + + #[storage] + struct Storage {} #[constructor] - fn constructor(_publicKey: felt252) { - Account::initializer(_publicKey); + fn constructor(ref self: ContractState, _publicKey: felt252) { + let mut unsafe_state = Account::unsafe_new_contract_state(); + Account::InternalImpl::initializer(ref unsafe_state, _publicKey); } - #[external] - fn setPublicKey(newPublicKey: felt252) { - Account::setPublicKey(newPublicKey); + #[external(v0)] + fn setPublicKey(ref self: ContractState, newPublicKey: felt252) { + let mut unsafe_state = Account::unsafe_new_contract_state(); + Account::PublicKeyCamelImpl::setPublicKey(ref unsafe_state, newPublicKey); } - #[view] - fn getPublicKey() -> felt252 { - Account::getPublicKey() + #[external(v0)] + fn getPublicKey(self: @ContractState) -> felt252 { + let unsafe_state = Account::unsafe_new_contract_state(); + Account::PublicKeyCamelImpl::getPublicKey(@unsafe_state) } - #[view] - fn isValidSignature(hash: felt252, signature: Array) -> felt252 { - Account::isValidSignature(hash, signature) + #[external(v0)] + fn isValidSignature(self: @ContractState, hash: felt252, signature: Array) -> felt252 { + let unsafe_state = Account::unsafe_new_contract_state(); + Account::SRC6CamelOnlyImpl::isValidSignature(@unsafe_state, hash, signature) } - #[view] - fn supportsInterface(interfaceId: felt252) -> bool { - Account::supportsInterface(interfaceId) + #[external(v0)] + fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { + let unsafe_state = Account::unsafe_new_contract_state(); + Account::SRC5CamelImpl::supportsInterface(@unsafe_state, interfaceId) } } diff --git a/src/openzeppelin/tests/mocks/snake_account_mock.cairo b/src/openzeppelin/tests/mocks/snake_account_mock.cairo index 03b9cd05d..80dc1f715 100644 --- a/src/openzeppelin/tests/mocks/snake_account_mock.cairo +++ b/src/openzeppelin/tests/mocks/snake_account_mock.cairo @@ -1,53 +1,39 @@ -#[account_contract] +#[starknet::contract] mod SnakeAccountMock { - use openzeppelin::account::interface::Call; use openzeppelin::account::Account; - use openzeppelin::utils::serde::SpanSerde; - #[constructor] - fn constructor(_public_key: felt252) { - Account::initializer(_public_key); - } + #[storage] + struct Storage {} - #[external] - fn __execute__(mut calls: Array) -> Array> { - Account::__execute__(calls) + #[constructor] + fn constructor(ref self: ContractState, _public_key: felt252) { + let mut unsafe_state = Account::unsafe_new_contract_state(); + Account::InternalImpl::initializer(ref unsafe_state, _public_key); } - #[external] - fn __validate__(mut calls: Array) -> felt252 { - Account::__validate__(calls) + #[external(v0)] + fn set_public_key(ref self: ContractState, new_public_key: felt252) { + let mut unsafe_state = Account::unsafe_new_contract_state(); + Account::PublicKeyImpl::set_public_key(ref unsafe_state, new_public_key); } - #[external] - fn __validate_declare__(class_hash: felt252) -> felt252 { - Account::__validate_declare__(class_hash) + #[external(v0)] + fn get_public_key(self: @ContractState) -> felt252 { + let unsafe_state = Account::unsafe_new_contract_state(); + Account::PublicKeyImpl::get_public_key(@unsafe_state) } - #[external] - fn __validate_deploy__( - class_hash: felt252, contract_address_salt: felt252, _public_key: felt252 + #[external(v0)] + fn is_valid_signature( + self: @ContractState, hash: felt252, signature: Array ) -> felt252 { - Account::__validate_deploy__(class_hash, contract_address_salt, _public_key) - } - - #[external] - fn set_public_key(new_public_key: felt252) { - Account::set_public_key(new_public_key); - } - - #[view] - fn get_public_key() -> felt252 { - Account::get_public_key() - } - - #[view] - fn is_valid_signature(hash: felt252, signature: Array) -> felt252 { - Account::is_valid_signature(hash, signature) + let unsafe_state = Account::unsafe_new_contract_state(); + Account::SRC6Impl::is_valid_signature(@unsafe_state, hash, signature) } - #[view] - fn supports_interface(interface_id: felt252) -> bool { - Account::supports_interface(interface_id) + #[external(v0)] + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { + let unsafe_state = Account::unsafe_new_contract_state(); + Account::SRC5Impl::supports_interface(@unsafe_state, interface_id) } } From dc4a233cd0c56b80eadb51fd0a8dabd7fce135e7 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Thu, 20 Jul 2023 09:17:44 -0400 Subject: [PATCH 068/246] Migrate pausable to cairo2 (#662) * bump to cairo v2.0.2 * update Cargo * comment out mods * update initializable syntax * move security tests * update initializable tests * fix formatting * update pausable syntax * update pausable tests * update cairo * update Cargo * simplify and clarify tests * fix formatting * simplify and clarify tests * Apply suggestions from code review Co-authored-by: Eric Nordelo * fix tests * change StorageTrait to InternalTrait * use InternalImpl * add external pausable impl * Apply suggestions from code review Co-authored-by: Eric Nordelo --------- Co-authored-by: Eric Nordelo --- src/openzeppelin/security.cairo | 2 +- src/openzeppelin/security/pausable.cairo | 65 +++++++---- src/openzeppelin/tests/security.cairo | 2 +- .../tests/security/test_pausable.cairo | 104 ++++++++++++++---- 4 files changed, 127 insertions(+), 46 deletions(-) diff --git a/src/openzeppelin/security.cairo b/src/openzeppelin/security.cairo index 1e38ab41a..25183eb51 100644 --- a/src/openzeppelin/security.cairo +++ b/src/openzeppelin/security.cairo @@ -1,3 +1,3 @@ //mod reentrancyguard; -//mod pausable; +mod pausable; mod initializable; diff --git a/src/openzeppelin/security/pausable.cairo b/src/openzeppelin/security/pausable.cairo index 0a9018f51..66e159181 100644 --- a/src/openzeppelin/security/pausable.cairo +++ b/src/openzeppelin/security/pausable.cairo @@ -1,39 +1,60 @@ -#[contract] +#[starknet::interface] +trait IPausable { + fn is_paused(self: @TState) -> bool; +} + +#[starknet::contract] mod Pausable { use starknet::ContractAddress; use starknet::get_caller_address; + #[storage] struct Storage { paused: bool } #[event] - fn Paused(account: ContractAddress) {} - - #[event] - fn Unpaused(account: ContractAddress) {} - - fn is_paused() -> bool { - paused::read() + #[derive(Drop, starknet::Event)] + enum Event { + Paused: Paused, + Unpaused: Unpaused, } - - fn assert_not_paused() { - assert(!is_paused(), 'Pausable: paused'); + #[derive(Drop, starknet::Event)] + struct Paused { + account: ContractAddress } - - fn assert_paused() { - assert(is_paused(), 'Pausable: not paused'); + #[derive(Drop, starknet::Event)] + struct Unpaused { + account: ContractAddress } - fn pause() { - assert_not_paused(); - paused::write(true); - Paused(get_caller_address()); + #[external(v0)] + impl PausableImpl of super::IPausable { + fn is_paused(self: @ContractState) -> bool { + self.paused.read() + } } - fn unpause() { - assert_paused(); - paused::write(false); - Unpaused(get_caller_address()); + #[generate_trait] + impl InternalImpl of InternalTrait { + fn assert_not_paused(self: @ContractState) { + assert(!self.paused.read(), 'Pausable: paused'); + } + + fn assert_paused(self: @ContractState) { + assert(self.paused.read(), 'Pausable: not paused'); + } + + fn _pause(ref self: ContractState) { + self.assert_not_paused(); + self.paused.write(true); + self.emit(Paused { account: get_caller_address() }); + } + + fn _unpause(ref self: ContractState) { + self.assert_paused(); + self.paused.write(false); + self.emit(Unpaused { account: get_caller_address() }); + } } } diff --git a/src/openzeppelin/tests/security.cairo b/src/openzeppelin/tests/security.cairo index 56019a336..c350e48b3 100644 --- a/src/openzeppelin/tests/security.cairo +++ b/src/openzeppelin/tests/security.cairo @@ -1,5 +1,5 @@ mod test_initializable; -//mod test_pausable; +mod test_pausable; //mod test_reentrancyguard; diff --git a/src/openzeppelin/tests/security/test_pausable.cairo b/src/openzeppelin/tests/security/test_pausable.cairo index 6b76e9375..79bafced6 100644 --- a/src/openzeppelin/tests/security/test_pausable.cairo +++ b/src/openzeppelin/tests/security/test_pausable.cairo @@ -1,47 +1,107 @@ -use openzeppelin::tests::mocks::mock_pausable::MockPausable; +use openzeppelin::security::pausable::Pausable; +use openzeppelin::security::pausable::Pausable::PausableImpl; +use openzeppelin::security::pausable::Pausable::InternalImpl; + +fn STATE() -> Pausable::ContractState { + Pausable::contract_state_for_testing() +} + +// +// is_paused +// #[test] #[available_gas(2000000)] -fn test_pause_when_unpaused() { - assert(!MockPausable::is_paused(), 'Should not be paused'); - assert(MockPausable::get_count() == 0, 'Should be 0'); - MockPausable::assert_unpaused_and_increment(); - assert(MockPausable::get_count() == 1, 'Should increment'); - MockPausable::pause(); - assert(MockPausable::is_paused(), 'Should be paused'); +fn test_is_paused() { + let mut state = STATE(); + assert(!PausableImpl::is_paused(@state), 'Should not be paused'); + + InternalImpl::_pause(ref state); + assert(PausableImpl::is_paused(@state), 'Should be paused'); + + InternalImpl::_unpause(ref state); + assert(!PausableImpl::is_paused(@state), 'Should not be paused'); +} + +// +// assert_paused +// + +#[test] +#[available_gas(2000000)] +fn test_assert_paused_when_paused() { + let mut state = STATE(); + InternalImpl::_pause(ref state); + InternalImpl::assert_paused(@state); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Pausable: not paused', ))] +fn test_assert_paused_when_not_paused() { + let state = STATE(); + InternalImpl::assert_paused(@state); } +// +// assert_not_paused +// + #[test] #[available_gas(2000000)] #[should_panic(expected: ('Pausable: paused', ))] -fn test_pause_when_paused() { - MockPausable::pause(); - MockPausable::pause(); +fn test_assert_not_paused_when_paused() { + let mut state = STATE(); + InternalImpl::_pause(ref state); + InternalImpl::assert_not_paused(@state); +} + +#[test] +#[available_gas(2000000)] +fn test_assert_not_paused_when_not_paused() { + let state = STATE(); + InternalImpl::assert_not_paused(@state); +} + +// +// pause +// + +#[test] +#[available_gas(2000000)] +fn test_pause_when_unpaused() { + let mut state = STATE(); + InternalImpl::_pause(ref state); + assert(PausableImpl::is_paused(@state), 'Should be paused'); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('Pausable: paused', ))] -fn test_pause_increment() { - MockPausable::pause(); - MockPausable::assert_unpaused_and_increment(); +fn test_pause_when_paused() { + let mut state = STATE(); + InternalImpl::_pause(ref state); + InternalImpl::_pause(ref state); } +// +// unpause +// + #[test] #[available_gas(2000000)] fn test_unpause_when_paused() { - MockPausable::pause(); - assert(MockPausable::is_paused(), 'Should be paused'); - MockPausable::unpause(); - assert(!MockPausable::is_paused(), 'Should not be paused'); - MockPausable::assert_unpaused_and_increment(); - assert(MockPausable::get_count() == 1, 'Should increment'); + let mut state = STATE(); + InternalImpl::_pause(ref state); + InternalImpl::_unpause(ref state); + assert(!PausableImpl::is_paused(@state), 'Should not be paused'); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('Pausable: not paused', ))] fn test_unpause_when_unpaused() { - assert(!MockPausable::is_paused(), 'Should be unpaused'); - MockPausable::unpause(); + let mut state = STATE(); + assert(!PausableImpl::is_paused(@state), 'Should be paused'); + InternalImpl::_unpause(ref state); } From 27514b17a203a17d7c37dca14dcfcce7c2485e2e Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Thu, 20 Jul 2023 09:45:30 -0400 Subject: [PATCH 069/246] Migrate reentrancyguard to cairo2 (#663) * bump to cairo v2.0.2 * update Cargo * comment out mods * update initializable syntax * move security tests * update initializable tests * fix formatting * remove SpanSerde * update reentrancyguard syntax * add reentrancyguard mocks * update reentrancyguard tests * update cairo * update Cargo * simplify and clarify tests * fix formatting * simplify and clarify tests * fix formatting * Apply suggestions from code review Co-authored-by: Eric Nordelo * fix tests * change StorageTrait to InternalTrait * remove internal is_entered * use internal contract state * Apply suggestions from code review Co-authored-by: Eric Nordelo * fix import order * change generic state to TState --------- Co-authored-by: Eric Nordelo --- src/openzeppelin/security.cairo | 2 +- .../security/reentrancyguard.cairo | 18 ++- src/openzeppelin/tests/mocks.cairo | 4 +- .../mocks/reentrancy_attacker_mock.cairo | 26 ++-- .../tests/mocks/reentrancy_mock.cairo | 135 +++++++++--------- src/openzeppelin/tests/security.cairo | 4 +- .../tests/security/test_reentrancyguard.cairo | 31 ++-- src/openzeppelin/utils/serde.cairo | 2 - 8 files changed, 117 insertions(+), 105 deletions(-) diff --git a/src/openzeppelin/security.cairo b/src/openzeppelin/security.cairo index 25183eb51..45a9c998a 100644 --- a/src/openzeppelin/security.cairo +++ b/src/openzeppelin/security.cairo @@ -1,3 +1,3 @@ -//mod reentrancyguard; +mod reentrancyguard; mod pausable; mod initializable; diff --git a/src/openzeppelin/security/reentrancyguard.cairo b/src/openzeppelin/security/reentrancyguard.cairo index 4aa7375a6..17a2e8d9c 100644 --- a/src/openzeppelin/security/reentrancyguard.cairo +++ b/src/openzeppelin/security/reentrancyguard.cairo @@ -1,17 +1,21 @@ -#[contract] +#[starknet::contract] mod ReentrancyGuard { use starknet::get_caller_address; + #[storage] struct Storage { entered: bool } - fn start() { - assert(!entered::read(), 'ReentrancyGuard: reentrant call'); - entered::write(true); - } + #[generate_trait] + impl InternalImpl of InternalTrait { + fn start(ref self: ContractState) { + assert(!self.entered.read(), 'ReentrancyGuard: reentrant call'); + self.entered.write(true); + } - fn end() { - entered::write(false); + fn end(ref self: ContractState) { + self.entered.write(false); + } } } diff --git a/src/openzeppelin/tests/mocks.cairo b/src/openzeppelin/tests/mocks.cairo index 3c1483fb3..b40cba35f 100644 --- a/src/openzeppelin/tests/mocks.cairo +++ b/src/openzeppelin/tests/mocks.cairo @@ -1,5 +1,5 @@ -// mod reentrancy_attacker_mock; -// mod reentrancy_mock; +mod reentrancy_attacker_mock; +mod reentrancy_mock; // mod erc721_receiver; // mod erc721_panic_mock; // mod mock_pausable; diff --git a/src/openzeppelin/tests/mocks/reentrancy_attacker_mock.cairo b/src/openzeppelin/tests/mocks/reentrancy_attacker_mock.cairo index 072432d00..39560f902 100644 --- a/src/openzeppelin/tests/mocks/reentrancy_attacker_mock.cairo +++ b/src/openzeppelin/tests/mocks/reentrancy_attacker_mock.cairo @@ -1,21 +1,23 @@ -#[abi] -trait IAttacker { - fn call_sender(); +#[starknet::interface] +trait IAttacker { + fn call_sender(self: @TState); } -#[contract] +#[starknet::contract] mod Attacker { - // Dispatcher + use starknet::ContractAddress; + use starknet::get_caller_address; use openzeppelin::tests::mocks::reentrancy_mock::IReentrancyMockDispatcher; use openzeppelin::tests::mocks::reentrancy_mock::IReentrancyMockDispatcherTrait; - // Other - use starknet::ContractAddress; - use starknet::get_caller_address; + #[storage] + struct Storage {} - #[external] - fn call_sender() { - let caller: ContractAddress = get_caller_address(); - IReentrancyMockDispatcher { contract_address: caller }.callback(); + #[external(v0)] + impl IAttackerImpl of super::IAttacker { + fn call_sender(self: @ContractState) { + let caller: ContractAddress = get_caller_address(); + IReentrancyMockDispatcher { contract_address: caller }.callback(); + } } } diff --git a/src/openzeppelin/tests/mocks/reentrancy_mock.cairo b/src/openzeppelin/tests/mocks/reentrancy_mock.cairo index a950d27f1..0850954c5 100644 --- a/src/openzeppelin/tests/mocks/reentrancy_mock.cairo +++ b/src/openzeppelin/tests/mocks/reentrancy_mock.cairo @@ -1,93 +1,92 @@ use starknet::ContractAddress; -#[abi] -trait IReentrancyGuarded { - fn count_external_recursive(n: felt252); +#[starknet::interface] +trait IReentrancyGuarded { + fn count_external_recursive(ref self: TState, n: felt252); } -#[abi] -trait IReentrancyMock { - #[view] - fn current_count() -> felt252; - #[external] - fn callback(); - #[external] - fn count_local_recursive(n: felt252); - #[external] - fn count_external_recursive(n: felt252); - #[external] - fn count_and_call(attacker: ContractAddress); - #[external] - fn count(); +#[starknet::interface] +trait IReentrancyMock { + fn current_count(self: @TState) -> felt252; + fn callback(ref self: TState); + fn count_local_recursive(ref self: TState, n: felt252); + fn count_external_recursive(ref self: TState, n: felt252); + fn count_and_call(ref self: TState, attacker: ContractAddress); } -#[contract] +#[starknet::contract] mod ReentrancyMock { - // OZ modules + use starknet::ContractAddress; + use starknet::get_contract_address; use openzeppelin::security::reentrancyguard::ReentrancyGuard; - - // Dispatchers - use super::IReentrancyGuardedDispatcher; - use super::IReentrancyGuardedDispatcherTrait; use openzeppelin::tests::mocks::reentrancy_attacker_mock::IAttackerDispatcher; use openzeppelin::tests::mocks::reentrancy_attacker_mock::IAttackerDispatcherTrait; + use super::IReentrancyGuardedDispatcher; + use super::IReentrancyGuardedDispatcherTrait; - // Other - use option::OptionTrait; - use starknet::ContractAddress; - use starknet::get_caller_address; - use starknet::get_contract_address; - + #[storage] struct Storage { counter: felt252 } - #[view] - fn current_count() -> felt252 { - counter::read() - } + #[generate_trait] + impl InternalImpl of InternalTrait { + fn count(ref self: ContractState) { + self.counter.write(self.counter.read() + 1); + } - #[external] - fn callback() { - ReentrancyGuard::start(); - count(); - ReentrancyGuard::end(); - } + fn _count_local_recursive(ref self: ContractState, n: felt252) { + let mut unsafe_state = ReentrancyGuard::unsafe_new_contract_state(); + ReentrancyGuard::InternalImpl::start(ref unsafe_state); - #[external] - fn count_local_recursive(n: felt252) { - ReentrancyGuard::start(); - gas::withdraw_gas().expect('Out of gas'); - if n != 0 { - count(); - count_local_recursive(n - 1); + if n != 0 { + self.count(); + self._count_local_recursive(n - 1); + } + + ReentrancyGuard::InternalImpl::end(ref unsafe_state); } - ReentrancyGuard::end(); } - #[external] - fn count_external_recursive(n: felt252) { - ReentrancyGuard::start(); - gas::withdraw_gas().expect('Out of gas'); - if n != 0 { - count(); - let this: ContractAddress = get_contract_address(); - IReentrancyGuardedDispatcher { contract_address: this }.count_external_recursive(n - 1) + #[external(v0)] + impl IReentrancyMockImpl of super::IReentrancyMock { + fn current_count(self: @ContractState) -> felt252 { + self.counter.read() } - ReentrancyGuard::end(); - } - #[external] - fn count_and_call(attacker: ContractAddress) { - ReentrancyGuard::start(); - gas::withdraw_gas().expect('Out of gas'); - count(); - IAttackerDispatcher { contract_address: attacker }.call_sender(); - ReentrancyGuard::end(); - } + fn callback(ref self: ContractState) { + let mut unsafe_state = ReentrancyGuard::unsafe_new_contract_state(); + ReentrancyGuard::InternalImpl::start(ref unsafe_state); + self.count(); + ReentrancyGuard::InternalImpl::end(ref unsafe_state); + } + + fn count_local_recursive(ref self: ContractState, n: felt252) { + self._count_local_recursive(n); + } + + fn count_external_recursive(ref self: ContractState, n: felt252) { + let mut unsafe_state = ReentrancyGuard::unsafe_new_contract_state(); + ReentrancyGuard::InternalImpl::start(ref unsafe_state); - #[external] - fn count() { - counter::write(counter::read() + 1); + if n != 0 { + self.count(); + let this: ContractAddress = get_contract_address(); + IReentrancyGuardedDispatcher { contract_address: this } + .count_external_recursive(n - 1) + } + + ReentrancyGuard::InternalImpl::end(ref unsafe_state); + } + + fn count_and_call(ref self: ContractState, attacker: ContractAddress) { + let mut unsafe_state = ReentrancyGuard::unsafe_new_contract_state(); + ReentrancyGuard::InternalImpl::start(ref unsafe_state); + + self.count(); + IAttackerDispatcher { contract_address: attacker }.call_sender(); + + ReentrancyGuard::InternalImpl::end(ref unsafe_state); + } } } diff --git a/src/openzeppelin/tests/security.cairo b/src/openzeppelin/tests/security.cairo index c350e48b3..1efdf53c8 100644 --- a/src/openzeppelin/tests/security.cairo +++ b/src/openzeppelin/tests/security.cairo @@ -1,5 +1,3 @@ mod test_initializable; +mod test_reentrancyguard; mod test_pausable; -//mod test_reentrancyguard; - - diff --git a/src/openzeppelin/tests/security/test_reentrancyguard.cairo b/src/openzeppelin/tests/security/test_reentrancyguard.cairo index 174035cb7..f0891b213 100644 --- a/src/openzeppelin/tests/security/test_reentrancyguard.cairo +++ b/src/openzeppelin/tests/security/test_reentrancyguard.cairo @@ -1,14 +1,18 @@ use openzeppelin::security::reentrancyguard::ReentrancyGuard; +use openzeppelin::security::reentrancyguard::ReentrancyGuard::InternalImpl; +use openzeppelin::security::reentrancyguard::ReentrancyGuard::entered::InternalContractStateTrait; use openzeppelin::tests::mocks::reentrancy_mock::ReentrancyMock; use openzeppelin::tests::mocks::reentrancy_mock::IReentrancyMockDispatcher; use openzeppelin::tests::mocks::reentrancy_mock::IReentrancyMockDispatcherTrait; use openzeppelin::tests::mocks::reentrancy_attacker_mock::Attacker; use openzeppelin::tests::utils; -use array::ArrayTrait; +fn STATE() -> ReentrancyGuard::ContractState { + ReentrancyGuard::contract_state_for_testing() +} fn deploy_mock() -> IReentrancyMockDispatcher { - let calldata = ArrayTrait::new(); + let calldata = array![]; let address = utils::deploy(ReentrancyMock::TEST_CLASS_HASH, calldata); IReentrancyMockDispatcher { contract_address: address } } @@ -20,25 +24,32 @@ fn deploy_mock() -> IReentrancyMockDispatcher { #[test] #[available_gas(2000000)] fn test_reentrancy_guard_start() { - assert(!ReentrancyGuard::entered::read(), 'Guard should not be active'); - ReentrancyGuard::start(); - assert(ReentrancyGuard::entered::read(), 'Guard should be active'); + let mut state = STATE(); + + assert(!state.entered.read(), 'Should not be entered'); + InternalImpl::start(ref state); + assert(state.entered.read(), 'Should be entered'); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('ReentrancyGuard: reentrant call', ))] fn test_reentrancy_guard_start_when_started() { - ReentrancyGuard::start(); - ReentrancyGuard::start(); + let mut state = STATE(); + + InternalImpl::start(ref state); + InternalImpl::start(ref state); } #[test] #[available_gas(2000000)] fn test_reentrancy_guard_end() { - ReentrancyGuard::start(); - ReentrancyGuard::end(); - assert(!ReentrancyGuard::entered::read(), 'Guard should not be active'); + let mut state = STATE(); + + InternalImpl::start(ref state); + assert(state.entered.read(), 'Should be entered'); + InternalImpl::end(ref state); + assert(!state.entered.read(), 'Should no longer be entered'); } // diff --git a/src/openzeppelin/utils/serde.cairo b/src/openzeppelin/utils/serde.cairo index 45b33c8e4..1b3ea8cd8 100644 --- a/src/openzeppelin/utils/serde.cairo +++ b/src/openzeppelin/utils/serde.cairo @@ -1,5 +1,3 @@ -use array::ArrayTrait; -use array::SpanTrait; use serde::Serde; trait SerializedAppend { From 8e108217b2e90c0c318b532c4c8898a55f0f320c Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 21 Jul 2023 18:04:18 +0200 Subject: [PATCH 070/246] Migrate ERC721 to Cairo2 (#667) * feat: migrate files * feat: add interface.cairo * feat: apply review updates * refactor: improve readability * fix: dual account tests * fix: account tests * fix: mocks * fix: dual721 tests * fix: erc721 tests * feat: apply review updates * feat: apply review suggestions --- src/openzeppelin/account/account.cairo | 1 - src/openzeppelin/lib.cairo | 2 +- src/openzeppelin/tests.cairo | 2 +- src/openzeppelin/tests/mocks.cairo | 11 +- .../tests/mocks/camel721_mock.cairo | 115 ++- .../tests/mocks/dual721_receiver_mocks.cairo | 86 +- .../tests/mocks/erc721_panic_mock.cairo | 118 +-- .../tests/mocks/erc721_receiver.cairo | 87 +- .../tests/mocks/non721_mock.cairo | 7 - .../tests/mocks/snake721_mock.cairo | 115 ++- src/openzeppelin/tests/token.cairo | 4 +- .../tests/token/test_dual20.cairo | 4 +- .../tests/token/test_dual721.cairo | 28 +- .../tests/token/test_erc721.cairo | 814 ++++++++++-------- src/openzeppelin/token.cairo | 2 +- src/openzeppelin/token/erc20.cairo | 6 +- src/openzeppelin/token/erc721.cairo | 7 +- src/openzeppelin/token/erc721/dual721.cairo | 28 +- .../token/erc721/dual721_receiver.cairo | 2 +- src/openzeppelin/token/erc721/erc721.cairo | 580 +++++-------- src/openzeppelin/token/erc721/interface.cairo | 102 ++- 21 files changed, 1096 insertions(+), 1025 deletions(-) delete mode 100644 src/openzeppelin/tests/mocks/non721_mock.cairo diff --git a/src/openzeppelin/account/account.cairo b/src/openzeppelin/account/account.cairo index 714f1f89f..e1a3d57dd 100644 --- a/src/openzeppelin/account/account.cairo +++ b/src/openzeppelin/account/account.cairo @@ -5,7 +5,6 @@ use serde::Serde; use starknet::account::Call; use starknet::ContractAddress; - const TRANSACTION_VERSION: felt252 = 1; // 2**128 + TRANSACTION_VERSION diff --git a/src/openzeppelin/lib.cairo b/src/openzeppelin/lib.cairo index 5c658386e..ef3dfb94f 100644 --- a/src/openzeppelin/lib.cairo +++ b/src/openzeppelin/lib.cairo @@ -3,5 +3,5 @@ mod account; mod introspection; mod security; mod tests; -// mod token; +mod token; mod utils; diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index 440fdfc2f..d2a724b37 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -3,5 +3,5 @@ mod account; mod introspection; mod mocks; mod security; -// mod token; +mod token; mod utils; diff --git a/src/openzeppelin/tests/mocks.cairo b/src/openzeppelin/tests/mocks.cairo index b40cba35f..0e838248e 100644 --- a/src/openzeppelin/tests/mocks.cairo +++ b/src/openzeppelin/tests/mocks.cairo @@ -1,12 +1,11 @@ mod reentrancy_attacker_mock; mod reentrancy_mock; -// mod erc721_receiver; -// mod erc721_panic_mock; +mod erc721_receiver; +mod erc721_panic_mock; // mod mock_pausable; // mod camel20_mock; // mod snake20_mock; // mod erc20_panic; -// mod non721_mock; // mod accesscontrol_panic_mock; mod account_panic_mock; // mod camel_accesscontrol_mock; @@ -14,8 +13,8 @@ mod camel_account_mock; // mod snake_accesscontrol_mock; mod snake_account_mock; // mod dual_ownable_mocks; -// mod snake721_mock; -// mod camel721_mock; +mod snake721_mock; +mod camel721_mock; mod non_implementing_mock; -// mod dual721_receiver_mocks; +mod dual721_receiver_mocks; mod src5_mocks; diff --git a/src/openzeppelin/tests/mocks/camel721_mock.cairo b/src/openzeppelin/tests/mocks/camel721_mock.cairo index b2e02793b..553114f7e 100644 --- a/src/openzeppelin/tests/mocks/camel721_mock.cairo +++ b/src/openzeppelin/tests/mocks/camel721_mock.cairo @@ -1,80 +1,103 @@ -#[contract] +#[starknet::contract] mod CamelERC721Mock { use starknet::ContractAddress; use starknet::get_caller_address; use openzeppelin::token::erc721::ERC721; - use openzeppelin::utils::serde::SpanSerde; + use openzeppelin::token::erc721::ERC721::InternalImpl; + use openzeppelin::token::erc721::ERC721::ERC721CamelOnlyImpl; + + #[storage] + struct Storage {} #[constructor] - fn constructor(name: felt252, symbol: felt252, tokenId: u256, uri: felt252) { - ERC721::initializer(name, symbol); - ERC721::_mint(get_caller_address(), tokenId); - ERC721::_set_token_uri(tokenId, uri); + fn constructor( + ref self: ContractState, name: felt252, symbol: felt252, tokenId: u256, uri: felt252 + ) { + let mut unsafe_state = ERC721::unsafe_new_contract_state(); + InternalImpl::initializer(ref unsafe_state, name, symbol); + InternalImpl::_mint(ref unsafe_state, get_caller_address(), tokenId); + InternalImpl::_set_token_uri(ref unsafe_state, tokenId, uri); } - // View - - #[view] - fn supportsInterface(interfaceId: felt252) -> bool { - ERC721::supportsInterface(interfaceId) + #[external(v0)] + fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { + let unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721::SRC5CamelImpl::supportsInterface(@unsafe_state, interfaceId) } - #[view] - fn name() -> felt252 { - ERC721::name() + #[external(v0)] + fn name(self: @ContractState) -> felt252 { + let unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721::ERC721MetadataImpl::name(@unsafe_state) } - #[view] - fn symbol() -> felt252 { - ERC721::symbol() + #[external(v0)] + fn symbol(self: @ContractState) -> felt252 { + let unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721::ERC721MetadataImpl::symbol(@unsafe_state) } - #[view] - fn tokenUri(tokenId: u256) -> felt252 { - ERC721::tokenUri(tokenId) + #[external(v0)] + fn tokenUri(self: @ContractState, tokenId: u256) -> felt252 { + let unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721::ERC721MetadataCamelOnlyImpl::tokenUri(@unsafe_state, tokenId) } - #[view] - fn balanceOf(account: ContractAddress) -> u256 { - ERC721::balanceOf(account) + #[external(v0)] + fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 { + let unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721CamelOnlyImpl::balanceOf(@unsafe_state, account) } - #[view] - fn ownerOf(tokenId: u256) -> ContractAddress { - ERC721::ownerOf(tokenId) + #[external(v0)] + fn ownerOf(self: @ContractState, tokenId: u256) -> ContractAddress { + let unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721CamelOnlyImpl::ownerOf(@unsafe_state, tokenId) } - #[view] - fn getApproved(tokenId: u256) -> ContractAddress { - ERC721::getApproved(tokenId) + #[external(v0)] + fn getApproved(self: @ContractState, tokenId: u256) -> ContractAddress { + let unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721CamelOnlyImpl::getApproved(@unsafe_state, tokenId) } - #[view] - fn isApprovedForAll(owner: ContractAddress, operator: ContractAddress) -> bool { - ERC721::isApprovedForAll(owner, operator) + #[external(v0)] + fn isApprovedForAll( + self: @ContractState, owner: ContractAddress, operator: ContractAddress + ) -> bool { + let unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721CamelOnlyImpl::isApprovedForAll(@unsafe_state, owner, operator) } - // External - - #[external] - fn approve(to: ContractAddress, tokenId: u256) { - ERC721::approve(to, tokenId) + #[external(v0)] + fn approve(ref self: ContractState, to: ContractAddress, tokenId: u256) { + let mut unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721::ERC721Impl::approve(ref unsafe_state, to, tokenId) } - #[external] - fn setApprovalForAll(operator: ContractAddress, approved: bool) { - ERC721::setApprovalForAll(operator, approved) + #[external(v0)] + fn setApprovalForAll(ref self: ContractState, operator: ContractAddress, approved: bool) { + let mut unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721CamelOnlyImpl::setApprovalForAll(ref unsafe_state, operator, approved) } - #[external] - fn transferFrom(from: ContractAddress, to: ContractAddress, tokenId: u256) { - ERC721::transferFrom(from, to, tokenId) + #[external(v0)] + fn transferFrom( + ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256 + ) { + let mut unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721CamelOnlyImpl::transferFrom(ref unsafe_state, from, to, tokenId) } - #[external] + #[external(v0)] fn safeTransferFrom( - from: ContractAddress, to: ContractAddress, tokenId: u256, data: Span + ref self: ContractState, + from: ContractAddress, + to: ContractAddress, + tokenId: u256, + data: Span ) { - ERC721::safeTransferFrom(from, to, tokenId, data) + let mut unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721CamelOnlyImpl::safeTransferFrom(ref unsafe_state, from, to, tokenId, data) } } diff --git a/src/openzeppelin/tests/mocks/dual721_receiver_mocks.cairo b/src/openzeppelin/tests/mocks/dual721_receiver_mocks.cairo index 3ffb3df74..897918b68 100644 --- a/src/openzeppelin/tests/mocks/dual721_receiver_mocks.cairo +++ b/src/openzeppelin/tests/mocks/dual721_receiver_mocks.cairo @@ -2,80 +2,110 @@ use openzeppelin::introspection::src5::SRC5; use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver::IERC721_RECEIVER_ID; -#[contract] +#[starknet::contract] mod SnakeERC721ReceiverMock { - use openzeppelin::utils::serde::SpanSerde; use starknet::ContractAddress; use super::ERC721Receiver; use super::IERC721_RECEIVER_ID; use super::SRC5; + #[storage] + struct Storage {} + #[constructor] - fn constructor() { - SRC5::register_interface(IERC721_RECEIVER_ID); + fn constructor(ref self: ContractState) { + let mut unsafe_state = SRC5::unsafe_new_contract_state(); + SRC5::InternalImpl::register_interface(ref unsafe_state, IERC721_RECEIVER_ID); } - #[view] + #[external(v0)] fn on_erc721_received( - operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span + self: @ContractState, + operator: ContractAddress, + from: ContractAddress, + token_id: u256, + data: Span ) -> felt252 { - ERC721Receiver::on_erc721_received(operator, from, token_id, data) + let unsafe_state = ERC721Receiver::unsafe_new_contract_state(); + ERC721Receiver::on_erc721_received(@unsafe_state, operator, from, token_id, data) } - #[view] - fn supports_interface(interface_id: felt252) -> bool { - ERC721Receiver::supports_interface(interface_id) + #[external(v0)] + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { + let unsafe_state = ERC721Receiver::unsafe_new_contract_state(); + ERC721Receiver::supports_interface(@unsafe_state, interface_id) } } -#[contract] +#[starknet::contract] mod CamelERC721ReceiverMock { - use openzeppelin::utils::serde::SpanSerde; use starknet::ContractAddress; use super::ERC721Receiver; use super::IERC721_RECEIVER_ID; use super::SRC5; + #[storage] + struct Storage {} + #[constructor] - fn constructor() { - SRC5::register_interface(IERC721_RECEIVER_ID); + fn constructor(ref self: ContractState) { + let mut unsafe_state = SRC5::unsafe_new_contract_state(); + SRC5::InternalImpl::register_interface(ref unsafe_state, IERC721_RECEIVER_ID); } - #[view] + #[external(v0)] fn onERC721Received( - operator: ContractAddress, from: ContractAddress, tokenId: u256, data: Span + self: @ContractState, + operator: ContractAddress, + from: ContractAddress, + tokenId: u256, + data: Span ) -> felt252 { - ERC721Receiver::on_erc721_received(operator, from, tokenId, data) + let unsafe_state = ERC721Receiver::unsafe_new_contract_state(); + ERC721Receiver::on_erc721_received(@unsafe_state, operator, from, tokenId, data) } - #[view] - fn supportsInterface(interfaceId: felt252) -> bool { - ERC721Receiver::supportsInterface(interfaceId) + #[external(v0)] + fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { + let unsafe_state = ERC721Receiver::unsafe_new_contract_state(); + ERC721Receiver::supportsInterface(@unsafe_state, interfaceId) } } -#[contract] +#[starknet::contract] mod SnakeERC721ReceiverPanicMock { - use openzeppelin::utils::serde::SpanSerde; use starknet::ContractAddress; - #[view] + #[storage] + struct Storage {} + + #[external(v0)] fn on_erc721_received( - operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span + self: @ContractState, + operator: ContractAddress, + from: ContractAddress, + token_id: u256, + data: Span ) -> felt252 { panic_with_felt252('Some error'); 3 } } -#[contract] +#[starknet::contract] mod CamelERC721ReceiverPanicMock { - use openzeppelin::utils::serde::SpanSerde; use starknet::ContractAddress; - #[view] + #[storage] + struct Storage {} + + #[external(v0)] fn onERC721Received( - operator: ContractAddress, from: ContractAddress, tokenId: u256, data: Span + self: @ContractState, + operator: ContractAddress, + from: ContractAddress, + tokenId: u256, + data: Span ) -> felt252 { panic_with_felt252('Some error'); 3 diff --git a/src/openzeppelin/tests/mocks/erc721_panic_mock.cairo b/src/openzeppelin/tests/mocks/erc721_panic_mock.cairo index b824bf125..6d172c379 100644 --- a/src/openzeppelin/tests/mocks/erc721_panic_mock.cairo +++ b/src/openzeppelin/tests/mocks/erc721_panic_mock.cairo @@ -5,146 +5,158 @@ // zero for ContractAddress // u256 { 3, 3 } for u256 -#[contract] +#[starknet::contract] mod SnakeERC721PanicMock { - use openzeppelin::utils::serde::SpanSerde; use starknet::ContractAddress; use zeroable::Zeroable; - // - // agnostic - // + #[storage] + struct Storage {} - #[view] - fn name() -> felt252 { + #[external(v0)] + fn name(self: @ContractState) -> felt252 { panic_with_felt252('Some error'); 3 } - #[view] - fn symbol() -> felt252 { + #[external(v0)] + fn symbol(self: @ContractState) -> felt252 { panic_with_felt252('Some error'); 3 } - #[external] - fn approve(to: ContractAddress, token_id: u256) { + #[external(v0)] + fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) { panic_with_felt252('Some error'); } - // - // snake - // - - #[view] - fn supports_interface(interface_id: felt252) -> bool { + #[external(v0)] + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { panic_with_felt252('Some error'); false } - #[view] - fn token_uri(token_id: u256) -> felt252 { + #[external(v0)] + fn token_uri(self: @ContractState, token_id: u256) -> felt252 { panic_with_felt252('Some error'); 3 } - #[view] - fn balance_of(account: ContractAddress) -> u256 { + #[external(v0)] + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { panic_with_felt252('Some error'); u256 { low: 3, high: 3 } } - #[view] - fn owner_of(token_id: u256) -> ContractAddress { + #[external(v0)] + fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress { panic_with_felt252('Some error'); Zeroable::zero() } - #[view] - fn get_approved(token_id: u256) -> ContractAddress { + #[external(v0)] + fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { panic_with_felt252('Some error'); Zeroable::zero() } - #[view] - fn is_approved_for_all(owner: ContractAddress, operator: ContractAddress) -> bool { + #[external(v0)] + fn is_approved_for_all( + self: @ContractState, owner: ContractAddress, operator: ContractAddress + ) -> bool { panic_with_felt252('Some error'); false } - #[external] - fn set_approval_for_all(operator: ContractAddress, approved: bool) { + #[external(v0)] + fn set_approval_for_all(ref self: ContractState, operator: ContractAddress, approved: bool) { panic_with_felt252('Some error'); } - #[external] - fn transfer_from(from: ContractAddress, to: ContractAddress, token_id: u256) { + #[external(v0)] + fn transfer_from( + ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 + ) { panic_with_felt252('Some error'); } - #[external] + #[external(v0)] fn safe_transfer_from( - from: ContractAddress, to: ContractAddress, token_id: u256, data: Span + ref self: ContractState, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span ) { panic_with_felt252('Some error'); } } -#[contract] +#[starknet::contract] mod CamelERC721PanicMock { - use openzeppelin::utils::serde::SpanSerde; use starknet::ContractAddress; use zeroable::Zeroable; - #[view] - fn supportsInterface(interfaceId: felt252) -> bool { + #[storage] + struct Storage {} + + #[external(v0)] + fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { panic_with_felt252('Some error'); false } - #[view] - fn tokenUri(tokenId: u256) -> felt252 { + #[external(v0)] + fn tokenUri(self: @ContractState, tokenId: u256) -> felt252 { panic_with_felt252('Some error'); 3 } - #[view] - fn balanceOf(account: ContractAddress) -> u256 { + #[external(v0)] + fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 { panic_with_felt252('Some error'); u256 { low: 3, high: 3 } } - #[view] - fn ownerOf(tokenId: u256) -> ContractAddress { + #[external(v0)] + fn ownerOf(self: @ContractState, tokenId: u256) -> ContractAddress { panic_with_felt252('Some error'); Zeroable::zero() } - #[view] - fn getApproved(tokenId: u256) -> ContractAddress { + #[external(v0)] + fn getApproved(self: @ContractState, tokenId: u256) -> ContractAddress { panic_with_felt252('Some error'); Zeroable::zero() } - #[view] - fn isApprovedForAll(owner: ContractAddress, operator: ContractAddress) -> bool { + #[external(v0)] + fn isApprovedForAll( + self: @ContractState, owner: ContractAddress, operator: ContractAddress + ) -> bool { panic_with_felt252('Some error'); false } - #[external] - fn setApprovalForAll(operator: ContractAddress, approved: bool) { + #[external(v0)] + fn setApprovalForAll(ref self: ContractState, operator: ContractAddress, approved: bool) { panic_with_felt252('Some error'); } - #[external] - fn transferFrom(from: ContractAddress, to: ContractAddress, tokenId: u256) { + #[external(v0)] + fn transferFrom( + ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256 + ) { panic_with_felt252('Some error'); } - #[external] + #[external(v0)] fn safeTransferFrom( - from: ContractAddress, to: ContractAddress, tokenId: u256, data: Span + ref self: ContractState, + from: ContractAddress, + to: ContractAddress, + tokenId: u256, + data: Span ) { panic_with_felt252('Some error'); } diff --git a/src/openzeppelin/tests/mocks/erc721_receiver.cairo b/src/openzeppelin/tests/mocks/erc721_receiver.cairo index aff07e7aa..673e4100e 100644 --- a/src/openzeppelin/tests/mocks/erc721_receiver.cairo +++ b/src/openzeppelin/tests/mocks/erc721_receiver.cairo @@ -1,37 +1,48 @@ const SUCCESS: felt252 = 123123; const FAILURE: felt252 = 456456; -#[contract] +#[starknet::contract] mod ERC721Receiver { + use array::SpanTrait; + use starknet::ContractAddress; + use openzeppelin::token::erc721::interface::IERC721Receiver; use openzeppelin::token::erc721::interface::IERC721ReceiverCamel; use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; - use openzeppelin::introspection::src5; + use openzeppelin::introspection::interface::ISRC5; + use openzeppelin::introspection::interface::ISRC5Camel; + use openzeppelin::introspection::src5::SRC5; - use array::SpanTrait; - use openzeppelin::utils::serde::SpanSerde; - use starknet::ContractAddress; + #[storage] + struct Storage {} #[constructor] - fn constructor() { - src5::SRC5::register_interface(IERC721_RECEIVER_ID); + fn constructor(ref self: ContractState) { + let mut unsafe_state = SRC5::unsafe_new_contract_state(); + SRC5::InternalImpl::register_interface(ref unsafe_state, IERC721_RECEIVER_ID); } - impl ISRC5Impl of src5::ISRC5 { - fn supports_interface(interface_id: felt252) -> bool { - src5::SRC5::supports_interface(interface_id) + impl ISRC5Impl of ISRC5 { + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { + let unsafe_state = SRC5::unsafe_new_contract_state(); + SRC5::SRC5Impl::supports_interface(@unsafe_state, interface_id) } } - impl ISRC5CamelImpl of src5::ISRC5Camel { - fn supportsInterface(interfaceId: felt252) -> bool { - src5::SRC5::supportsInterface(interfaceId) + impl ISRC5CamelImpl of ISRC5Camel { + fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { + let unsafe_state = SRC5::unsafe_new_contract_state(); + SRC5::SRC5CamelImpl::supportsInterface(@unsafe_state, interfaceId) } } - impl ERC721ReceiverImpl of IERC721Receiver { + impl ERC721ReceiverImpl of IERC721Receiver { fn on_erc721_received( - operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span + self: @ContractState, + operator: ContractAddress, + from: ContractAddress, + token_id: u256, + data: Span ) -> felt252 { if *data.at(0) == super::SUCCESS { IERC721_RECEIVER_ID @@ -41,39 +52,47 @@ mod ERC721Receiver { } } - impl ERC721ReceiverCamelImpl of IERC721ReceiverCamel { + impl ERC721ReceiverCamelImpl of IERC721ReceiverCamel { fn onERC721Received( - operator: ContractAddress, from: ContractAddress, tokenId: u256, data: Span + self: @ContractState, + operator: ContractAddress, + from: ContractAddress, + tokenId: u256, + data: Span ) -> felt252 { - ERC721ReceiverImpl::on_erc721_received(operator, from, tokenId, data) + ERC721ReceiverImpl::on_erc721_received(self, operator, from, tokenId, data) } } - #[view] - fn supports_interface(interface_id: felt252) -> bool { - ISRC5Impl::supports_interface(interface_id) + #[external(v0)] + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { + ISRC5Impl::supports_interface(self, interface_id) } - #[view] - fn supportsInterface(interfaceId: felt252) -> bool { - ISRC5CamelImpl::supportsInterface(interfaceId) + #[external(v0)] + fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { + ISRC5CamelImpl::supportsInterface(self, interfaceId) } - #[external] + #[external(v0)] fn on_erc721_received( - operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span + self: @ContractState, + operator: ContractAddress, + from: ContractAddress, + token_id: u256, + data: Span ) -> felt252 { - ERC721ReceiverImpl::on_erc721_received(operator, from, token_id, data) + ERC721ReceiverImpl::on_erc721_received(self, operator, from, token_id, data) } - #[external] + #[external(v0)] fn onERC721Received( - operator: ContractAddress, from: ContractAddress, tokenId: u256, data: Span + self: @ContractState, + operator: ContractAddress, + from: ContractAddress, + tokenId: u256, + data: Span ) -> felt252 { - ERC721ReceiverCamelImpl::onERC721Received(operator, from, tokenId, data) + ERC721ReceiverCamelImpl::onERC721Received(self, operator, from, tokenId, data) } } - - -#[contract] -mod ERC721NonReceiver {} diff --git a/src/openzeppelin/tests/mocks/non721_mock.cairo b/src/openzeppelin/tests/mocks/non721_mock.cairo deleted file mode 100644 index 05c7d140c..000000000 --- a/src/openzeppelin/tests/mocks/non721_mock.cairo +++ /dev/null @@ -1,7 +0,0 @@ -#[contract] -mod NonERC721 { - #[view] - fn nope() -> bool { - false - } -} diff --git a/src/openzeppelin/tests/mocks/snake721_mock.cairo b/src/openzeppelin/tests/mocks/snake721_mock.cairo index eca3d70cb..13335a52f 100644 --- a/src/openzeppelin/tests/mocks/snake721_mock.cairo +++ b/src/openzeppelin/tests/mocks/snake721_mock.cairo @@ -1,80 +1,103 @@ -#[contract] +#[starknet::contract] mod SnakeERC721Mock { use starknet::ContractAddress; use starknet::get_caller_address; use openzeppelin::token::erc721::ERC721; - use openzeppelin::utils::serde::SpanSerde; + use openzeppelin::token::erc721::ERC721::InternalImpl; + use openzeppelin::token::erc721::ERC721::ERC721Impl; + + #[storage] + struct Storage {} #[constructor] - fn constructor(name: felt252, symbol: felt252, token_id: u256, uri: felt252) { - ERC721::initializer(name, symbol); - ERC721::_mint(get_caller_address(), token_id); - ERC721::_set_token_uri(token_id, uri); + fn constructor( + ref self: ContractState, name: felt252, symbol: felt252, token_id: u256, uri: felt252 + ) { + let mut unsafe_state = ERC721::unsafe_new_contract_state(); + InternalImpl::initializer(ref unsafe_state, name, symbol); + InternalImpl::_mint(ref unsafe_state, get_caller_address(), token_id); + InternalImpl::_set_token_uri(ref unsafe_state, token_id, uri); } - // View - - #[view] - fn supports_interface(interface_id: felt252) -> bool { - ERC721::supports_interface(interface_id) + #[external(v0)] + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { + let unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721::SRC5Impl::supports_interface(@unsafe_state, interface_id) } - #[view] - fn name() -> felt252 { - ERC721::name() + #[external(v0)] + fn name(self: @ContractState) -> felt252 { + let unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721::ERC721MetadataImpl::name(@unsafe_state) } - #[view] - fn symbol() -> felt252 { - ERC721::symbol() + #[external(v0)] + fn symbol(self: @ContractState) -> felt252 { + let unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721::ERC721MetadataImpl::symbol(@unsafe_state) } - #[view] - fn token_uri(token_id: u256) -> felt252 { - ERC721::token_uri(token_id) + #[external(v0)] + fn token_uri(self: @ContractState, token_id: u256) -> felt252 { + let unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721::ERC721MetadataImpl::token_uri(@unsafe_state, token_id) } - #[view] - fn balance_of(account: ContractAddress) -> u256 { - ERC721::balance_of(account) + #[external(v0)] + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { + let unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721Impl::balance_of(@unsafe_state, account) } - #[view] - fn owner_of(token_id: u256) -> ContractAddress { - ERC721::owner_of(token_id) + #[external(v0)] + fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress { + let unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721Impl::owner_of(@unsafe_state, token_id) } - #[view] - fn get_approved(token_id: u256) -> ContractAddress { - ERC721::get_approved(token_id) + #[external(v0)] + fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { + let unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721Impl::get_approved(@unsafe_state, token_id) } - #[view] - fn is_approved_for_all(owner: ContractAddress, operator: ContractAddress) -> bool { - ERC721::is_approved_for_all(owner, operator) + #[external(v0)] + fn is_approved_for_all( + self: @ContractState, owner: ContractAddress, operator: ContractAddress + ) -> bool { + let unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721Impl::is_approved_for_all(@unsafe_state, owner, operator) } - // External - - #[external] - fn approve(to: ContractAddress, token_id: u256) { - ERC721::approve(to, token_id) + #[external(v0)] + fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) { + let mut unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721Impl::approve(ref unsafe_state, to, token_id) } - #[external] - fn set_approval_for_all(operator: ContractAddress, approved: bool) { - ERC721::set_approval_for_all(operator, approved) + #[external(v0)] + fn set_approval_for_all(ref self: ContractState, operator: ContractAddress, approved: bool) { + let mut unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721Impl::set_approval_for_all(ref unsafe_state, operator, approved) } - #[external] - fn transfer_from(from: ContractAddress, to: ContractAddress, token_id: u256) { - ERC721::transfer_from(from, to, token_id) + #[external(v0)] + fn transfer_from( + ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 + ) { + let mut unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721Impl::transfer_from(ref unsafe_state, from, to, token_id) } - #[external] + #[external(v0)] fn safe_transfer_from( - from: ContractAddress, to: ContractAddress, token_id: u256, data: Span + ref self: ContractState, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span ) { - ERC721::safe_transfer_from(from, to, token_id, data) + let mut unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721Impl::safe_transfer_from(ref unsafe_state, from, to, token_id, data) } } diff --git a/src/openzeppelin/tests/token.cairo b/src/openzeppelin/tests/token.cairo index ebb5dc5c4..ffee08cb1 100644 --- a/src/openzeppelin/tests/token.cairo +++ b/src/openzeppelin/tests/token.cairo @@ -1,5 +1,5 @@ -mod test_dual20; +// mod test_dual20; mod test_dual721; -mod test_erc20; +// mod test_erc20; mod test_erc721; mod test_dual721_receiver; diff --git a/src/openzeppelin/tests/token/test_dual20.cairo b/src/openzeppelin/tests/token/test_dual20.cairo index 1afa77c31..52d0f0906 100644 --- a/src/openzeppelin/tests/token/test_dual20.cairo +++ b/src/openzeppelin/tests/token/test_dual20.cairo @@ -6,7 +6,7 @@ use starknet::testing::set_contract_address; use openzeppelin::tests::mocks::camel20_mock::CamelERC20Mock; use openzeppelin::tests::mocks::erc20_panic::SnakeERC20Panic; use openzeppelin::tests::mocks::erc20_panic::CamelERC20Panic; -use openzeppelin::tests::mocks::non721_mock::NonERC721; +use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::mocks::snake20_mock::SnakeERC20Mock; use openzeppelin::token::erc20::dual20::DualERC20; use openzeppelin::token::erc20::dual20::DualERC20Trait; @@ -66,7 +66,7 @@ fn setup_camel() -> (DualERC20, IERC20CamelDispatcher) { fn setup_non_erc20() -> DualERC20 { let calldata = ArrayTrait::new(); - let target = utils::deploy(NonERC721::TEST_CLASS_HASH, calldata); + let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); DualERC20 { contract_address: target } } diff --git a/src/openzeppelin/tests/token/test_dual721.cairo b/src/openzeppelin/tests/token/test_dual721.cairo index b0e45e568..dfaa53bad 100644 --- a/src/openzeppelin/tests/token/test_dual721.cairo +++ b/src/openzeppelin/tests/token/test_dual721.cairo @@ -5,9 +5,9 @@ use starknet::testing::set_caller_address; use starknet::testing::set_contract_address; use openzeppelin::token::erc721::interface::IERC721_ID; use openzeppelin::token::erc721::interface::IERC721Dispatcher; -use openzeppelin::token::erc721::interface::IERC721CamelDispatcher; +use openzeppelin::token::erc721::interface::IERC721CamelOnlyDispatcher; use openzeppelin::token::erc721::interface::IERC721DispatcherTrait; -use openzeppelin::token::erc721::interface::IERC721CamelDispatcherTrait; +use openzeppelin::token::erc721::interface::IERC721CamelOnlyDispatcherTrait; use openzeppelin::token::erc721::dual721::DualCaseERC721Trait; use openzeppelin::token::erc721::dual721::DualCaseERC721; use openzeppelin::tests::mocks::snake721_mock::SnakeERC721Mock; @@ -43,7 +43,7 @@ fn OPERATOR() -> ContractAddress { contract_address_const::<40>() } fn DATA(success: bool) -> Span { - let mut data = ArrayTrait::new(); + let mut data = array![]; if success { data.append_serde(SUCCESS); } else { @@ -57,7 +57,7 @@ fn DATA(success: bool) -> Span { // fn setup_snake() -> (DualCaseERC721, IERC721Dispatcher) { - let mut calldata = ArrayTrait::new(); + let mut calldata = array![]; calldata.append_serde(NAME); calldata.append_serde(SYMBOL); calldata.append_serde(TOKEN_ID); @@ -67,8 +67,8 @@ fn setup_snake() -> (DualCaseERC721, IERC721Dispatcher) { (DualCaseERC721 { contract_address: target }, IERC721Dispatcher { contract_address: target }) } -fn setup_camel() -> (DualCaseERC721, IERC721CamelDispatcher) { - let mut calldata = ArrayTrait::new(); +fn setup_camel() -> (DualCaseERC721, IERC721CamelOnlyDispatcher) { + let mut calldata = array![]; calldata.append_serde(NAME); calldata.append_serde(SYMBOL); calldata.append_serde(TOKEN_ID); @@ -77,19 +77,19 @@ fn setup_camel() -> (DualCaseERC721, IERC721CamelDispatcher) { let target = utils::deploy(CamelERC721Mock::TEST_CLASS_HASH, calldata); ( DualCaseERC721 { contract_address: target }, - IERC721CamelDispatcher { contract_address: target } + IERC721CamelOnlyDispatcher { contract_address: target } ) } fn setup_non_erc721() -> DualCaseERC721 { - let calldata = ArrayTrait::new(); + let calldata = array![]; let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); DualCaseERC721 { contract_address: target } } fn setup_erc721_panic() -> (DualCaseERC721, DualCaseERC721) { - let snake_target = utils::deploy(SnakeERC721PanicMock::TEST_CLASS_HASH, ArrayTrait::new()); - let camel_target = utils::deploy(CamelERC721PanicMock::TEST_CLASS_HASH, ArrayTrait::new()); + let snake_target = utils::deploy(SnakeERC721PanicMock::TEST_CLASS_HASH, array![]); + let camel_target = utils::deploy(CamelERC721PanicMock::TEST_CLASS_HASH, array![]); ( DualCaseERC721 { contract_address: snake_target }, DualCaseERC721 { contract_address: camel_target } @@ -97,7 +97,7 @@ fn setup_erc721_panic() -> (DualCaseERC721, DualCaseERC721) { } fn setup_receiver() -> ContractAddress { - utils::deploy(ERC721Receiver::TEST_CLASS_HASH, ArrayTrait::new()) + utils::deploy(ERC721Receiver::TEST_CLASS_HASH, array![]) } // @@ -155,7 +155,7 @@ fn test_dual_symbol_exists_and_panics() { } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_dual_approve() { let (snake_dispatcher, snake_target) = setup_snake(); set_contract_address(OWNER()); @@ -475,9 +475,9 @@ fn test_dual_safeTransferFrom_exists_and_panics() { #[test] #[available_gas(2000000)] fn test_dual_getApproved() { - let (dispatcher, target) = setup_camel(); + let (dispatcher, _) = setup_camel(); set_contract_address(OWNER()); - target.approve(SPENDER(), TOKEN_ID); + dispatcher.approve(SPENDER(), TOKEN_ID); assert(dispatcher.get_approved(TOKEN_ID) == SPENDER(), 'Should return approval'); } diff --git a/src/openzeppelin/tests/token/test_erc721.cairo b/src/openzeppelin/tests/token/test_erc721.cairo index 5037ae251..5c7fdc4f9 100644 --- a/src/openzeppelin/tests/token/test_erc721.cairo +++ b/src/openzeppelin/tests/token/test_erc721.cairo @@ -1,16 +1,3 @@ -use openzeppelin::account::Account; -use openzeppelin::introspection::src5; -use openzeppelin::token::erc721; -use openzeppelin::token::erc721::ERC721; - -use openzeppelin::tests::utils; -use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; -use openzeppelin::tests::mocks::erc721_receiver::ERC721NonReceiver; -use openzeppelin::tests::mocks::erc721_receiver::SUCCESS; -use openzeppelin::tests::mocks::erc721_receiver::FAILURE; -use openzeppelin::tests::mocks::camel_account_mock::CamelAccountMock; -use openzeppelin::tests::mocks::dual721_receiver_mocks::CamelERC721ReceiverMock; - use starknet::contract_address_const; use starknet::ContractAddress; use starknet::testing::set_caller_address; @@ -20,12 +7,38 @@ use array::ArrayTrait; use traits::Into; use zeroable::Zeroable; +use openzeppelin::account::Account; +use openzeppelin::introspection; +use openzeppelin::introspection::src5; +use openzeppelin::token::erc721; +use openzeppelin::token::erc721::ERC721; +use openzeppelin::token::erc721::ERC721::ERC721CamelOnlyImpl; +use openzeppelin::token::erc721::ERC721::ERC721Impl; +use openzeppelin::token::erc721::ERC721::ERC721MetadataCamelOnlyImpl; +use openzeppelin::token::erc721::ERC721::ERC721MetadataImpl; +use openzeppelin::token::erc721::ERC721::InternalImpl; +use openzeppelin::token::erc721::ERC721::SRC5CamelImpl; +use openzeppelin::token::erc721::ERC721::SRC5Impl; +use openzeppelin::tests::mocks::camel_account_mock::CamelAccountMock; +use openzeppelin::tests::mocks::dual721_receiver_mocks::CamelERC721ReceiverMock; +use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; +use openzeppelin::tests::mocks::erc721_receiver::SUCCESS; +use openzeppelin::tests::mocks::erc721_receiver::FAILURE; +use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; +use openzeppelin::tests::utils; + +use ERC721::_owners::InternalContractStateTrait as OwnersTrait; +use ERC721::_token_approvals::InternalContractStateTrait as TokenApprovalsTrait; + const NAME: felt252 = 111; const SYMBOL: felt252 = 222; const URI: felt252 = 333; const TOKEN_ID: u256 = 7; const PUBKEY: felt252 = 444; +fn STATE() -> ERC721::ContractState { + ERC721::contract_state_for_testing() +} fn ZERO() -> ContractAddress { Zeroable::zero() } @@ -46,7 +59,7 @@ fn OTHER() -> ContractAddress { } fn DATA(success: bool) -> Span { - let mut data = ArrayTrait::new(); + let mut data = array![]; if success { data.append(SUCCESS); } else { @@ -59,28 +72,28 @@ fn DATA(success: bool) -> Span { // Setup // -fn setup() { - ERC721::initializer(NAME, SYMBOL); - ERC721::_mint(OWNER(), TOKEN_ID); +fn setup() -> ERC721::ContractState { + let mut state = STATE(); + InternalImpl::initializer(ref state, NAME, SYMBOL); + InternalImpl::_mint(ref state, OWNER(), TOKEN_ID); + state } fn setup_receiver() -> ContractAddress { - utils::deploy(ERC721Receiver::TEST_CLASS_HASH, ArrayTrait::new()) + utils::deploy(ERC721Receiver::TEST_CLASS_HASH, array![]) } fn setup_camel_receiver() -> ContractAddress { - utils::deploy(CamelERC721ReceiverMock::TEST_CLASS_HASH, ArrayTrait::new()) + utils::deploy(CamelERC721ReceiverMock::TEST_CLASS_HASH, array![]) } fn setup_account() -> ContractAddress { - let mut calldata = ArrayTrait::new(); - calldata.append(PUBKEY); + let mut calldata = array![PUBKEY]; utils::deploy(Account::TEST_CLASS_HASH, calldata) } fn setup_camel_account() -> ContractAddress { - let mut calldata = ArrayTrait::new(); - calldata.append(PUBKEY); + let mut calldata = array![PUBKEY]; utils::deploy(CamelAccountMock::TEST_CLASS_HASH, calldata) } @@ -89,35 +102,49 @@ fn setup_camel_account() -> ContractAddress { // #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_constructor() { - ERC721::constructor(NAME, SYMBOL); + let mut state = STATE(); + ERC721::constructor(ref state, NAME, SYMBOL); - assert(ERC721::name() == NAME, 'Name should be NAME'); - assert(ERC721::symbol() == SYMBOL, 'Symbol should be SYMBOL'); - assert(ERC721::balance_of(OWNER()) == 0, 'Balance should be zero'); + assert(ERC721MetadataImpl::name(@state) == NAME, 'Name should be NAME'); + assert(ERC721MetadataImpl::symbol(@state) == SYMBOL, 'Symbol should be SYMBOL'); + assert(ERC721Impl::balance_of(@state, OWNER()) == 0, 'Balance should be zero'); - assert(ERC721::supports_interface(erc721::interface::IERC721_ID), 'Missing interface ID'); assert( - ERC721::supports_interface(erc721::interface::IERC721_METADATA_ID), 'missing interface ID' + SRC5Impl::supports_interface(@state, erc721::interface::IERC721_ID), 'Missing interface ID' + ); + assert( + SRC5Impl::supports_interface(@state, erc721::interface::IERC721_METADATA_ID), + 'missing interface ID' + ); + assert( + SRC5Impl::supports_interface(@state, introspection::interface::ISRC5_ID), + 'missing interface ID' ); - assert(ERC721::supports_interface(src5::ISRC5_ID), 'missing interface ID'); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_initialize() { - ERC721::initializer(NAME, SYMBOL); + let mut state = STATE(); + InternalImpl::initializer(ref state, NAME, SYMBOL); - assert(ERC721::name() == NAME, 'Name should be NAME'); - assert(ERC721::symbol() == SYMBOL, 'Symbol should be SYMBOL'); - assert(ERC721::balance_of(OWNER()) == 0, 'Balance should be zero'); + assert(ERC721MetadataImpl::name(@state) == NAME, 'Name should be NAME'); + assert(ERC721MetadataImpl::symbol(@state) == SYMBOL, 'Symbol should be SYMBOL'); + assert(ERC721Impl::balance_of(@state, OWNER()) == 0, 'Balance should be zero'); - assert(ERC721::supports_interface(erc721::interface::IERC721_ID), 'Missing interface ID'); assert( - ERC721::supports_interface(erc721::interface::IERC721_METADATA_ID), 'missing interface ID' + SRC5Impl::supports_interface(@state, erc721::interface::IERC721_ID), 'Missing interface ID' + ); + assert( + SRC5Impl::supports_interface(@state, erc721::interface::IERC721_METADATA_ID), + 'missing interface ID' + ); + assert( + SRC5Impl::supports_interface(@state, introspection::interface::ISRC5_ID), + 'missing interface ID' ); - assert(ERC721::supports_interface(src5::ISRC5_ID), 'missing interface ID'); } // @@ -125,76 +152,78 @@ fn test_initialize() { // #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_balance_of() { - setup(); - assert(ERC721::balance_of(OWNER()) == 1, 'Should return balance'); + let state = setup(); + assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Should return balance'); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid account', ))] fn test_balance_of_zero() { - ERC721::balance_of(ZERO()); + ERC721Impl::balance_of(@STATE(), ZERO()); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_owner_of() { - setup(); - assert(ERC721::owner_of(TOKEN_ID) == OWNER(), 'Should return owner'); + let state = setup(); + assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Should return owner'); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] fn test_owner_of_non_minted() { - ERC721::owner_of(u256_from_felt252(7)); + ERC721Impl::owner_of(@STATE(), u256_from_felt252(7)); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] fn test_token_uri_non_minted() { - ERC721::token_uri(u256_from_felt252(7)); + ERC721MetadataImpl::token_uri(@STATE(), u256_from_felt252(7)); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_get_approved() { - setup(); + let mut state = setup(); let spender = SPENDER(); let token_id = TOKEN_ID; - assert(ERC721::get_approved(token_id) == ZERO(), 'Should return non-approval'); - ERC721::_approve(spender, token_id); - assert(ERC721::get_approved(token_id) == spender, 'Should return approval'); + assert(ERC721Impl::get_approved(@state, token_id) == ZERO(), 'Should return non-approval'); + InternalImpl::_approve(ref state, spender, token_id); + assert(ERC721Impl::get_approved(@state, token_id) == spender, 'Should return approval'); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] fn test_get_approved_nonexistent() { - ERC721::get_approved(u256_from_felt252(7)); + ERC721Impl::get_approved(@STATE(), u256_from_felt252(7)); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test__exists() { + let mut state = STATE(); let zero = ZERO(); let token_id = TOKEN_ID; - assert(!ERC721::_exists(token_id), 'Token should not exist'); - assert(ERC721::_owners::read(token_id) == zero, 'Invalid owner'); - ERC721::_mint(RECIPIENT(), token_id); + assert(!InternalImpl::_exists(@state, token_id), 'Token should not exist'); + assert(state._owners.read(token_id) == zero, 'Invalid owner'); + + InternalImpl::_mint(ref state, RECIPIENT(), token_id); - assert(ERC721::_exists(token_id), 'Token should exist'); - assert(ERC721::_owners::read(token_id) == RECIPIENT(), 'Invalid owner'); + assert(InternalImpl::_exists(@state, token_id), 'Token should exist'); + assert(state._owners.read(token_id) == RECIPIENT(), 'Invalid owner'); - ERC721::_burn(token_id); + InternalImpl::_burn(ref state, token_id); - assert(!ERC721::_exists(token_id), 'Token should not exist'); - assert(ERC721::_owners::read(token_id) == zero, 'Invalid owner'); + assert(!InternalImpl::_exists(@state, token_id), 'Token should not exist'); + assert(state._owners.read(token_id) == zero, 'Invalid owner'); } // @@ -202,78 +231,84 @@ fn test__exists() { // #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_approve_from_owner() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC721::approve(SPENDER(), TOKEN_ID); - assert(ERC721::get_approved(TOKEN_ID) == SPENDER(), 'Spender not approved correctly'); + ERC721Impl::approve(ref state, SPENDER(), TOKEN_ID); + assert( + ERC721Impl::get_approved(@state, TOKEN_ID) == SPENDER(), 'Spender not approved correctly' + ); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_approve_from_operator() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC721::set_approval_for_all(OPERATOR(), true); + ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); set_caller_address(OPERATOR()); - ERC721::approve(SPENDER(), TOKEN_ID); - assert(ERC721::get_approved(TOKEN_ID) == SPENDER(), 'Spender not approved correctly'); + ERC721Impl::approve(ref state, SPENDER(), TOKEN_ID); + assert( + ERC721Impl::get_approved(@state, TOKEN_ID) == SPENDER(), 'Spender not approved correctly' + ); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: unauthorized caller', ))] fn test_approve_from_unauthorized() { - setup(); + let mut state = setup(); set_caller_address(OTHER()); - ERC721::approve(SPENDER(), TOKEN_ID); + ERC721Impl::approve(ref state, SPENDER(), TOKEN_ID); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: approval to owner', ))] fn test_approve_to_owner() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC721::approve(OWNER(), TOKEN_ID); + ERC721Impl::approve(ref state, OWNER(), TOKEN_ID); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] fn test_approve_nonexistent() { - ERC721::approve(SPENDER(), TOKEN_ID); + let mut state = STATE(); + ERC721Impl::approve(ref state, SPENDER(), TOKEN_ID); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test__approve() { - setup(); - - ERC721::_approve(SPENDER(), TOKEN_ID); - assert(ERC721::get_approved(TOKEN_ID) == SPENDER(), 'Spender not approved correctly'); + let mut state = setup(); + InternalImpl::_approve(ref state, SPENDER(), TOKEN_ID); + assert( + ERC721Impl::get_approved(@state, TOKEN_ID) == SPENDER(), 'Spender not approved correctly' + ); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: approval to owner', ))] fn test__approve_to_owner() { - setup(); - - ERC721::_approve(OWNER(), TOKEN_ID); + let mut state = setup(); + InternalImpl::_approve(ref state, OWNER(), TOKEN_ID); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] fn test__approve_nonexistent() { - ERC721::_approve(SPENDER(), TOKEN_ID); + let mut state = STATE(); + InternalImpl::_approve(ref state, SPENDER(), TOKEN_ID); } // @@ -281,58 +316,77 @@ fn test__approve_nonexistent() { // #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_set_approval_for_all() { + let mut state = STATE(); set_caller_address(OWNER()); - assert(!ERC721::is_approved_for_all(OWNER(), OPERATOR()), 'Invalid default value'); - ERC721::set_approval_for_all(OPERATOR(), true); - assert(ERC721::is_approved_for_all(OWNER(), OPERATOR()), 'Operator not approved correctly'); + assert(!ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), 'Invalid default value'); + + ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); + assert( + ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), + 'Operator not approved correctly' + ); - ERC721::set_approval_for_all(OPERATOR(), false); - assert(!ERC721::is_approved_for_all(OWNER(), OPERATOR()), 'Approval not revoked correctly'); + ERC721Impl::set_approval_for_all(ref state, OPERATOR(), false); + assert( + !ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), + 'Approval not revoked correctly' + ); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: self approval', ))] fn test_set_approval_for_all_owner_equal_operator_true() { + let mut state = STATE(); set_caller_address(OWNER()); - ERC721::set_approval_for_all(OWNER(), true); + ERC721Impl::set_approval_for_all(ref state, OWNER(), true); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: self approval', ))] fn test_set_approval_for_all_owner_equal_operator_false() { + let mut state = STATE(); set_caller_address(OWNER()); - ERC721::set_approval_for_all(OWNER(), false); + ERC721Impl::set_approval_for_all(ref state, OWNER(), false); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test__set_approval_for_all() { - assert(!ERC721::is_approved_for_all(OWNER(), OPERATOR()), 'Invalid default value'); + let mut state = STATE(); + assert(!ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), 'Invalid default value'); - ERC721::_set_approval_for_all(OWNER(), OPERATOR(), true); - assert(ERC721::is_approved_for_all(OWNER(), OPERATOR()), 'Operator not approved correctly'); + InternalImpl::_set_approval_for_all(ref state, OWNER(), OPERATOR(), true); + assert( + ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), + 'Operator not approved correctly' + ); - ERC721::_set_approval_for_all(OWNER(), OPERATOR(), false); - assert(!ERC721::is_approved_for_all(OWNER(), OPERATOR()), 'Operator not approved correctly'); + InternalImpl::_set_approval_for_all(ref state, OWNER(), OPERATOR(), false); + assert( + !ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), + 'Operator not approved correctly' + ); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: self approval', ))] fn test__set_approval_for_all_owner_equal_operator_true() { - ERC721::_set_approval_for_all(OWNER(), OWNER(), true); + let mut state = STATE(); + InternalImpl::_set_approval_for_all(ref state, OWNER(), OWNER(), true); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: self approval', ))] fn test__set_approval_for_all_owner_equal_operator_false() { - ERC721::_set_approval_for_all(OWNER(), OWNER(), false); + let mut state = STATE(); + InternalImpl::_set_approval_for_all(ref state, OWNER(), OWNER(), false); } // @@ -340,147 +394,148 @@ fn test__set_approval_for_all_owner_equal_operator_false() { // #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_transfer_from_owner() { - setup(); + let mut state = setup(); let token_id = TOKEN_ID; let owner = OWNER(); let recipient = RECIPIENT(); // set approval to check reset - ERC721::_approve(OTHER(), token_id); + InternalImpl::_approve(ref state, OTHER(), token_id); assert_state_before_transfer(token_id, owner, recipient); - assert(ERC721::get_approved(token_id) == OTHER(), 'Approval not implicitly reset'); + assert(ERC721Impl::get_approved(@state, token_id) == OTHER(), 'Approval not implicitly reset'); set_caller_address(owner); - ERC721::transfer_from(owner, recipient, token_id); + ERC721Impl::transfer_from(ref state, owner, recipient, token_id); assert_state_after_transfer(token_id, owner, recipient); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_transferFrom_owner() { - setup(); + let mut state = setup(); let token_id = TOKEN_ID; let owner = OWNER(); let recipient = RECIPIENT(); // set approval to check reset - ERC721::_approve(OTHER(), token_id); + InternalImpl::_approve(ref state, OTHER(), token_id); assert_state_before_transfer(token_id, owner, recipient); - assert(ERC721::get_approved(token_id) == OTHER(), 'Approval not implicitly reset'); + assert(ERC721Impl::get_approved(@state, token_id) == OTHER(), 'Approval not implicitly reset'); set_caller_address(owner); - ERC721::transferFrom(owner, recipient, token_id); + ERC721CamelOnlyImpl::transferFrom(ref state, owner, recipient, token_id); assert_state_after_transfer(token_id, owner, recipient); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] fn test_transfer_from_nonexistent() { - ERC721::transfer_from(ZERO(), RECIPIENT(), TOKEN_ID); + let mut state = STATE(); + ERC721Impl::transfer_from(ref state, ZERO(), RECIPIENT(), TOKEN_ID); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] fn test_transferFrom_nonexistent() { - ERC721::transferFrom(ZERO(), RECIPIENT(), TOKEN_ID); + let mut state = STATE(); + ERC721CamelOnlyImpl::transferFrom(ref state, ZERO(), RECIPIENT(), TOKEN_ID); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid receiver', ))] fn test_transfer_from_to_zero() { - setup(); - + let mut state = setup(); set_caller_address(OWNER()); - ERC721::transfer_from(OWNER(), ZERO(), TOKEN_ID); + ERC721Impl::transfer_from(ref state, OWNER(), ZERO(), TOKEN_ID); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid receiver', ))] fn test_transferFrom_to_zero() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC721::transferFrom(OWNER(), ZERO(), TOKEN_ID); + ERC721CamelOnlyImpl::transferFrom(ref state, OWNER(), ZERO(), TOKEN_ID); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_transfer_from_to_owner() { - setup(); + let mut state = setup(); - assert(ERC721::owner_of(TOKEN_ID) == OWNER(), 'Ownership before'); - assert(ERC721::balance_of(OWNER()) == 1, 'Balance of owner before'); + assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership before'); + assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner before'); set_caller_address(OWNER()); - ERC721::transfer_from(OWNER(), OWNER(), TOKEN_ID); + ERC721Impl::transfer_from(ref state, OWNER(), OWNER(), TOKEN_ID); - assert(ERC721::owner_of(TOKEN_ID) == OWNER(), 'Ownership after'); - assert(ERC721::balance_of(OWNER()) == 1, 'Balance of owner after'); + assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership after'); + assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner after'); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_transferFrom_to_owner() { - setup(); + let mut state = setup(); - assert(ERC721::owner_of(TOKEN_ID) == OWNER(), 'Ownership before'); - assert(ERC721::balance_of(OWNER()) == 1, 'Balance of owner before'); + assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership before'); + assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner before'); set_caller_address(OWNER()); - ERC721::transferFrom(OWNER(), OWNER(), TOKEN_ID); + ERC721CamelOnlyImpl::transferFrom(ref state, OWNER(), OWNER(), TOKEN_ID); - assert(ERC721::owner_of(TOKEN_ID) == OWNER(), 'Ownership after'); - assert(ERC721::balance_of(OWNER()) == 1, 'Balance of owner after'); + assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership after'); + assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner after'); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_transfer_from_approved() { - setup(); + let mut state = setup(); let token_id = TOKEN_ID; let owner = OWNER(); let recipient = RECIPIENT(); assert_state_before_transfer(token_id, owner, recipient); set_caller_address(owner); - ERC721::approve(OPERATOR(), token_id); + ERC721Impl::approve(ref state, OPERATOR(), token_id); set_caller_address(OPERATOR()); - ERC721::transfer_from(owner, recipient, token_id); + ERC721Impl::transfer_from(ref state, owner, recipient, token_id); assert_state_after_transfer(token_id, owner, recipient); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_transferFrom_approved() { - setup(); + let mut state = setup(); let token_id = TOKEN_ID; let owner = OWNER(); let recipient = RECIPIENT(); assert_state_before_transfer(token_id, owner, recipient); set_caller_address(owner); - ERC721::approve(OPERATOR(), token_id); + ERC721Impl::approve(ref state, OPERATOR(), token_id); set_caller_address(OPERATOR()); - ERC721::transferFrom(owner, recipient, token_id); + ERC721CamelOnlyImpl::transferFrom(ref state, owner, recipient, token_id); assert_state_after_transfer(token_id, owner, recipient); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_transfer_from_approved_for_all() { - setup(); + let mut state = setup(); let token_id = TOKEN_ID; let owner = OWNER(); let recipient = RECIPIENT(); @@ -488,18 +543,18 @@ fn test_transfer_from_approved_for_all() { assert_state_before_transfer(token_id, owner, recipient); set_caller_address(owner); - ERC721::set_approval_for_all(OPERATOR(), true); + ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); set_caller_address(OPERATOR()); - ERC721::transfer_from(owner, recipient, token_id); + ERC721Impl::transfer_from(ref state, owner, recipient, token_id); assert_state_after_transfer(token_id, owner, recipient); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_transferFrom_approved_for_all() { - setup(); + let mut state = setup(); let token_id = TOKEN_ID; let owner = OWNER(); let recipient = RECIPIENT(); @@ -507,32 +562,30 @@ fn test_transferFrom_approved_for_all() { assert_state_before_transfer(token_id, owner, recipient); set_caller_address(owner); - ERC721::set_approval_for_all(OPERATOR(), true); + ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); set_caller_address(OPERATOR()); - ERC721::transferFrom(owner, recipient, token_id); + ERC721CamelOnlyImpl::transferFrom(ref state, owner, recipient, token_id); assert_state_after_transfer(token_id, owner, recipient); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: unauthorized caller', ))] fn test_transfer_from_unauthorized() { - setup(); - + let mut state = setup(); set_caller_address(OTHER()); - ERC721::transfer_from(OWNER(), RECIPIENT(), TOKEN_ID); + ERC721Impl::transfer_from(ref state, OWNER(), RECIPIENT(), TOKEN_ID); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: unauthorized caller', ))] fn test_transferFrom_unauthorized() { - setup(); - + let mut state = setup(); set_caller_address(OTHER()); - ERC721::transferFrom(OWNER(), RECIPIENT(), TOKEN_ID); + ERC721CamelOnlyImpl::transferFrom(ref state, OWNER(), RECIPIENT(), TOKEN_ID); } // @@ -540,9 +593,9 @@ fn test_transferFrom_unauthorized() { // #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_safe_transfer_from_to_account() { - setup(); + let mut state = setup(); let account = setup_account(); let token_id = TOKEN_ID; let owner = OWNER(); @@ -550,15 +603,15 @@ fn test_safe_transfer_from_to_account() { assert_state_before_transfer(token_id, owner, account); set_caller_address(owner); - ERC721::safe_transfer_from(owner, account, token_id, DATA(true)); + ERC721Impl::safe_transfer_from(ref state, owner, account, token_id, DATA(true)); assert_state_after_transfer(token_id, owner, account); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_safeTransferFrom_to_account() { - setup(); + let mut state = setup(); let account = setup_account(); let token_id = TOKEN_ID; let owner = OWNER(); @@ -566,15 +619,15 @@ fn test_safeTransferFrom_to_account() { assert_state_before_transfer(token_id, owner, account); set_caller_address(owner); - ERC721::safeTransferFrom(owner, account, token_id, DATA(true)); + ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, account, token_id, DATA(true)); assert_state_after_transfer(token_id, owner, account); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_safe_transfer_from_to_account_camel() { - setup(); + let mut state = setup(); let account = setup_camel_account(); let token_id = TOKEN_ID; let owner = OWNER(); @@ -582,15 +635,15 @@ fn test_safe_transfer_from_to_account_camel() { assert_state_before_transfer(token_id, owner, account); set_caller_address(owner); - ERC721::safe_transfer_from(owner, account, token_id, DATA(true)); + ERC721Impl::safe_transfer_from(ref state, owner, account, token_id, DATA(true)); assert_state_after_transfer(token_id, owner, account); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_safeTransferFrom_to_account_camel() { - setup(); + let mut state = setup(); let account = setup_camel_account(); let token_id = TOKEN_ID; let owner = OWNER(); @@ -598,15 +651,15 @@ fn test_safeTransferFrom_to_account_camel() { assert_state_before_transfer(token_id, owner, account); set_caller_address(owner); - ERC721::safeTransferFrom(owner, account, token_id, DATA(true)); + ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, account, token_id, DATA(true)); assert_state_after_transfer(token_id, owner, account); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_safe_transfer_from_to_receiver() { - setup(); + let mut state = setup(); let receiver = setup_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); @@ -614,15 +667,15 @@ fn test_safe_transfer_from_to_receiver() { assert_state_before_transfer(token_id, owner, receiver); set_caller_address(owner); - ERC721::safe_transfer_from(owner, receiver, token_id, DATA(true)); + ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); assert_state_after_transfer(token_id, owner, receiver); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_safeTransferFrom_to_receiver() { - setup(); + let mut state = setup(); let receiver = setup_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); @@ -630,15 +683,15 @@ fn test_safeTransferFrom_to_receiver() { assert_state_before_transfer(token_id, owner, receiver); set_caller_address(owner); - ERC721::safeTransferFrom(owner, receiver, token_id, DATA(true)); + ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); assert_state_after_transfer(token_id, owner, receiver); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_safe_transfer_from_to_receiver_camel() { - setup(); + let mut state = setup(); let receiver = setup_camel_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); @@ -646,15 +699,15 @@ fn test_safe_transfer_from_to_receiver_camel() { assert_state_before_transfer(token_id, owner, receiver); set_caller_address(owner); - ERC721::safe_transfer_from(owner, receiver, token_id, DATA(true)); + ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); assert_state_after_transfer(token_id, owner, receiver); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_safeTransferFrom_to_receiver_camel() { - setup(); + let mut state = setup(); let receiver = setup_camel_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); @@ -662,199 +715,203 @@ fn test_safeTransferFrom_to_receiver_camel() { assert_state_before_transfer(token_id, owner, receiver); set_caller_address(owner); - ERC721::safeTransferFrom(owner, receiver, token_id, DATA(true)); + ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); assert_state_after_transfer(token_id, owner, receiver); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: safe transfer failed', ))] fn test_safe_transfer_from_to_receiver_failure() { - setup(); + let mut state = setup(); let receiver = setup_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); set_caller_address(owner); - ERC721::safe_transfer_from(owner, receiver, token_id, DATA(false)); + ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(false)); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: safe transfer failed', ))] fn test_safeTransferFrom_to_receiver_failure() { - setup(); + let mut state = setup(); let receiver = setup_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); set_caller_address(owner); - ERC721::safeTransferFrom(owner, receiver, token_id, DATA(false)); + ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(false)); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: safe transfer failed', ))] fn test_safe_transfer_from_to_receiver_failure_camel() { - setup(); + let mut state = setup(); let receiver = setup_camel_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); set_caller_address(owner); - ERC721::safe_transfer_from(owner, receiver, token_id, DATA(false)); + ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(false)); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: safe transfer failed', ))] fn test_safeTransferFrom_to_receiver_failure_camel() { - setup(); + let mut state = setup(); let receiver = setup_camel_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); set_caller_address(owner); - ERC721::safeTransferFrom(owner, receiver, token_id, DATA(false)); + ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(false)); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] fn test_safe_transfer_from_to_non_receiver() { - setup(); - let recipient = utils::deploy(ERC721NonReceiver::TEST_CLASS_HASH, ArrayTrait::new()); + let mut state = setup(); + let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); let token_id = TOKEN_ID; let owner = OWNER(); set_caller_address(owner); - ERC721::safe_transfer_from(owner, recipient, token_id, DATA(true)); + ERC721Impl::safe_transfer_from(ref state, owner, recipient, token_id, DATA(true)); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] fn test_safeTransferFrom_to_non_receiver() { - setup(); - let recipient = utils::deploy(ERC721NonReceiver::TEST_CLASS_HASH, ArrayTrait::new()); + let mut state = setup(); + let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); let token_id = TOKEN_ID; let owner = OWNER(); set_caller_address(owner); - ERC721::safeTransferFrom(owner, recipient, token_id, DATA(true)); + ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, recipient, token_id, DATA(true)); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] fn test_safe_transfer_from_nonexistent() { - ERC721::safe_transfer_from(ZERO(), RECIPIENT(), TOKEN_ID, DATA(true)); + let mut state = STATE(); + ERC721Impl::safe_transfer_from(ref state, ZERO(), RECIPIENT(), TOKEN_ID, DATA(true)); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] fn test_safeTransferFrom_nonexistent() { - ERC721::safeTransferFrom(ZERO(), RECIPIENT(), TOKEN_ID, DATA(true)); + let mut state = STATE(); + ERC721CamelOnlyImpl::safeTransferFrom(ref state, ZERO(), RECIPIENT(), TOKEN_ID, DATA(true)); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid receiver', ))] fn test_safe_transfer_from_to_zero() { - setup(); - + let mut state = setup(); set_caller_address(OWNER()); - ERC721::safe_transfer_from(OWNER(), ZERO(), TOKEN_ID, DATA(true)); + ERC721Impl::safe_transfer_from(ref state, OWNER(), ZERO(), TOKEN_ID, DATA(true)); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid receiver', ))] fn test_safeTransferFrom_to_zero() { - setup(); - + let mut state = setup(); set_caller_address(OWNER()); - ERC721::safeTransferFrom(OWNER(), ZERO(), TOKEN_ID, DATA(true)); + ERC721CamelOnlyImpl::safeTransferFrom(ref state, OWNER(), ZERO(), TOKEN_ID, DATA(true)); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_safe_transfer_from_to_owner() { + let mut state = STATE(); let token_id = TOKEN_ID; let owner = setup_receiver(); - ERC721::initializer(NAME, SYMBOL); - ERC721::_mint(owner, token_id); + InternalImpl::initializer(ref state, NAME, SYMBOL); + InternalImpl::_mint(ref state, owner, token_id); - assert(ERC721::owner_of(token_id) == owner, 'Ownership before'); - assert(ERC721::balance_of(owner) == 1, 'Balance of owner before'); + assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); + assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); set_caller_address(owner); - ERC721::safe_transfer_from(owner, owner, token_id, DATA(true)); + ERC721Impl::safe_transfer_from(ref state, owner, owner, token_id, DATA(true)); - assert(ERC721::owner_of(token_id) == owner, 'Ownership after'); - assert(ERC721::balance_of(owner) == 1, 'Balance of owner after'); + assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership after'); + assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner after'); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_safeTransferFrom_to_owner() { + let mut state = STATE(); let token_id = TOKEN_ID; let owner = setup_receiver(); - ERC721::initializer(NAME, SYMBOL); - ERC721::_mint(owner, token_id); + InternalImpl::initializer(ref state, NAME, SYMBOL); + InternalImpl::_mint(ref state, owner, token_id); - assert(ERC721::owner_of(token_id) == owner, 'Ownership before'); - assert(ERC721::balance_of(owner) == 1, 'Balance of owner before'); + assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); + assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); set_caller_address(owner); - ERC721::safeTransferFrom(owner, owner, token_id, DATA(true)); + ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, owner, token_id, DATA(true)); - assert(ERC721::owner_of(token_id) == owner, 'Ownership after'); - assert(ERC721::balance_of(owner) == 1, 'Balance of owner after'); + assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership after'); + assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner after'); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_safe_transfer_from_to_owner_camel() { + let mut state = STATE(); let token_id = TOKEN_ID; let owner = setup_camel_receiver(); - ERC721::initializer(NAME, SYMBOL); - ERC721::_mint(owner, token_id); + InternalImpl::initializer(ref state, NAME, SYMBOL); + InternalImpl::_mint(ref state, owner, token_id); - assert(ERC721::owner_of(token_id) == owner, 'Ownership before'); - assert(ERC721::balance_of(owner) == 1, 'Balance of owner before'); + assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); + assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); set_caller_address(owner); - ERC721::safe_transfer_from(owner, owner, token_id, DATA(true)); + ERC721Impl::safe_transfer_from(ref state, owner, owner, token_id, DATA(true)); - assert(ERC721::owner_of(token_id) == owner, 'Ownership after'); - assert(ERC721::balance_of(owner) == 1, 'Balance of owner after'); + assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership after'); + assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner after'); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_safeTransferFrom_to_owner_camel() { + let mut state = STATE(); let token_id = TOKEN_ID; let owner = setup_camel_receiver(); - ERC721::initializer(NAME, SYMBOL); - ERC721::_mint(owner, token_id); + InternalImpl::initializer(ref state, NAME, SYMBOL); + InternalImpl::_mint(ref state, owner, token_id); - assert(ERC721::owner_of(token_id) == owner, 'Ownership before'); - assert(ERC721::balance_of(owner) == 1, 'Balance of owner before'); + assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); + assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); set_caller_address(owner); - ERC721::safeTransferFrom(owner, owner, token_id, DATA(true)); + ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, owner, token_id, DATA(true)); - assert(ERC721::owner_of(token_id) == owner, 'Ownership after'); - assert(ERC721::balance_of(owner) == 1, 'Balance of owner after'); + assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership after'); + assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner after'); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_safe_transfer_from_approved() { - setup(); + let mut state = setup(); let receiver = setup_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); @@ -862,18 +919,18 @@ fn test_safe_transfer_from_approved() { assert_state_before_transfer(token_id, owner, receiver); set_caller_address(owner); - ERC721::approve(OPERATOR(), token_id); + ERC721Impl::approve(ref state, OPERATOR(), token_id); set_caller_address(OPERATOR()); - ERC721::safe_transfer_from(owner, receiver, token_id, DATA(true)); + ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); assert_state_after_transfer(token_id, owner, receiver); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_safeTransferFrom_approved() { - setup(); + let mut state = setup(); let receiver = setup_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); @@ -881,18 +938,18 @@ fn test_safeTransferFrom_approved() { assert_state_before_transfer(token_id, owner, receiver); set_caller_address(owner); - ERC721::approve(OPERATOR(), token_id); + ERC721Impl::approve(ref state, OPERATOR(), token_id); set_caller_address(OPERATOR()); - ERC721::safeTransferFrom(owner, receiver, token_id, DATA(true)); + ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); assert_state_after_transfer(token_id, owner, receiver); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_safe_transfer_from_approved_camel() { - setup(); + let mut state = setup(); let receiver = setup_camel_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); @@ -900,18 +957,18 @@ fn test_safe_transfer_from_approved_camel() { assert_state_before_transfer(token_id, owner, receiver); set_caller_address(owner); - ERC721::approve(OPERATOR(), token_id); + ERC721Impl::approve(ref state, OPERATOR(), token_id); set_caller_address(OPERATOR()); - ERC721::safe_transfer_from(owner, receiver, token_id, DATA(true)); + ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); assert_state_after_transfer(token_id, owner, receiver); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_safeTransferFrom_approved_camel() { - setup(); + let mut state = setup(); let receiver = setup_camel_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); @@ -919,18 +976,18 @@ fn test_safeTransferFrom_approved_camel() { assert_state_before_transfer(token_id, owner, receiver); set_caller_address(owner); - ERC721::approve(OPERATOR(), token_id); + ERC721Impl::approve(ref state, OPERATOR(), token_id); set_caller_address(OPERATOR()); - ERC721::safeTransferFrom(owner, receiver, token_id, DATA(true)); + ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); assert_state_after_transfer(token_id, owner, receiver); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_safe_transfer_from_approved_for_all() { - setup(); + let mut state = setup(); let receiver = setup_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); @@ -938,18 +995,18 @@ fn test_safe_transfer_from_approved_for_all() { assert_state_before_transfer(token_id, owner, receiver); set_caller_address(owner); - ERC721::set_approval_for_all(OPERATOR(), true); + ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); set_caller_address(OPERATOR()); - ERC721::safe_transfer_from(owner, receiver, token_id, DATA(true)); + ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); assert_state_after_transfer(token_id, owner, receiver); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_safeTransferFrom_approved_for_all() { - setup(); + let mut state = setup(); let receiver = setup_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); @@ -957,18 +1014,18 @@ fn test_safeTransferFrom_approved_for_all() { assert_state_before_transfer(token_id, owner, receiver); set_caller_address(owner); - ERC721::set_approval_for_all(OPERATOR(), true); + ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); set_caller_address(OPERATOR()); - ERC721::safeTransferFrom(owner, receiver, token_id, DATA(true)); + ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); assert_state_after_transfer(token_id, owner, receiver); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_safe_transfer_from_approved_for_all_camel() { - setup(); + let mut state = setup(); let receiver = setup_camel_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); @@ -976,18 +1033,18 @@ fn test_safe_transfer_from_approved_for_all_camel() { assert_state_before_transfer(token_id, owner, receiver); set_caller_address(owner); - ERC721::set_approval_for_all(OPERATOR(), true); + ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); set_caller_address(OPERATOR()); - ERC721::safe_transfer_from(owner, receiver, token_id, DATA(true)); + ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); assert_state_after_transfer(token_id, owner, receiver); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test_safeTransferFrom_approved_for_all_camel() { - setup(); + let mut state = setup(); let receiver = setup_camel_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); @@ -995,30 +1052,30 @@ fn test_safeTransferFrom_approved_for_all_camel() { assert_state_before_transfer(token_id, owner, receiver); set_caller_address(owner); - ERC721::set_approval_for_all(OPERATOR(), true); + ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); set_caller_address(OPERATOR()); - ERC721::safeTransferFrom(owner, receiver, token_id, DATA(true)); + ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); assert_state_after_transfer(token_id, owner, receiver); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: unauthorized caller', ))] fn test_safe_transfer_from_unauthorized() { - setup(); + let mut state = setup(); set_caller_address(OTHER()); - ERC721::safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); + ERC721Impl::safe_transfer_from(ref state, OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: unauthorized caller', ))] fn test_safeTransferFrom_unauthorized() { - setup(); + let mut state = setup(); set_caller_address(OTHER()); - ERC721::safeTransferFrom(OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); + ERC721CamelOnlyImpl::safeTransferFrom(ref state, OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); } // @@ -1026,41 +1083,40 @@ fn test_safeTransferFrom_unauthorized() { // #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test__transfer() { - setup(); + let mut state = setup(); let token_id = TOKEN_ID; let owner = OWNER(); let recipient = RECIPIENT(); assert_state_before_transfer(token_id, owner, recipient); - ERC721::_transfer(owner, recipient, token_id); + InternalImpl::_transfer(ref state, owner, recipient, token_id); assert_state_after_transfer(token_id, owner, recipient); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] fn test__transfer_nonexistent() { - ERC721::_transfer(ZERO(), RECIPIENT(), TOKEN_ID); + let mut state = STATE(); + InternalImpl::_transfer(ref state, ZERO(), RECIPIENT(), TOKEN_ID); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid receiver', ))] fn test__transfer_to_zero() { - setup(); - - ERC721::_transfer(OWNER(), ZERO(), TOKEN_ID); + let mut state = setup(); + InternalImpl::_transfer(ref state, OWNER(), ZERO(), TOKEN_ID); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: wrong sender', ))] fn test__transfer_from_invalid_owner() { - setup(); - - ERC721::_transfer(RECIPIENT(), OWNER(), TOKEN_ID); + let mut state = setup(); + InternalImpl::_transfer(ref state, RECIPIENT(), OWNER(), TOKEN_ID); } // @@ -1068,29 +1124,31 @@ fn test__transfer_from_invalid_owner() { // #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test__mint() { + let mut state = STATE(); let recipient = RECIPIENT(); let token_id = TOKEN_ID; + assert_state_before_mint(recipient); - ERC721::_mint(RECIPIENT(), TOKEN_ID); + InternalImpl::_mint(ref state, RECIPIENT(), TOKEN_ID); assert_state_after_mint(token_id, recipient); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid receiver', ))] fn test__mint_to_zero() { - ERC721::_mint(ZERO(), TOKEN_ID); + let mut state = STATE(); + InternalImpl::_mint(ref state, ZERO(), TOKEN_ID); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: token already minted', ))] fn test__mint_already_exist() { - setup(); - - ERC721::_mint(RECIPIENT(), TOKEN_ID); + let mut state = setup(); + InternalImpl::_mint(ref state, RECIPIENT(), TOKEN_ID); } // @@ -1098,98 +1156,106 @@ fn test__mint_already_exist() { // #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test__safe_mint_to_receiver() { + let mut state = STATE(); let recipient = setup_receiver(); let token_id = TOKEN_ID; assert_state_before_mint(recipient); - ERC721::_safe_mint(recipient, token_id, DATA(true)); + InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(true)); assert_state_after_mint(token_id, recipient); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test__safe_mint_to_receiver_camel() { + let mut state = STATE(); let recipient = setup_camel_receiver(); let token_id = TOKEN_ID; assert_state_before_mint(recipient); - ERC721::_safe_mint(recipient, token_id, DATA(true)); + InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(true)); assert_state_after_mint(token_id, recipient); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test__safe_mint_to_account() { + let mut state = STATE(); let account = setup_account(); let token_id = TOKEN_ID; assert_state_before_mint(account); - ERC721::_safe_mint(account, token_id, DATA(true)); + InternalImpl::_safe_mint(ref state, account, token_id, DATA(true)); assert_state_after_mint(token_id, account); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test__safe_mint_to_account_camel() { + let mut state = STATE(); let account = setup_camel_account(); let token_id = TOKEN_ID; assert_state_before_mint(account); - ERC721::_safe_mint(account, token_id, DATA(true)); + InternalImpl::_safe_mint(ref state, account, token_id, DATA(true)); assert_state_after_mint(token_id, account); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] fn test__safe_mint_to_non_receiver() { - let recipient = utils::deploy(ERC721NonReceiver::TEST_CLASS_HASH, ArrayTrait::new()); + let mut state = STATE(); + let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); let token_id = TOKEN_ID; assert_state_before_mint(recipient); - ERC721::_safe_mint(recipient, token_id, DATA(true)); + InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(true)); assert_state_after_mint(token_id, recipient); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: safe mint failed', ))] fn test__safe_mint_to_receiver_failure() { + let mut state = STATE(); let recipient = setup_receiver(); let token_id = TOKEN_ID; assert_state_before_mint(recipient); - ERC721::_safe_mint(recipient, token_id, DATA(false)); + InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(false)); assert_state_after_mint(token_id, recipient); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: safe mint failed', ))] fn test__safe_mint_to_receiver_failure_camel() { + let mut state = STATE(); let recipient = setup_camel_receiver(); let token_id = TOKEN_ID; assert_state_before_mint(recipient); - ERC721::_safe_mint(recipient, token_id, DATA(false)); + InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(false)); assert_state_after_mint(token_id, recipient); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid receiver', ))] fn test__safe_mint_to_zero() { - ERC721::_safe_mint(ZERO(), TOKEN_ID, DATA(true)); + let mut state = STATE(); + InternalImpl::_safe_mint(ref state, ZERO(), TOKEN_ID, DATA(true)); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: token already minted', ))] fn test__safe_mint_already_exist() { - setup(); - ERC721::_safe_mint(RECIPIENT(), TOKEN_ID, DATA(true)); + let mut state = setup(); + InternalImpl::_safe_mint(ref state, RECIPIENT(), TOKEN_ID, DATA(true)); } // @@ -1197,28 +1263,29 @@ fn test__safe_mint_already_exist() { // #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test__burn() { - setup(); + let mut state = setup(); - ERC721::_approve(OTHER(), TOKEN_ID); + InternalImpl::_approve(ref state, OTHER(), TOKEN_ID); - assert(ERC721::owner_of(TOKEN_ID) == OWNER(), 'Ownership before'); - assert(ERC721::balance_of(OWNER()) == 1, 'Balance of owner before'); - assert(ERC721::get_approved(TOKEN_ID) == OTHER(), 'Approval before'); + assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership before'); + assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner before'); + assert(ERC721Impl::get_approved(@state, TOKEN_ID) == OTHER(), 'Approval before'); - ERC721::_burn(TOKEN_ID); + InternalImpl::_burn(ref state, TOKEN_ID); - assert(ERC721::_owners::read(TOKEN_ID) == ZERO(), 'Ownership after'); - assert(ERC721::balance_of(OWNER()) == 0, 'Balance of owner after'); - assert(ERC721::_token_approvals::read(TOKEN_ID) == ZERO(), 'Approval after'); + assert(state._owners.read(TOKEN_ID) == ZERO(), 'Ownership after'); + assert(ERC721Impl::balance_of(@state, OWNER()) == 0, 'Balance of owner after'); + assert(state._token_approvals.read(TOKEN_ID) == ZERO(), 'Approval after'); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] fn test__burn_nonexistent() { - ERC721::_burn(TOKEN_ID); + let mut state = STATE(); + InternalImpl::_burn(ref state, TOKEN_ID); } // @@ -1226,20 +1293,21 @@ fn test__burn_nonexistent() { // #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] fn test__set_token_uri() { - setup(); + let mut state = setup(); - assert(ERC721::token_uri(TOKEN_ID) == 0, 'URI should be 0'); - ERC721::_set_token_uri(TOKEN_ID, URI); - assert(ERC721::token_uri(TOKEN_ID) == URI, 'URI should be set'); + assert(ERC721MetadataImpl::token_uri(@state, TOKEN_ID) == 0, 'URI should be 0'); + InternalImpl::_set_token_uri(ref state, TOKEN_ID, URI); + assert(ERC721MetadataImpl::token_uri(@state, TOKEN_ID) == URI, 'URI should be set'); } #[test] -#[available_gas(2000000)] +#[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid token ID', ))] fn test__set_token_uri_nonexistent() { - ERC721::_set_token_uri(TOKEN_ID, URI); + let mut state = STATE(); + InternalImpl::_set_token_uri(ref state, TOKEN_ID, URI); } // @@ -1249,24 +1317,28 @@ fn test__set_token_uri_nonexistent() { fn assert_state_before_transfer( token_id: u256, owner: ContractAddress, recipient: ContractAddress ) { - assert(ERC721::owner_of(token_id) == owner, 'Ownership before'); - assert(ERC721::balance_of(owner) == 1, 'Balance of owner before'); - assert(ERC721::balance_of(recipient) == 0, 'Balance of recipient before'); + let state = STATE(); + assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); + assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); + assert(ERC721Impl::balance_of(@state, recipient) == 0, 'Balance of recipient before'); } fn assert_state_after_transfer(token_id: u256, owner: ContractAddress, recipient: ContractAddress) { - assert(ERC721::owner_of(token_id) == recipient, 'Ownership after'); - assert(ERC721::balance_of(owner) == 0, 'Balance of owner after'); - assert(ERC721::balance_of(recipient) == 1, 'Balance of recipient after'); - assert(ERC721::get_approved(token_id) == ZERO(), 'Approval not implicitly reset'); + let state = STATE(); + assert(ERC721Impl::owner_of(@state, token_id) == recipient, 'Ownership after'); + assert(ERC721Impl::balance_of(@state, owner) == 0, 'Balance of owner after'); + assert(ERC721Impl::balance_of(@state, recipient) == 1, 'Balance of recipient after'); + assert(ERC721Impl::get_approved(@state, token_id) == ZERO(), 'Approval not implicitly reset'); } fn assert_state_before_mint(recipient: ContractAddress) { - assert(ERC721::balance_of(recipient) == 0, 'Balance of recipient before'); + let state = STATE(); + assert(ERC721Impl::balance_of(@state, recipient) == 0, 'Balance of recipient before'); } fn assert_state_after_mint(token_id: u256, recipient: ContractAddress) { - assert(ERC721::owner_of(token_id) == recipient, 'Ownership after'); - assert(ERC721::balance_of(recipient) == 1, 'Balance of recipient after'); - assert(ERC721::get_approved(token_id) == ZERO(), 'Approval implicitly set'); + let state = STATE(); + assert(ERC721Impl::owner_of(@state, token_id) == recipient, 'Ownership after'); + assert(ERC721Impl::balance_of(@state, recipient) == 1, 'Balance of recipient after'); + assert(ERC721Impl::get_approved(@state, token_id) == ZERO(), 'Approval implicitly set'); } diff --git a/src/openzeppelin/token.cairo b/src/openzeppelin/token.cairo index f9a848d01..51f2f6d68 100644 --- a/src/openzeppelin/token.cairo +++ b/src/openzeppelin/token.cairo @@ -1,2 +1,2 @@ -mod erc20; +// mod erc20; mod erc721; diff --git a/src/openzeppelin/token/erc20.cairo b/src/openzeppelin/token/erc20.cairo index 1a728ef23..bd33b3953 100644 --- a/src/openzeppelin/token/erc20.cairo +++ b/src/openzeppelin/token/erc20.cairo @@ -1,5 +1,7 @@ mod erc20; -use erc20::{ERC20, ERC20ABIDispatcher, ERC20ABIDispatcherTrait}; - mod interface; mod dual20; + +use erc20::ERC20; +use erc20::ERC20ABIDispatcher; +use erc20::ERC20ABIDispatcherTrait; diff --git a/src/openzeppelin/token/erc721.cairo b/src/openzeppelin/token/erc721.cairo index 0fab567a8..591703ecc 100644 --- a/src/openzeppelin/token/erc721.cairo +++ b/src/openzeppelin/token/erc721.cairo @@ -1,5 +1,6 @@ -mod erc721; -use erc721::ERC721; -mod interface; mod dual721; mod dual721_receiver; +mod erc721; +mod interface; + +use erc721::ERC721; diff --git a/src/openzeppelin/token/erc721/dual721.cairo b/src/openzeppelin/token/erc721/dual721.cairo index bdd49fd03..55655d9c7 100644 --- a/src/openzeppelin/token/erc721/dual721.cairo +++ b/src/openzeppelin/token/erc721/dual721.cairo @@ -39,21 +39,17 @@ trait DualCaseERC721Trait { impl DualCaseERC721Impl of DualCaseERC721Trait { fn name(self: @DualCaseERC721) -> felt252 { - let args = ArrayTrait::new(); - - call_contract_syscall(*self.contract_address, selectors::name, args.span()) + call_contract_syscall(*self.contract_address, selectors::name, array![].span()) .unwrap_and_cast() } fn symbol(self: @DualCaseERC721) -> felt252 { - let args = ArrayTrait::new(); - - call_contract_syscall(*self.contract_address, selectors::symbol, args.span()) + call_contract_syscall(*self.contract_address, selectors::symbol, array![].span()) .unwrap_and_cast() } fn token_uri(self: @DualCaseERC721, token_id: u256) -> felt252 { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(token_id); try_selector_with_fallback( @@ -63,7 +59,7 @@ impl DualCaseERC721Impl of DualCaseERC721Trait { } fn balance_of(self: @DualCaseERC721, account: ContractAddress) -> u256 { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(account); try_selector_with_fallback( @@ -73,7 +69,7 @@ impl DualCaseERC721Impl of DualCaseERC721Trait { } fn owner_of(self: @DualCaseERC721, token_id: u256) -> ContractAddress { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(token_id); try_selector_with_fallback( @@ -83,7 +79,7 @@ impl DualCaseERC721Impl of DualCaseERC721Trait { } fn get_approved(self: @DualCaseERC721, token_id: u256) -> ContractAddress { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(token_id); try_selector_with_fallback( @@ -95,7 +91,7 @@ impl DualCaseERC721Impl of DualCaseERC721Trait { fn is_approved_for_all( self: @DualCaseERC721, owner: ContractAddress, operator: ContractAddress ) -> bool { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(owner); args.append_serde(operator); @@ -109,7 +105,7 @@ impl DualCaseERC721Impl of DualCaseERC721Trait { } fn approve(self: @DualCaseERC721, to: ContractAddress, token_id: u256) { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(to); args.append_serde(token_id); call_contract_syscall(*self.contract_address, selectors::approve, args.span()) @@ -117,7 +113,7 @@ impl DualCaseERC721Impl of DualCaseERC721Trait { } fn set_approval_for_all(self: @DualCaseERC721, operator: ContractAddress, approved: bool) { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(operator); args.append_serde(approved); @@ -133,7 +129,7 @@ impl DualCaseERC721Impl of DualCaseERC721Trait { fn transfer_from( self: @DualCaseERC721, from: ContractAddress, to: ContractAddress, token_id: u256 ) { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(from); args.append_serde(to); args.append_serde(token_id); @@ -151,7 +147,7 @@ impl DualCaseERC721Impl of DualCaseERC721Trait { token_id: u256, mut data: Span ) { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(from); args.append_serde(to); args.append_serde(token_id); @@ -167,7 +163,7 @@ impl DualCaseERC721Impl of DualCaseERC721Trait { } fn supports_interface(self: @DualCaseERC721, interface_id: felt252) -> bool { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(interface_id); try_selector_with_fallback( diff --git a/src/openzeppelin/token/erc721/dual721_receiver.cairo b/src/openzeppelin/token/erc721/dual721_receiver.cairo index 3dc682b87..6c13fc931 100644 --- a/src/openzeppelin/token/erc721/dual721_receiver.cairo +++ b/src/openzeppelin/token/erc721/dual721_receiver.cairo @@ -29,7 +29,7 @@ impl DualCaseERC721ReceiverImpl of DualCaseERC721ReceiverTrait { token_id: u256, data: Span ) -> felt252 { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(operator); args.append_serde(from); args.append_serde(token_id); diff --git a/src/openzeppelin/token/erc721/erc721.cairo b/src/openzeppelin/token/erc721/erc721.cairo index 339c63ebd..4e2a1bb80 100644 --- a/src/openzeppelin/token/erc721/erc721.cairo +++ b/src/openzeppelin/token/erc721/erc721.cairo @@ -1,75 +1,24 @@ use starknet::ContractAddress; -#[abi] -trait ERC721ABI { - // case agnostic - #[view] - fn name() -> felt252; - #[view] - fn symbol() -> felt252; - #[external] - fn approve(to: ContractAddress, token_id: u256); - // snake_case - #[view] - fn supports_interface(interface_id: felt252) -> bool; - #[view] - fn balance_of(account: ContractAddress) -> u256; - #[view] - fn owner_of(token_id: u256) -> ContractAddress; - #[external] - fn transfer_from(from: ContractAddress, to: ContractAddress, token_id: u256); - #[external] - fn safe_transfer_from( - from: ContractAddress, to: ContractAddress, token_id: u256, data: Span - ); - #[external] - fn set_approval_for_all(operator: ContractAddress, approved: bool); - #[view] - fn get_approved(token_id: u256) -> ContractAddress; - #[view] - fn is_approved_for_all(owner: ContractAddress, operator: ContractAddress) -> bool; - #[view] - fn token_uri(token_id: u256) -> felt252; - // camelCase - #[view] - fn supportsInterface(interfaceId: felt252) -> bool; - #[view] - fn balanceOf(account: ContractAddress) -> u256; - #[view] - fn ownerOf(tokenId: u256) -> ContractAddress; - #[external] - fn transferFrom(from: ContractAddress, to: ContractAddress, tokenId: u256); - #[external] - fn safeTransferFrom( - from: ContractAddress, to: ContractAddress, tokenId: u256, data: Span - ); - #[external] - fn setApprovalForAll(operator: ContractAddress, approved: bool); - #[view] - fn getApproved(tokenId: u256) -> ContractAddress; - #[view] - fn isApprovedForAll(owner: ContractAddress, operator: ContractAddress) -> bool; - #[view] - fn tokenUri(tokenId: u256) -> felt252; -} - -#[contract] +#[starknet::contract] mod ERC721 { + use array::SpanTrait; + use option::OptionTrait; + use starknet::ContractAddress; + use starknet::get_caller_address; + use zeroable::Zeroable; + use openzeppelin::account; use openzeppelin::introspection::dual_src5::DualCaseSRC5; use openzeppelin::introspection::dual_src5::DualCaseSRC5Trait; + use openzeppelin::introspection::interface::ISRC5; + use openzeppelin::introspection::interface::ISRC5Camel; use openzeppelin::introspection::src5; - use openzeppelin::token::erc721; use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721Receiver; use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721ReceiverTrait; - use openzeppelin::utils::serde::SpanSerde; - - use array::SpanTrait; - use option::OptionTrait; - use starknet::ContractAddress; - use starknet::get_caller_address; - use zeroable::Zeroable; + use openzeppelin::token::erc721::interface; + #[storage] struct Storage { _name: felt252, _symbol: felt252, @@ -81,385 +30,324 @@ mod ERC721 { } #[event] - fn Transfer(from: ContractAddress, to: ContractAddress, token_id: u256) {} + #[derive(Drop, starknet::Event)] + enum Event { + Transfer: Transfer, + Approval: Approval, + ApprovalForAll: ApprovalForAll + } - #[event] - fn Approval(owner: ContractAddress, approved: ContractAddress, token_id: u256) {} + #[derive(Drop, starknet::Event)] + struct Transfer { + from: ContractAddress, + to: ContractAddress, + token_id: u256 + } - #[event] - fn ApprovalForAll(owner: ContractAddress, operator: ContractAddress, approved: bool) {} + #[derive(Drop, starknet::Event)] + struct Approval { + owner: ContractAddress, + approved: ContractAddress, + token_id: u256 + } + + #[derive(Drop, starknet::Event)] + struct ApprovalForAll { + owner: ContractAddress, + operator: ContractAddress, + approved: bool + } #[constructor] - fn constructor(name: felt252, symbol: felt252) { - initializer(name, symbol); + fn constructor(ref self: ContractState, name: felt252, symbol: felt252) { + self.initializer(name, symbol); } - impl ISRC5Impl of src5::ISRC5 { - fn supports_interface(interface_id: felt252) -> bool { - src5::SRC5::supports_interface(interface_id) + // + // External + // + + #[external(v0)] + impl SRC5Impl of ISRC5 { + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { + let unsafe_state = src5::SRC5::unsafe_new_contract_state(); + src5::SRC5::SRC5Impl::supports_interface(@unsafe_state, interface_id) } } - impl ISRC5CamelImpl of src5::ISRC5Camel { - fn supportsInterface(interfaceId: felt252) -> bool { - src5::SRC5::supportsInterface(interfaceId) + #[external(v0)] + impl SRC5CamelImpl of ISRC5Camel { + fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { + let unsafe_state = src5::SRC5::unsafe_new_contract_state(); + src5::SRC5::SRC5CamelImpl::supportsInterface(@unsafe_state, interfaceId) } } - impl ERC721Impl of erc721::interface::IERC721 { - fn name() -> felt252 { - _name::read() + #[external(v0)] + impl ERC721MetadataImpl of interface::IERC721Metadata { + fn name(self: @ContractState) -> felt252 { + self._name.read() + } + + fn symbol(self: @ContractState) -> felt252 { + self._symbol.read() } - fn symbol() -> felt252 { - _symbol::read() + fn token_uri(self: @ContractState, token_id: u256) -> felt252 { + assert(self._exists(token_id), 'ERC721: invalid token ID'); + self._token_uri.read(token_id) } + } - fn token_uri(token_id: u256) -> felt252 { - assert(_exists(token_id), 'ERC721: invalid token ID'); - _token_uri::read(token_id) + #[external(v0)] + impl ERC721MetadataCamelOnlyImpl of interface::IERC721MetadataCamelOnly { + fn tokenUri(self: @ContractState, tokenId: u256) -> felt252 { + assert(self._exists(tokenId), 'ERC721: invalid token ID'); + self._token_uri.read(tokenId) } + } - fn balance_of(account: ContractAddress) -> u256 { + #[external(v0)] + impl ERC721Impl of interface::IERC721 { + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { assert(!account.is_zero(), 'ERC721: invalid account'); - _balances::read(account) + self._balances.read(account) } - fn owner_of(token_id: u256) -> ContractAddress { - _owner_of(token_id) + fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress { + self._owner_of(token_id) } - fn get_approved(token_id: u256) -> ContractAddress { - assert(_exists(token_id), 'ERC721: invalid token ID'); - _token_approvals::read(token_id) + fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { + assert(self._exists(token_id), 'ERC721: invalid token ID'); + self._token_approvals.read(token_id) } - fn is_approved_for_all(owner: ContractAddress, operator: ContractAddress) -> bool { - _operator_approvals::read((owner, operator)) + fn is_approved_for_all( + self: @ContractState, owner: ContractAddress, operator: ContractAddress + ) -> bool { + self._operator_approvals.read((owner, operator)) } - fn approve(to: ContractAddress, token_id: u256) { - let owner = _owner_of(token_id); + fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) { + let owner = self._owner_of(token_id); let caller = get_caller_address(); assert( - owner == caller | is_approved_for_all(owner, caller), 'ERC721: unauthorized caller' + owner == caller || ERC721Impl::is_approved_for_all(@self, owner, caller), + 'ERC721: unauthorized caller' ); - _approve(to, token_id); + self._approve(to, token_id); } - fn set_approval_for_all(operator: ContractAddress, approved: bool) { - _set_approval_for_all(get_caller_address(), operator, approved) + fn set_approval_for_all( + ref self: ContractState, operator: ContractAddress, approved: bool + ) { + self._set_approval_for_all(get_caller_address(), operator, approved) } - fn transfer_from(from: ContractAddress, to: ContractAddress, token_id: u256) { + fn transfer_from( + ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 + ) { assert( - _is_approved_or_owner(get_caller_address(), token_id), 'ERC721: unauthorized caller' + self._is_approved_or_owner(get_caller_address(), token_id), + 'ERC721: unauthorized caller' ); - _transfer(from, to, token_id); + self._transfer(from, to, token_id); } fn safe_transfer_from( - from: ContractAddress, to: ContractAddress, token_id: u256, data: Span + ref self: ContractState, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span ) { assert( - _is_approved_or_owner(get_caller_address(), token_id), 'ERC721: unauthorized caller' + self._is_approved_or_owner(get_caller_address(), token_id), + 'ERC721: unauthorized caller' ); - _safe_transfer(from, to, token_id, data); + self._safe_transfer(from, to, token_id, data); } } - impl ERC721CamelImpl of erc721::interface::IERC721Camel { - fn name() -> felt252 { - ERC721Impl::name() - } - - fn symbol() -> felt252 { - ERC721Impl::symbol() - } - - fn tokenUri(tokenId: u256) -> felt252 { - ERC721Impl::token_uri(tokenId) - } - - fn balanceOf(account: ContractAddress) -> u256 { - ERC721Impl::balance_of(account) - } - - fn ownerOf(tokenId: u256) -> ContractAddress { - ERC721Impl::owner_of(tokenId) + #[external(v0)] + impl ERC721CamelOnlyImpl of interface::IERC721CamelOnly { + fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 { + ERC721Impl::balance_of(self, account) } - fn approve(to: ContractAddress, tokenId: u256) { - ERC721Impl::approve(to, tokenId) + fn ownerOf(self: @ContractState, tokenId: u256) -> ContractAddress { + ERC721Impl::owner_of(self, tokenId) } - fn getApproved(tokenId: u256) -> ContractAddress { - ERC721Impl::get_approved(tokenId) + fn getApproved(self: @ContractState, tokenId: u256) -> ContractAddress { + ERC721Impl::get_approved(self, tokenId) } - fn isApprovedForAll(owner: ContractAddress, operator: ContractAddress) -> bool { - ERC721Impl::is_approved_for_all(owner, operator) + fn isApprovedForAll( + self: @ContractState, owner: ContractAddress, operator: ContractAddress + ) -> bool { + ERC721Impl::is_approved_for_all(self, owner, operator) } - fn setApprovalForAll(operator: ContractAddress, approved: bool) { - ERC721Impl::set_approval_for_all(operator, approved) + fn setApprovalForAll(ref self: ContractState, operator: ContractAddress, approved: bool) { + ERC721Impl::set_approval_for_all(ref self, operator, approved) } - fn transferFrom(from: ContractAddress, to: ContractAddress, tokenId: u256) { - ERC721Impl::transfer_from(from, to, tokenId) + fn transferFrom( + ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256 + ) { + ERC721Impl::transfer_from(ref self, from, to, tokenId) } fn safeTransferFrom( - from: ContractAddress, to: ContractAddress, tokenId: u256, data: Span + ref self: ContractState, + from: ContractAddress, + to: ContractAddress, + tokenId: u256, + data: Span ) { - ERC721Impl::safe_transfer_from(from, to, tokenId, data) + ERC721Impl::safe_transfer_from(ref self, from, to, tokenId, data) } } - // View - - #[view] - fn supports_interface(interface_id: felt252) -> bool { - ISRC5Impl::supports_interface(interface_id) - } - - #[view] - fn supportsInterface(interfaceId: felt252) -> bool { - ISRC5CamelImpl::supportsInterface(interfaceId) - } - - #[view] - fn name() -> felt252 { - ERC721Impl::name() - } - - #[view] - fn symbol() -> felt252 { - ERC721Impl::symbol() - } - - #[view] - fn token_uri(token_id: u256) -> felt252 { - ERC721Impl::token_uri(token_id) - } - - #[view] - fn tokenUri(tokenId: u256) -> felt252 { - ERC721CamelImpl::tokenUri(tokenId) - } - - #[view] - fn balance_of(account: ContractAddress) -> u256 { - ERC721Impl::balance_of(account) - } - - #[view] - fn balanceOf(account: ContractAddress) -> u256 { - ERC721CamelImpl::balanceOf(account) - } - - #[view] - fn owner_of(token_id: u256) -> ContractAddress { - ERC721Impl::owner_of(token_id) - } - - #[view] - fn ownerOf(tokenId: u256) -> ContractAddress { - ERC721CamelImpl::ownerOf(tokenId) - } - - #[view] - fn get_approved(token_id: u256) -> ContractAddress { - ERC721Impl::get_approved(token_id) - } - - #[view] - fn getApproved(tokenId: u256) -> ContractAddress { - ERC721CamelImpl::getApproved(tokenId) - } - - #[view] - fn is_approved_for_all(owner: ContractAddress, operator: ContractAddress) -> bool { - ERC721Impl::is_approved_for_all(owner, operator) - } - - #[view] - fn isApprovedForAll(owner: ContractAddress, operator: ContractAddress) -> bool { - ERC721CamelImpl::isApprovedForAll(owner, operator) - } - - // External - - #[external] - fn approve(to: ContractAddress, token_id: u256) { - ERC721Impl::approve(to, token_id) - } - - #[external] - fn set_approval_for_all(operator: ContractAddress, approved: bool) { - ERC721Impl::set_approval_for_all(operator, approved) - } - - #[external] - fn setApprovalForAll(operator: ContractAddress, approved: bool) { - ERC721CamelImpl::setApprovalForAll(operator, approved) - } - - #[external] - fn transfer_from(from: ContractAddress, to: ContractAddress, token_id: u256) { - ERC721Impl::transfer_from(from, to, token_id) - } - - #[external] - fn transferFrom(from: ContractAddress, to: ContractAddress, tokenId: u256) { - ERC721CamelImpl::transferFrom(from, to, tokenId) - } - - #[external] - fn safe_transfer_from( - from: ContractAddress, to: ContractAddress, token_id: u256, data: Span - ) { - ERC721Impl::safe_transfer_from(from, to, token_id, data) - } - - #[external] - fn safeTransferFrom( - from: ContractAddress, to: ContractAddress, tokenId: u256, data: Span - ) { - ERC721CamelImpl::safeTransferFrom(from, to, tokenId, data) - } - + // // Internal - - #[internal] - fn initializer(name_: felt252, symbol_: felt252) { - _name::write(name_); - _symbol::write(symbol_); - src5::SRC5::register_interface(erc721::interface::IERC721_ID); - src5::SRC5::register_interface(erc721::interface::IERC721_METADATA_ID); - } - - #[internal] - fn _owner_of(token_id: u256) -> ContractAddress { - let owner = _owners::read(token_id); - match owner.is_zero() { - bool::False(()) => owner, - bool::True(()) => panic_with_felt252('ERC721: invalid token ID') + // + + #[generate_trait] + impl InternalImpl of InternalTrait { + fn initializer(ref self: ContractState, name_: felt252, symbol_: felt252) { + self._name.write(name_); + self._symbol.write(symbol_); + + let mut unsafe_state = src5::SRC5::unsafe_new_contract_state(); + src5::SRC5::InternalImpl::register_interface(ref unsafe_state, interface::IERC721_ID); + src5::SRC5::InternalImpl::register_interface( + ref unsafe_state, interface::IERC721_METADATA_ID + ); } - } - #[internal] - fn _exists(token_id: u256) -> bool { - !_owners::read(token_id).is_zero() - } + fn _owner_of(self: @ContractState, token_id: u256) -> ContractAddress { + let owner = self._owners.read(token_id); + match owner.is_zero() { + bool::False(()) => owner, + bool::True(()) => panic_with_felt252('ERC721: invalid token ID') + } + } - #[internal] - fn _is_approved_or_owner(spender: ContractAddress, token_id: u256) -> bool { - let owner = _owner_of(token_id); - owner == spender | is_approved_for_all(owner, spender) | spender == get_approved(token_id) - } + fn _exists(self: @ContractState, token_id: u256) -> bool { + !self._owners.read(token_id).is_zero() + } - #[internal] - fn _approve(to: ContractAddress, token_id: u256) { - let owner = _owner_of(token_id); - assert(owner != to, 'ERC721: approval to owner'); - _token_approvals::write(token_id, to); - Approval(owner, to, token_id); - } + fn _is_approved_or_owner( + self: @ContractState, spender: ContractAddress, token_id: u256 + ) -> bool { + let owner = self._owner_of(token_id); + let is_approved_for_all = ERC721Impl::is_approved_for_all(self, owner, spender); + owner == spender + || is_approved_for_all + || spender == ERC721Impl::get_approved(self, token_id) + } - #[internal] - fn _set_approval_for_all(owner: ContractAddress, operator: ContractAddress, approved: bool) { - assert(owner != operator, 'ERC721: self approval'); - _operator_approvals::write((owner, operator), approved); - ApprovalForAll(owner, operator, approved); - } + fn _approve(ref self: ContractState, to: ContractAddress, token_id: u256) { + let owner = self._owner_of(token_id); + assert(owner != to, 'ERC721: approval to owner'); - #[internal] - fn _mint(to: ContractAddress, token_id: u256) { - assert(!to.is_zero(), 'ERC721: invalid receiver'); - assert(!_exists(token_id), 'ERC721: token already minted'); + self._token_approvals.write(token_id, to); + self.emit(Approval { owner, approved: to, token_id }); + } - // Update balances - _balances::write(to, _balances::read(to) + 1); + fn _set_approval_for_all( + ref self: ContractState, + owner: ContractAddress, + operator: ContractAddress, + approved: bool + ) { + assert(owner != operator, 'ERC721: self approval'); + self._operator_approvals.write((owner, operator), approved); + self.emit(ApprovalForAll { owner, operator, approved }); + } - // Update token_id owner - _owners::write(token_id, to); + fn _mint(ref self: ContractState, to: ContractAddress, token_id: u256) { + assert(!to.is_zero(), 'ERC721: invalid receiver'); + assert(!self._exists(token_id), 'ERC721: token already minted'); - // Emit event - Transfer(Zeroable::zero(), to, token_id); - } + self._balances.write(to, self._balances.read(to) + 1); + self._owners.write(token_id, to); - #[internal] - fn _transfer(from: ContractAddress, to: ContractAddress, token_id: u256) { - assert(!to.is_zero(), 'ERC721: invalid receiver'); - let owner = _owner_of(token_id); - assert(from == owner, 'ERC721: wrong sender'); + self.emit(Transfer { from: Zeroable::zero(), to, token_id }); + } - // Implicit clear approvals, no need to emit an event - _token_approvals::write(token_id, Zeroable::zero()); + fn _transfer( + ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 + ) { + assert(!to.is_zero(), 'ERC721: invalid receiver'); + let owner = self._owner_of(token_id); + assert(from == owner, 'ERC721: wrong sender'); - // Update balances - _balances::write(from, _balances::read(from) - 1); - _balances::write(to, _balances::read(to) + 1); + // Implicit clear approvals, no need to emit an event + self._token_approvals.write(token_id, Zeroable::zero()); - // Update token_id owner - _owners::write(token_id, to); + self._balances.write(from, self._balances.read(from) - 1); + self._balances.write(to, self._balances.read(to) + 1); + self._owners.write(token_id, to); - // Emit event - Transfer(from, to, token_id); - } + self.emit(Transfer { from, to, token_id }); + } - #[internal] - fn _burn(token_id: u256) { - let owner = _owner_of(token_id); + fn _burn(ref self: ContractState, token_id: u256) { + let owner = self._owner_of(token_id); - // Implicit clear approvals, no need to emit an event - _token_approvals::write(token_id, Zeroable::zero()); + // Implicit clear approvals, no need to emit an event + self._token_approvals.write(token_id, Zeroable::zero()); - // Update balances - _balances::write(owner, _balances::read(owner) - 1); + self._balances.write(owner, self._balances.read(owner) - 1); + self._owners.write(token_id, Zeroable::zero()); - // Delete owner - _owners::write(token_id, Zeroable::zero()); + self.emit(Transfer { from: owner, to: Zeroable::zero(), token_id }); + } - // Emit event - Transfer(owner, Zeroable::zero(), token_id); - } + fn _safe_mint( + ref self: ContractState, to: ContractAddress, token_id: u256, data: Span + ) { + self._mint(to, token_id); + assert( + _check_on_erc721_received(Zeroable::zero(), to, token_id, data), + 'ERC721: safe mint failed' + ); + } - #[internal] - fn _safe_mint(to: ContractAddress, token_id: u256, data: Span) { - _mint(to, token_id); - assert( - _check_on_erc721_received(Zeroable::zero(), to, token_id, data), - 'ERC721: safe mint failed' - ); - } + fn _safe_transfer( + ref self: ContractState, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span + ) { + self._transfer(from, to, token_id); + assert( + _check_on_erc721_received(from, to, token_id, data), 'ERC721: safe transfer failed' + ); + } - #[internal] - fn _safe_transfer( - from: ContractAddress, to: ContractAddress, token_id: u256, data: Span - ) { - _transfer(from, to, token_id); - assert(_check_on_erc721_received(from, to, token_id, data), 'ERC721: safe transfer failed'); + fn _set_token_uri(ref self: ContractState, token_id: u256, token_uri: felt252) { + assert(self._exists(token_id), 'ERC721: invalid token ID'); + self._token_uri.write(token_id, token_uri) + } } #[internal] - fn _set_token_uri(token_id: u256, token_uri: felt252) { - assert(_exists(token_id), 'ERC721: invalid token ID'); - _token_uri::write(token_id, token_uri) - } - - #[private] fn _check_on_erc721_received( from: ContractAddress, to: ContractAddress, token_id: u256, data: Span ) -> bool { if (DualCaseSRC5 { contract_address: to } - .supports_interface(erc721::interface::IERC721_RECEIVER_ID)) { + .supports_interface(interface::IERC721_RECEIVER_ID)) { DualCaseERC721Receiver { contract_address: to } .on_erc721_received( get_caller_address(), from, token_id, data - ) == erc721::interface::IERC721_RECEIVER_ID + ) == interface::IERC721_RECEIVER_ID } else { DualCaseSRC5 { contract_address: to }.supports_interface(account::interface::ISRC6_ID) } diff --git a/src/openzeppelin/token/erc721/interface.cairo b/src/openzeppelin/token/erc721/interface.cairo index aa0acc454..5e22d6f07 100644 --- a/src/openzeppelin/token/erc721/interface.cairo +++ b/src/openzeppelin/token/erc721/interface.cairo @@ -1,4 +1,3 @@ -use openzeppelin::utils::serde::SpanSerde; use starknet::ContractAddress; use array::SpanTrait; @@ -8,66 +7,81 @@ const IERC721_METADATA_ID: felt252 = const IERC721_RECEIVER_ID: felt252 = 0x3a0dff5f70d80458ad14ae37bb182a728e3c8cdda0402a5daa86620bdf910bc; -#[abi] -trait IERC721 { - fn balance_of(account: ContractAddress) -> u256; - fn owner_of(token_id: u256) -> ContractAddress; - fn transfer_from(from: ContractAddress, to: ContractAddress, token_id: u256); +#[starknet::interface] +trait IERC721 { + fn balance_of(self: @TState, account: ContractAddress) -> u256; + fn owner_of(self: @TState, token_id: u256) -> ContractAddress; + fn transfer_from(ref self: TState, from: ContractAddress, to: ContractAddress, token_id: u256); fn safe_transfer_from( - from: ContractAddress, to: ContractAddress, token_id: u256, data: Span + ref self: TState, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span ); - fn approve(to: ContractAddress, token_id: u256); - fn set_approval_for_all(operator: ContractAddress, approved: bool); - fn get_approved(token_id: u256) -> ContractAddress; - fn is_approved_for_all(owner: ContractAddress, operator: ContractAddress) -> bool; - // IERC721Metadata - fn name() -> felt252; - fn symbol() -> felt252; - fn token_uri(token_id: u256) -> felt252; + fn approve(ref self: TState, to: ContractAddress, token_id: u256); + fn set_approval_for_all(ref self: TState, operator: ContractAddress, approved: bool); + fn get_approved(self: @TState, token_id: u256) -> ContractAddress; + fn is_approved_for_all( + self: @TState, owner: ContractAddress, operator: ContractAddress + ) -> bool; } -#[abi] -trait IERC721Camel { - fn balanceOf(account: ContractAddress) -> u256; - fn ownerOf(tokenId: u256) -> ContractAddress; - fn transferFrom(from: ContractAddress, to: ContractAddress, tokenId: u256); +#[starknet::interface] +trait IERC721CamelOnly { + fn balanceOf(self: @TState, account: ContractAddress) -> u256; + fn ownerOf(self: @TState, tokenId: u256) -> ContractAddress; + fn transferFrom(ref self: TState, from: ContractAddress, to: ContractAddress, tokenId: u256); fn safeTransferFrom( - from: ContractAddress, to: ContractAddress, tokenId: u256, data: Span + ref self: TState, + from: ContractAddress, + to: ContractAddress, + tokenId: u256, + data: Span ); - fn approve(to: ContractAddress, tokenId: u256); - fn setApprovalForAll(operator: ContractAddress, approved: bool); - fn getApproved(tokenId: u256) -> ContractAddress; - fn isApprovedForAll(owner: ContractAddress, operator: ContractAddress) -> bool; - // IERC721Metadata - fn name() -> felt252; - fn symbol() -> felt252; - fn tokenUri(tokenId: u256) -> felt252; + fn setApprovalForAll(ref self: TState, operator: ContractAddress, approved: bool); + fn getApproved(self: @TState, tokenId: u256) -> ContractAddress; + fn isApprovedForAll(self: @TState, owner: ContractAddress, operator: ContractAddress) -> bool; } // -// ERC721Receiver +// IERC721Metadata // -#[abi] -trait ERC721ReceiverABI { - fn on_erc721_received( - operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span - ) -> felt252; - fn onERC721Received( - operator: ContractAddress, from: ContractAddress, tokenId: u256, data: Span - ) -> felt252; +#[starknet::interface] +trait IERC721Metadata { + fn name(self: @TState) -> felt252; + fn symbol(self: @TState) -> felt252; + fn token_uri(self: @TState, token_id: u256) -> felt252; } -#[abi] -trait IERC721Receiver { +#[starknet::interface] +trait IERC721MetadataCamelOnly { + fn tokenUri(self: @TState, tokenId: u256) -> felt252; +} + +// +// ERC721Receiver +// + +#[starknet::interface] +trait IERC721Receiver { fn on_erc721_received( - operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span + self: @TState, + operator: ContractAddress, + from: ContractAddress, + token_id: u256, + data: Span ) -> felt252; } -#[abi] -trait IERC721ReceiverCamel { +#[starknet::interface] +trait IERC721ReceiverCamel { fn onERC721Received( - operator: ContractAddress, from: ContractAddress, tokenId: u256, data: Span + self: @TState, + operator: ContractAddress, + from: ContractAddress, + tokenId: u256, + data: Span ) -> felt252; } From a00a9d3e1ec7f734e9855216115f0e7539618aa3 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Fri, 21 Jul 2023 13:32:11 -0400 Subject: [PATCH 071/246] Migrate ownable to cairo2 (#665) * bump to cairo v2.0.2 * update Cargo * comment out mods * update initializable syntax * move security tests * update initializable tests * fix formatting * remove SpanSerde * update ownable syntax * add ownable_camel impl * update syntax in ownable mocks * update ownable tests * fix formatting * update cairo * update Cargo * simplify and clarify tests * fix formatting * simplify internal fns * add initializer * fix constructor, tidy up code * fix tests * Apply suggestions from code review Co-authored-by: Eric Nordelo * fix tests * change StorageTrait to InternalTrait * start refactor * change impls to external fns * use IOwnableCamelOnly * fix tests * remove unneeded mut * remove unneeded mut * Apply suggestions from code review Co-authored-by: Eric Nordelo * change to TState * fix formatting * remove Ownable prefix in fn calls * remove internal owner fn * use internal state trait to read owner --------- Co-authored-by: Eric Nordelo --- src/openzeppelin/access.cairo | 2 +- .../access/ownable/interface.cairo | 25 ++- src/openzeppelin/access/ownable/ownable.cairo | 110 ++++++------- src/openzeppelin/lib.cairo | 2 +- src/openzeppelin/tests.cairo | 2 +- src/openzeppelin/tests/access.cairo | 4 +- .../tests/access/test_dual_ownable.cairo | 17 +- .../tests/access/test_ownable.cairo | 145 ++++++++++++------ src/openzeppelin/tests/mocks.cairo | 2 +- .../tests/mocks/dual_ownable_mocks.cairo | 103 ++++++++----- 10 files changed, 235 insertions(+), 177 deletions(-) diff --git a/src/openzeppelin/access.cairo b/src/openzeppelin/access.cairo index 8f59b4ad1..55beac34c 100644 --- a/src/openzeppelin/access.cairo +++ b/src/openzeppelin/access.cairo @@ -1,2 +1,2 @@ -mod accesscontrol; +//mod accesscontrol; mod ownable; diff --git a/src/openzeppelin/access/ownable/interface.cairo b/src/openzeppelin/access/ownable/interface.cairo index 41cd27a69..4641093dc 100644 --- a/src/openzeppelin/access/ownable/interface.cairo +++ b/src/openzeppelin/access/ownable/interface.cairo @@ -1,21 +1,14 @@ use starknet::ContractAddress; -#[abi] -trait IOwnable { - #[view] - fn owner() -> ContractAddress; - #[external] - fn transfer_ownership(new_owner: ContractAddress); - #[external] - fn renounce_ownership(); +#[starknet::interface] +trait IOwnable { + fn owner(self: @TState) -> ContractAddress; + fn transfer_ownership(ref self: TState, new_owner: ContractAddress); + fn renounce_ownership(ref self: TState); } -#[abi] -trait IOwnableCamel { - #[view] - fn owner() -> ContractAddress; - #[external] - fn transferOwnership(newOwner: ContractAddress); - #[external] - fn renounceOwnership(); +#[starknet::interface] +trait IOwnableCamelOnly { + fn transferOwnership(ref self: TState, newOwner: ContractAddress); + fn renounceOwnership(ref self: TState); } diff --git a/src/openzeppelin/access/ownable/ownable.cairo b/src/openzeppelin/access/ownable/ownable.cairo index 29e21dde0..a7670a100 100644 --- a/src/openzeppelin/access/ownable/ownable.cairo +++ b/src/openzeppelin/access/ownable/ownable.cairo @@ -1,92 +1,76 @@ -#[contract] +#[starknet::contract] mod Ownable { use openzeppelin::access::ownable::interface; use starknet::ContractAddress; use starknet::get_caller_address; use zeroable::Zeroable; + #[storage] struct Storage { _owner: ContractAddress } #[event] - fn OwnershipTransferred(previous_owner: ContractAddress, new_owner: ContractAddress) {} + #[derive(Drop, starknet::Event)] + enum Event { + OwnershipTransferred: OwnershipTransferred + } + + #[derive(Drop, starknet::Event)] + struct OwnershipTransferred { + previous_owner: ContractAddress, + new_owner: ContractAddress, + } - impl OwnableImpl of interface::IOwnable { - fn owner() -> ContractAddress { - _owner::read() + #[generate_trait] + impl InternalImpl of InternalTrait { + fn initializer(ref self: ContractState, owner: ContractAddress) { + self._transfer_ownership(owner); } - fn transfer_ownership(new_owner: ContractAddress) { - assert(!new_owner.is_zero(), 'New owner is the zero address'); - assert_only_owner(); - _transfer_ownership(new_owner); + fn assert_only_owner(self: @ContractState) { + let owner: ContractAddress = self._owner.read(); + let caller: ContractAddress = get_caller_address(); + assert(!caller.is_zero(), 'Caller is the zero address'); + assert(caller == owner, 'Caller is not the owner'); } - fn renounce_ownership() { - assert_only_owner(); - _transfer_ownership(Zeroable::zero()); + fn _transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { + let previous_owner: ContractAddress = self._owner.read(); + self._owner.write(new_owner); + self + .emit( + OwnershipTransferred { previous_owner: previous_owner, new_owner: new_owner } + ); } } - impl OwnableCamelImpl of interface::IOwnableCamel { - fn owner() -> ContractAddress { - OwnableImpl::owner() + #[external(v0)] + impl OwnableImpl of interface::IOwnable { + fn owner(self: @ContractState) -> ContractAddress { + self._owner.read() } - fn transferOwnership(newOwner: ContractAddress) { - OwnableImpl::transfer_ownership(newOwner); + fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { + assert(!new_owner.is_zero(), 'New owner is the zero address'); + self.assert_only_owner(); + self._transfer_ownership(new_owner); } - fn renounceOwnership() { - OwnableImpl::renounce_ownership(); + fn renounce_ownership(ref self: ContractState) { + self.assert_only_owner(); + self._transfer_ownership(Zeroable::zero()); } } - #[view] - fn owner() -> ContractAddress { - OwnableImpl::owner() - } - - #[external] - fn transfer_ownership(new_owner: ContractAddress) { - OwnableImpl::transfer_ownership(new_owner); - } - - #[external] - fn transferOwnership(newOwner: ContractAddress) { - OwnableCamelImpl::transferOwnership(newOwner); - } - - #[external] - fn renounce_ownership() { - OwnableImpl::renounce_ownership(); - } - - #[external] - fn renounceOwnership() { - OwnableCamelImpl::renounceOwnership(); - } - - // Internals - - #[internal] - fn initializer(owner: ContractAddress) { - _transfer_ownership(owner); - } - - #[internal] - fn assert_only_owner() { - let owner: ContractAddress = _owner::read(); - let caller: ContractAddress = get_caller_address(); - assert(!caller.is_zero(), 'Caller is the zero address'); - assert(caller == owner, 'Caller is not the owner'); - } + #[external(v0)] + impl OwnableCamelOnlyImpl of interface::IOwnableCamelOnly { + fn transferOwnership(ref self: ContractState, newOwner: ContractAddress) { + OwnableImpl::transfer_ownership(ref self, newOwner); + } - #[internal] - fn _transfer_ownership(new_owner: ContractAddress) { - let previous_owner: ContractAddress = _owner::read(); - _owner::write(new_owner); - OwnershipTransferred(previous_owner, new_owner); + fn renounceOwnership(ref self: ContractState) { + OwnableImpl::renounce_ownership(ref self); + } } } diff --git a/src/openzeppelin/lib.cairo b/src/openzeppelin/lib.cairo index ef3dfb94f..e03376525 100644 --- a/src/openzeppelin/lib.cairo +++ b/src/openzeppelin/lib.cairo @@ -1,4 +1,4 @@ -// mod access; +mod access; mod account; mod introspection; mod security; diff --git a/src/openzeppelin/tests.cairo b/src/openzeppelin/tests.cairo index d2a724b37..cc78a11ec 100644 --- a/src/openzeppelin/tests.cairo +++ b/src/openzeppelin/tests.cairo @@ -1,4 +1,4 @@ -// mod access; +mod access; mod account; mod introspection; mod mocks; diff --git a/src/openzeppelin/tests/access.cairo b/src/openzeppelin/tests/access.cairo index 149c62b59..b40958d1d 100644 --- a/src/openzeppelin/tests/access.cairo +++ b/src/openzeppelin/tests/access.cairo @@ -1,4 +1,4 @@ -mod test_accesscontrol; -mod test_dual_accesscontrol; +//mod test_accesscontrol; +//mod test_dual_accesscontrol; mod test_ownable; mod test_dual_ownable; diff --git a/src/openzeppelin/tests/access/test_dual_ownable.cairo b/src/openzeppelin/tests/access/test_dual_ownable.cairo index aa86d3874..672df30ce 100644 --- a/src/openzeppelin/tests/access/test_dual_ownable.cairo +++ b/src/openzeppelin/tests/access/test_dual_ownable.cairo @@ -1,4 +1,3 @@ -use array::ArrayTrait; use starknet::ContractAddress; use starknet::contract_address_const; use starknet::testing::set_caller_address; @@ -6,9 +5,9 @@ use starknet::testing::set_contract_address; use zeroable::Zeroable; use openzeppelin::access::ownable::interface::IOwnableDispatcher; -use openzeppelin::access::ownable::interface::IOwnableCamelDispatcher; +use openzeppelin::access::ownable::interface::IOwnableCamelOnlyDispatcher; use openzeppelin::access::ownable::interface::IOwnableDispatcherTrait; -use openzeppelin::access::ownable::interface::IOwnableCamelDispatcherTrait; +use openzeppelin::access::ownable::interface::IOwnableCamelOnlyDispatcherTrait; use openzeppelin::access::ownable::dual_ownable::DualCaseOwnableTrait; use openzeppelin::access::ownable::dual_ownable::DualCaseOwnable; use openzeppelin::tests::mocks::dual_ownable_mocks::SnakeOwnableMock; @@ -42,13 +41,13 @@ fn setup_snake() -> (DualCaseOwnable, IOwnableDispatcher) { (DualCaseOwnable { contract_address: target }, IOwnableDispatcher { contract_address: target }) } -fn setup_camel() -> (DualCaseOwnable, IOwnableCamelDispatcher) { +fn setup_camel() -> (DualCaseOwnable, IOwnableCamelOnlyDispatcher) { let mut calldata = ArrayTrait::new(); calldata.append_serde(OWNER()); let target = utils::deploy(CamelOwnableMock::TEST_CLASS_HASH, calldata); ( DualCaseOwnable { contract_address: target }, - IOwnableCamelDispatcher { contract_address: target } + IOwnableCamelOnlyDispatcher { contract_address: target } ) } @@ -158,10 +157,10 @@ fn test_dual_renounce_ownership_exists_and_panics() { #[test] #[available_gas(2000000)] fn test_dual_transferOwnership() { - let (dispatcher, target) = setup_camel(); + let (dispatcher, _) = setup_camel(); set_contract_address(OWNER()); dispatcher.transfer_ownership(NEW_OWNER()); - assert(target.owner() == NEW_OWNER(), 'Should be new owner'); + assert(dispatcher.owner() == NEW_OWNER(), 'Should be new owner'); } #[test] @@ -175,10 +174,10 @@ fn test_dual_transferOwnership_exists_and_panics() { #[test] #[available_gas(2000000)] fn test_dual_renounceOwnership() { - let (dispatcher, target) = setup_camel(); + let (dispatcher, _) = setup_camel(); set_contract_address(OWNER()); dispatcher.renounce_ownership(); - assert(target.owner().is_zero(), 'Should be zero'); + assert(dispatcher.owner().is_zero(), 'Should be zero'); } #[test] diff --git a/src/openzeppelin/tests/access/test_ownable.cairo b/src/openzeppelin/tests/access/test_ownable.cairo index a3a4f0af9..2b906dbed 100644 --- a/src/openzeppelin/tests/access/test_ownable.cairo +++ b/src/openzeppelin/tests/access/test_ownable.cairo @@ -1,25 +1,37 @@ -use openzeppelin::access::ownable::Ownable; - use starknet::ContractAddress; use starknet::contract_address_const; use starknet::testing; use zeroable::Zeroable; +use openzeppelin::access::ownable::Ownable; +use openzeppelin::access::ownable::Ownable::OwnableImpl; +use openzeppelin::access::ownable::Ownable::OwnableCamelOnlyImpl; +use openzeppelin::access::ownable::Ownable::InternalImpl; +use openzeppelin::access::ownable::Ownable::_owner::InternalContractStateTrait; fn ZERO() -> ContractAddress { contract_address_const::<0>() } fn OWNER() -> ContractAddress { - contract_address_const::<1>() + contract_address_const::<10>() } fn OTHER() -> ContractAddress { - contract_address_const::<2>() + contract_address_const::<20>() } -fn setup() { - testing::set_caller_address(OWNER()); - Ownable::initializer(OWNER()); +// +// Setup +// + +fn STATE() -> Ownable::ContractState { + Ownable::contract_state_for_testing() +} + +fn setup() -> Ownable::ContractState { + let mut state = STATE(); + InternalImpl::initializer(ref state, OWNER()); + state } // @@ -29,9 +41,51 @@ fn setup() { #[test] #[available_gas(2000000)] fn test_initializer() { - assert(Ownable::owner().is_zero(), 'Should be zero'); - setup(); - assert(Ownable::owner() == OWNER(), 'Owner should be set'); + let mut state = STATE(); + assert(state._owner.read().is_zero(), 'Should be zero'); + InternalImpl::initializer(ref state, OWNER()); + assert(state._owner.read() == OWNER(), 'Owner should be set'); +} + +// +// assert_only_owner +// + +#[test] +#[available_gas(2000000)] +fn test_assert_only_owner() { + let state = setup(); + testing::set_caller_address(OWNER()); + InternalImpl::assert_only_owner(@state); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Caller is not the owner', ))] +fn test_assert_only_owner_when_not_owner() { + let state = setup(); + testing::set_caller_address(OTHER()); + InternalImpl::assert_only_owner(@state); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Caller is the zero address', ))] +fn test_assert_only_owner_when_caller_zero() { + let state = setup(); + InternalImpl::assert_only_owner(@state); +} + +// +// _transfer_ownership +// + +#[test] +#[available_gas(2000000)] +fn test__transfer_ownership() { + let mut state = setup(); + InternalImpl::_transfer_ownership(ref state, OTHER()); + assert(state._owner.read() == OTHER(), 'Owner should be OTHER'); } // @@ -41,65 +95,71 @@ fn test_initializer() { #[test] #[available_gas(2000000)] fn test_transfer_ownership() { - setup(); - Ownable::transfer_ownership(OTHER()); - assert(Ownable::owner() == OTHER(), 'Should transfer ownership'); + let mut state = setup(); + testing::set_caller_address(OWNER()); + OwnableImpl::transfer_ownership(ref state, OTHER()); + assert(OwnableImpl::owner(@state) == OTHER(), 'Should transfer ownership'); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('New owner is the zero address', ))] fn test_transfer_ownership_to_zero() { - setup(); - Ownable::transfer_ownership(ZERO()); + let mut state = setup(); + testing::set_caller_address(OWNER()); + OwnableImpl::transfer_ownership(ref state, ZERO()); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('Caller is the zero address', ))] fn test_transfer_ownership_from_zero() { - Ownable::transfer_ownership(OTHER()); + let mut state = setup(); + OwnableImpl::transfer_ownership(ref state, OTHER()); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('Caller is not the owner', ))] fn test_transfer_ownership_from_nonowner() { - setup(); + let mut state = setup(); testing::set_caller_address(OTHER()); - Ownable::transfer_ownership(OTHER()); + OwnableImpl::transfer_ownership(ref state, OTHER()); } #[test] #[available_gas(2000000)] fn test_transferOwnership() { - setup(); - Ownable::transferOwnership(OTHER()); - assert(Ownable::owner() == OTHER(), 'Should transfer ownership'); + let mut state = setup(); + testing::set_caller_address(OWNER()); + OwnableCamelOnlyImpl::transferOwnership(ref state, OTHER()); + assert(OwnableImpl::owner(@state) == OTHER(), 'Should transfer ownership'); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('New owner is the zero address', ))] fn test_transferOwnership_to_zero() { - setup(); - Ownable::transferOwnership(ZERO()); + let mut state = setup(); + testing::set_caller_address(OWNER()); + OwnableCamelOnlyImpl::transferOwnership(ref state, ZERO()); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('Caller is the zero address', ))] fn test_transferOwnership_from_zero() { - Ownable::transferOwnership(OTHER()); + let mut state = setup(); + OwnableCamelOnlyImpl::transferOwnership(ref state, OTHER()); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('Caller is not the owner', ))] fn test_transferOwnership_from_nonowner() { - setup(); + let mut state = setup(); testing::set_caller_address(OTHER()); - Ownable::transferOwnership(OTHER()); + OwnableCamelOnlyImpl::transferOwnership(ref state, OTHER()); } // @@ -109,52 +169,51 @@ fn test_transferOwnership_from_nonowner() { #[test] #[available_gas(2000000)] fn test_renounce_ownership() { - setup(); - Ownable::renounce_ownership(); - assert(Ownable::owner() == ZERO(), 'Should renounce ownership'); + let mut state = setup(); + testing::set_caller_address(OWNER()); + OwnableImpl::renounce_ownership(ref state); + assert(OwnableImpl::owner(@state) == ZERO(), 'Should renounce ownership'); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('Caller is the zero address', ))] fn test_renounce_ownership_from_zero_address() { - setup(); - testing::set_caller_address(ZERO()); - Ownable::renounce_ownership(); + let mut state = setup(); + OwnableImpl::renounce_ownership(ref state); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('Caller is not the owner', ))] fn test_renounce_ownership_from_nonowner() { - setup(); + let mut state = setup(); testing::set_caller_address(OTHER()); - Ownable::renounce_ownership(); + OwnableImpl::renounce_ownership(ref state); } #[test] #[available_gas(2000000)] fn test_renounceOwnership() { - setup(); - Ownable::renounceOwnership(); - assert(Ownable::owner() == ZERO(), 'Should renounce ownership'); + let mut state = setup(); + testing::set_caller_address(OWNER()); + OwnableCamelOnlyImpl::renounceOwnership(ref state); + assert(OwnableImpl::owner(@state) == ZERO(), 'Should renounce ownership'); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('Caller is the zero address', ))] fn test_renounceOwnership_from_zero_address() { - setup(); - testing::set_caller_address(ZERO()); - Ownable::renounceOwnership(); + let mut state = setup(); + OwnableCamelOnlyImpl::renounceOwnership(ref state); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('Caller is not the owner', ))] fn test_renounceOwnership_from_nonowner() { - setup(); + let mut state = setup(); testing::set_caller_address(OTHER()); - Ownable::renounceOwnership(); + OwnableCamelOnlyImpl::renounceOwnership(ref state); } - diff --git a/src/openzeppelin/tests/mocks.cairo b/src/openzeppelin/tests/mocks.cairo index 0e838248e..4670b5c0c 100644 --- a/src/openzeppelin/tests/mocks.cairo +++ b/src/openzeppelin/tests/mocks.cairo @@ -12,7 +12,7 @@ mod account_panic_mock; mod camel_account_mock; // mod snake_accesscontrol_mock; mod snake_account_mock; -// mod dual_ownable_mocks; +mod dual_ownable_mocks; mod snake721_mock; mod camel721_mock; mod non_implementing_mock; diff --git a/src/openzeppelin/tests/mocks/dual_ownable_mocks.cairo b/src/openzeppelin/tests/mocks/dual_ownable_mocks.cairo index 407d63e3b..7826e859e 100644 --- a/src/openzeppelin/tests/mocks/dual_ownable_mocks.cairo +++ b/src/openzeppelin/tests/mocks/dual_ownable_mocks.cairo @@ -1,93 +1,116 @@ -#[contract] +use openzeppelin::access::ownable::Ownable; + +#[starknet::contract] mod SnakeOwnableMock { use starknet::ContractAddress; - use openzeppelin::access::ownable::Ownable; + use super::Ownable; + + #[storage] + struct Storage {} #[constructor] - fn constructor(owner: ContractAddress) { - Ownable::initializer(owner); + fn constructor(self: @ContractState, owner: ContractAddress) { + let mut unsafe_state = Ownable::unsafe_new_contract_state(); + Ownable::InternalImpl::initializer(ref unsafe_state, owner); } - #[view] - fn owner() -> ContractAddress { - Ownable::owner() + #[external(v0)] + fn owner(self: @ContractState) -> ContractAddress { + let unsafe_state = Ownable::unsafe_new_contract_state(); + Ownable::OwnableImpl::owner(@unsafe_state) } - #[external] - fn transfer_ownership(new_owner: ContractAddress) { - Ownable::transfer_ownership(new_owner); + #[external(v0)] + fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { + let mut unsafe_state = Ownable::unsafe_new_contract_state(); + Ownable::OwnableImpl::transfer_ownership(ref unsafe_state, new_owner); } - #[external] - fn renounce_ownership() { - Ownable::renounce_ownership(); + #[external(v0)] + fn renounce_ownership(ref self: ContractState) { + let mut unsafe_state = Ownable::unsafe_new_contract_state(); + Ownable::OwnableImpl::renounce_ownership(ref unsafe_state); } } -#[contract] +#[starknet::contract] mod CamelOwnableMock { use starknet::ContractAddress; - use openzeppelin::access::ownable::Ownable; + use super::Ownable; + + #[storage] + struct Storage {} #[constructor] - fn constructor(owner: ContractAddress) { - Ownable::initializer(owner); + fn constructor(self: @ContractState, owner: ContractAddress) { + let mut unsafe_state = Ownable::unsafe_new_contract_state(); + Ownable::InternalImpl::initializer(ref unsafe_state, owner); } - #[view] - fn owner() -> ContractAddress { - Ownable::owner() + #[external(v0)] + fn owner(self: @ContractState) -> ContractAddress { + let unsafe_state = Ownable::unsafe_new_contract_state(); + Ownable::OwnableImpl::owner(@unsafe_state) } - #[external] - fn transferOwnership(newOwner: ContractAddress) { - Ownable::transferOwnership(newOwner); + #[external(v0)] + fn transferOwnership(ref self: ContractState, newOwner: ContractAddress) { + let mut unsafe_state = Ownable::unsafe_new_contract_state(); + Ownable::OwnableCamelOnlyImpl::transferOwnership(ref unsafe_state, newOwner); } - #[external] - fn renounceOwnership() { - Ownable::renounceOwnership(); + #[external(v0)] + fn renounceOwnership(ref self: ContractState) { + let mut unsafe_state = Ownable::unsafe_new_contract_state(); + Ownable::OwnableCamelOnlyImpl::renounceOwnership(ref unsafe_state); } } -#[contract] +#[starknet::contract] mod SnakeOwnablePanicMock { use starknet::ContractAddress; + use zeroable::Zeroable; - #[view] - fn owner() -> ContractAddress { + #[storage] + struct Storage {} + + #[external(v0)] + fn owner(self: @ContractState) -> ContractAddress { panic_with_felt252('Some error'); Zeroable::zero() } - #[external] - fn transfer_ownership(new_owner: ContractAddress) { + #[external(v0)] + fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { panic_with_felt252('Some error'); } - #[external] - fn renounce_ownership() { + #[external(v0)] + fn renounce_ownership(ref self: ContractState) { panic_with_felt252('Some error'); } } -#[contract] +#[starknet::contract] mod CamelOwnablePanicMock { use starknet::ContractAddress; - #[view] - fn owner() -> ContractAddress { + #[storage] + struct Storage {} + + #[external(v0)] + fn owner(self: @ContractState) -> ContractAddress { panic_with_felt252('Some error'); Zeroable::zero() } - #[external] - fn transfer_ownership(new_owner: ContractAddress) { + #[external(v0)] + fn transferOwnership(ref self: ContractState, newOwner: ContractAddress) { panic_with_felt252('Some error'); } - #[external] - fn renounce_ownership() { + #[external(v0)] + fn renounceOwnership(ref self: ContractState) { panic_with_felt252('Some error'); } } From 0b40485b8e332af7ff5a0472171b05455648f0ad Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Sat, 22 Jul 2023 17:30:03 +0200 Subject: [PATCH 072/246] Migrate ERC20 to Cairo 2 (#669) * feat: fix tests * feat: apply review updates --- src/openzeppelin/account.cairo | 13 +- src/openzeppelin/account/account.cairo | 29 -- src/openzeppelin/account/interface.cairo | 29 ++ .../tests/account/test_account.cairo | 224 +++++----- src/openzeppelin/tests/mocks.cairo | 7 +- .../tests/mocks/camel20_mock.cairo | 81 ++-- .../tests/mocks/erc20_panic.cairo | 83 ++-- .../tests/mocks/mock_pausable.cairo | 34 -- .../tests/mocks/snake20_mock.cairo | 81 ++-- src/openzeppelin/tests/token.cairo | 5 +- .../tests/token/test_dual20.cairo | 10 +- src/openzeppelin/tests/token/test_erc20.cairo | 321 +++++++------- src/openzeppelin/token.cairo | 2 +- src/openzeppelin/token/erc20.cairo | 6 +- src/openzeppelin/token/erc20/dual20.cairo | 22 +- src/openzeppelin/token/erc20/erc20.cairo | 411 +++++++----------- src/openzeppelin/token/erc20/interface.cairo | 110 +++-- 17 files changed, 725 insertions(+), 743 deletions(-) delete mode 100644 src/openzeppelin/tests/mocks/mock_pausable.cairo diff --git a/src/openzeppelin/account.cairo b/src/openzeppelin/account.cairo index 47e57df8a..d8f6cab7a 100644 --- a/src/openzeppelin/account.cairo +++ b/src/openzeppelin/account.cairo @@ -1,8 +1,11 @@ mod account; -use account::{ - Account, AccountCamelABIDispatcher, AccountCamelABIDispatcherTrait, AccountABIDispatcher, - AccountABIDispatcherTrait, TRANSACTION_VERSION, QUERY_VERSION -}; - mod dual_account; mod interface; + +use account::Account; +use account::TRANSACTION_VERSION; +use account::QUERY_VERSION; +use interface::AccountCamelABIDispatcher; +use interface::AccountCamelABIDispatcherTrait; +use interface::AccountABIDispatcher; +use interface::AccountABIDispatcherTrait; diff --git a/src/openzeppelin/account/account.cairo b/src/openzeppelin/account/account.cairo index e1a3d57dd..21b2d79d7 100644 --- a/src/openzeppelin/account/account.cairo +++ b/src/openzeppelin/account/account.cairo @@ -10,35 +10,6 @@ const TRANSACTION_VERSION: felt252 = 1; // 2**128 + TRANSACTION_VERSION const QUERY_VERSION: felt252 = 340282366920938463463374607431768211457; -#[starknet::interface] -trait AccountABI { - fn __execute__(self: @TState, calls: Array) -> Array>; - fn __validate__(self: @TState, calls: Array) -> felt252; - fn __validate_declare__(self: @TState, class_hash: felt252) -> felt252; - fn __validate_deploy__( - self: @TState, class_hash: felt252, contract_address_salt: felt252, _public_key: felt252 - ) -> felt252; - fn set_public_key(ref self: TState, new_public_key: felt252); - fn get_public_key(self: @TState) -> felt252; - fn is_valid_signature(self: @TState, hash: felt252, signature: Array) -> felt252; - fn supports_interface(self: @TState, interface_id: felt252) -> bool; -} - -// Entry points case-convention is enforced by the protocol -#[starknet::interface] -trait AccountCamelABI { - fn __execute__(self: @TState, calls: Array) -> Array>; - fn __validate__(self: @TState, calls: Array) -> felt252; - fn __validate_declare__(self: @TState, classHash: felt252) -> felt252; - fn __validate_deploy__( - self: @TState, classHash: felt252, contractAddressSalt: felt252, _publicKey: felt252 - ) -> felt252; - fn setPublicKey(ref self: TState, newPublicKey: felt252); - fn getPublicKey(self: @TState) -> felt252; - fn isValidSignature(self: @TState, hash: felt252, signature: Array) -> felt252; - fn supportsInterface(self: @TState, interfaceId: felt252) -> bool; -} - trait PublicKeyTrait { fn set_public_key(ref self: TState, new_public_key: felt252); fn get_public_key(self: @TState) -> felt252; diff --git a/src/openzeppelin/account/interface.cairo b/src/openzeppelin/account/interface.cairo index 2027c0c99..e557b6dec 100644 --- a/src/openzeppelin/account/interface.cairo +++ b/src/openzeppelin/account/interface.cairo @@ -21,3 +21,32 @@ trait ISRC6CamelOnly { trait IDeclarer { fn __validate_declare__(self: @TState, class_hash: felt252) -> felt252; } + +#[starknet::interface] +trait AccountABI { + fn __execute__(self: @TState, calls: Array) -> Array>; + fn __validate__(self: @TState, calls: Array) -> felt252; + fn __validate_declare__(self: @TState, class_hash: felt252) -> felt252; + fn __validate_deploy__( + self: @TState, class_hash: felt252, contract_address_salt: felt252, _public_key: felt252 + ) -> felt252; + fn set_public_key(ref self: TState, new_public_key: felt252); + fn get_public_key(self: @TState) -> felt252; + fn is_valid_signature(self: @TState, hash: felt252, signature: Array) -> felt252; + fn supports_interface(self: @TState, interface_id: felt252) -> bool; +} + +// Entry points case-convention is enforced by the protocol +#[starknet::interface] +trait AccountCamelABI { + fn __execute__(self: @TState, calls: Array) -> Array>; + fn __validate__(self: @TState, calls: Array) -> felt252; + fn __validate_declare__(self: @TState, classHash: felt252) -> felt252; + fn __validate_deploy__( + self: @TState, classHash: felt252, contractAddressSalt: felt252, _publicKey: felt252 + ) -> felt252; + fn setPublicKey(ref self: TState, newPublicKey: felt252); + fn getPublicKey(self: @TState) -> felt252; + fn isValidSignature(self: @TState, hash: felt252, signature: Array) -> felt252; + fn supportsInterface(self: @TState, interfaceId: felt252) -> bool; +} diff --git a/src/openzeppelin/tests/account/test_account.cairo b/src/openzeppelin/tests/account/test_account.cairo index 48ab02685..b5d9828f7 100644 --- a/src/openzeppelin/tests/account/test_account.cairo +++ b/src/openzeppelin/tests/account/test_account.cairo @@ -15,9 +15,9 @@ use openzeppelin::account::QUERY_VERSION; use openzeppelin::account::TRANSACTION_VERSION; use openzeppelin::introspection::interface::ISRC5_ID; use openzeppelin::tests::utils; -// use openzeppelin::token::erc20::ERC20; -// use openzeppelin::token::erc20::interface::IERC20Dispatcher; -// use openzeppelin::token::erc20::interface::IERC20DispatcherTrait; +use openzeppelin::token::erc20::ERC20; +use openzeppelin::token::erc20::interface::IERC20Dispatcher; +use openzeppelin::token::erc20::interface::IERC20DispatcherTrait; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; @@ -78,19 +78,19 @@ fn setup_dispatcher(data: Option<@SignedTransactionData>) -> AccountABIDispatche AccountABIDispatcher { contract_address: address } } -// fn deploy_erc20(recipient: ContractAddress, initial_supply: u256) -> IERC20Dispatcher { -// let name = 0; -// let symbol = 0; -// let mut calldata = array![]; +fn deploy_erc20(recipient: ContractAddress, initial_supply: u256) -> IERC20Dispatcher { + let name = 0; + let symbol = 0; + let mut calldata = array![]; -// calldata.append_serde(name); -// calldata.append_serde(symbol); -// calldata.append_serde(initial_supply); -// calldata.append_serde(recipient); + calldata.append_serde(name); + calldata.append_serde(symbol); + calldata.append_serde(initial_supply); + calldata.append_serde(recipient); -// let address = utils::deploy(ERC20::TEST_CLASS_HASH, calldata); -// IERC20Dispatcher { contract_address: address } -// } + let address = utils::deploy(ERC20::TEST_CLASS_HASH, calldata); + IERC20Dispatcher { contract_address: address } +} // // constructor @@ -281,59 +281,59 @@ fn test_validate_declare_empty_signature() { account.__validate_declare__(CLASS_HASH()); } -// fn test_execute_with_version(version: Option) { -// let data = SIGNED_TX_DATA(); -// let account = setup_dispatcher(Option::Some(@data)); -// let erc20 = deploy_erc20(account.contract_address, 1000); -// let recipient = contract_address_const::<0x123>(); - -// // Craft call and add to calls array -// let mut calldata = array![]; -// let amount: u256 = 200; -// calldata.append_serde(recipient); -// calldata.append_serde(amount); -// let call = Call { -// to: erc20.contract_address, selector: selectors::transfer, calldata: calldata -// }; -// let mut calls = array![]; -// calls.append(call); - -// // Handle version for test -// if version.is_some() { -// testing::set_version(version.unwrap()); -// } - -// // Execute -// let ret = account.__execute__(calls); - -// // Assert that the transfer was successful -// assert(erc20.balance_of(account.contract_address) == 800, 'Should have remainder'); -// assert(erc20.balance_of(recipient) == amount, 'Should have transferred'); - -// // Test return value -// let mut call_serialized_retval = *ret.at(0); -// let call_retval = Serde::::deserialize(ref call_serialized_retval); -// assert(call_retval.unwrap(), 'Should have succeeded'); -// } - -// #[test] -// #[available_gas(2000000)] -// fn test_execute() { -// test_execute_with_version(Option::None(())); -// } - -// #[test] -// #[available_gas(2000000)] -// fn test_execute_query_version() { -// test_execute_with_version(Option::Some(QUERY_VERSION)); -// } - -// #[test] -// #[available_gas(2000000)] -// #[should_panic(expected: ('Account: invalid tx version', 'ENTRYPOINT_FAILED'))] -// fn test_execute_invalid_version() { -// test_execute_with_version(Option::Some(TRANSACTION_VERSION - 1)); -// } +fn test_execute_with_version(version: Option) { + let data = SIGNED_TX_DATA(); + let account = setup_dispatcher(Option::Some(@data)); + let erc20 = deploy_erc20(account.contract_address, 1000); + let recipient = contract_address_const::<0x123>(); + + // Craft call and add to calls array + let mut calldata = array![]; + let amount: u256 = 200; + calldata.append_serde(recipient); + calldata.append_serde(amount); + let call = Call { + to: erc20.contract_address, selector: selectors::transfer, calldata: calldata + }; + let mut calls = array![]; + calls.append(call); + + // Handle version for test + if version.is_some() { + testing::set_version(version.unwrap()); + } + + // Execute + let ret = account.__execute__(calls); + + // Assert that the transfer was successful + assert(erc20.balance_of(account.contract_address) == 800, 'Should have remainder'); + assert(erc20.balance_of(recipient) == amount, 'Should have transferred'); + + // Test return value + let mut call_serialized_retval = *ret.at(0); + let call_retval = Serde::::deserialize(ref call_serialized_retval); + assert(call_retval.unwrap(), 'Should have succeeded'); +} + +#[test] +#[available_gas(2000000)] +fn test_execute() { + test_execute_with_version(Option::None(())); +} + +#[test] +#[available_gas(2000000)] +fn test_execute_query_version() { + test_execute_with_version(Option::Some(QUERY_VERSION)); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Account: invalid tx version', 'ENTRYPOINT_FAILED'))] +fn test_execute_invalid_version() { + test_execute_with_version(Option::Some(TRANSACTION_VERSION - 1)); +} #[test] #[available_gas(2000000)] @@ -356,51 +356,51 @@ fn test_validate_invalid() { account.__validate__(calls); } -// #[test] -// #[available_gas(2000000)] -// fn test_multicall() { -// let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); -// let erc20 = deploy_erc20(account.contract_address, 1000); -// let recipient1 = contract_address_const::<0x123>(); -// let recipient2 = contract_address_const::<0x456>(); -// let mut calls = array![]; - -// // Craft call1 -// let mut calldata1 = array![]; -// let amount1: u256 = 300; -// calldata1.append_serde(recipient1); -// calldata1.append_serde(amount1); -// let call1 = Call { -// to: erc20.contract_address, selector: selectors::transfer, calldata: calldata1 -// }; - -// // Craft call2 -// let mut calldata2 = array![]; -// let amount2: u256 = 500; -// calldata2.append_serde(recipient2); -// calldata2.append_serde(amount2); -// let call2 = Call { -// to: erc20.contract_address, selector: selectors::transfer, calldata: calldata2 -// }; - -// // Bundle calls and exeute -// calls.append(call1); -// calls.append(call2); -// let ret = account.__execute__(calls); - -// // Assert that the transfers were successful -// assert(erc20.balance_of(account.contract_address) == 200, 'Should have remainder'); -// assert(erc20.balance_of(recipient1) == 300, 'Should have transferred'); -// assert(erc20.balance_of(recipient2) == 500, 'Should have transferred'); - -// // Test return value -// let mut call1_serialized_retval = *ret.at(0); -// let mut call2_serialized_retval = *ret.at(1); -// let call1_retval = Serde::::deserialize(ref call1_serialized_retval); -// let call2_retval = Serde::::deserialize(ref call2_serialized_retval); -// assert(call1_retval.unwrap(), 'Should have succeeded'); -// assert(call2_retval.unwrap(), 'Should have succeeded'); -// } +#[test] +#[available_gas(20000000)] +fn test_multicall() { + let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let erc20 = deploy_erc20(account.contract_address, 1000); + let recipient1 = contract_address_const::<0x123>(); + let recipient2 = contract_address_const::<0x456>(); + let mut calls = array![]; + + // Craft call1 + let mut calldata1 = array![]; + let amount1: u256 = 300; + calldata1.append_serde(recipient1); + calldata1.append_serde(amount1); + let call1 = Call { + to: erc20.contract_address, selector: selectors::transfer, calldata: calldata1 + }; + + // Craft call2 + let mut calldata2 = array![]; + let amount2: u256 = 500; + calldata2.append_serde(recipient2); + calldata2.append_serde(amount2); + let call2 = Call { + to: erc20.contract_address, selector: selectors::transfer, calldata: calldata2 + }; + + // Bundle calls and exeute + calls.append(call1); + calls.append(call2); + let ret = account.__execute__(calls); + + // Assert that the transfers were successful + assert(erc20.balance_of(account.contract_address) == 200, 'Should have remainder'); + assert(erc20.balance_of(recipient1) == 300, 'Should have transferred'); + assert(erc20.balance_of(recipient2) == 500, 'Should have transferred'); + + // Test return value + let mut call1_serialized_retval = *ret.at(0); + let mut call2_serialized_retval = *ret.at(1); + let call1_retval = Serde::::deserialize(ref call1_serialized_retval); + let call2_retval = Serde::::deserialize(ref call2_serialized_retval); + assert(call1_retval.unwrap(), 'Should have succeeded'); + assert(call2_retval.unwrap(), 'Should have succeeded'); +} #[test] #[available_gas(2000000)] diff --git a/src/openzeppelin/tests/mocks.cairo b/src/openzeppelin/tests/mocks.cairo index 4670b5c0c..4a8530b24 100644 --- a/src/openzeppelin/tests/mocks.cairo +++ b/src/openzeppelin/tests/mocks.cairo @@ -2,10 +2,9 @@ mod reentrancy_attacker_mock; mod reentrancy_mock; mod erc721_receiver; mod erc721_panic_mock; -// mod mock_pausable; -// mod camel20_mock; -// mod snake20_mock; -// mod erc20_panic; +mod camel20_mock; +mod snake20_mock; +mod erc20_panic; // mod accesscontrol_panic_mock; mod account_panic_mock; // mod camel_accesscontrol_mock; diff --git a/src/openzeppelin/tests/mocks/camel20_mock.cairo b/src/openzeppelin/tests/mocks/camel20_mock.cairo index 3b9bd1048..6eab0e06f 100644 --- a/src/openzeppelin/tests/mocks/camel20_mock.cairo +++ b/src/openzeppelin/tests/mocks/camel20_mock.cairo @@ -1,58 +1,77 @@ -#[contract] +#[starknet::contract] mod CamelERC20Mock { use starknet::ContractAddress; use openzeppelin::token::erc20::ERC20; + #[storage] + struct Storage {} + #[constructor] fn constructor( - name: felt252, symbol: felt252, initial_supply: u256, recipient: ContractAddress + ref self: ContractState, + name: felt252, + symbol: felt252, + initial_supply: u256, + recipient: ContractAddress ) { - ERC20::initializer(name, symbol); - ERC20::_mint(recipient, initial_supply); + let mut unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::InternalImpl::initializer(ref unsafe_state, name, symbol); + ERC20::InternalImpl::_mint(ref unsafe_state, recipient, initial_supply); } - #[view] - fn name() -> felt252 { - ERC20::name() + #[external(v0)] + fn name(self: @ContractState) -> felt252 { + let unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::ERC20Impl::name(@unsafe_state) } - #[view] - fn symbol() -> felt252 { - ERC20::symbol() + #[external(v0)] + fn symbol(self: @ContractState) -> felt252 { + let unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::ERC20Impl::symbol(@unsafe_state) } - #[view] - fn decimals() -> u8 { - ERC20::decimals() + #[external(v0)] + fn decimals(self: @ContractState) -> u8 { + let unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::ERC20Impl::decimals(@unsafe_state) } - #[view] - fn totalSupply() -> u256 { - ERC20::totalSupply() + #[external(v0)] + fn totalSupply(self: @ContractState) -> u256 { + let unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::ERC20CamelOnlyImpl::totalSupply(@unsafe_state) } - #[view] - fn balanceOf(account: ContractAddress) -> u256 { - ERC20::balanceOf(account) + #[external(v0)] + fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 { + let unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::ERC20CamelOnlyImpl::balanceOf(@unsafe_state, account) } - #[view] - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { - ERC20::allowance(owner, spender) + #[external(v0)] + fn allowance(self: @ContractState, owner: ContractAddress, spender: ContractAddress) -> u256 { + let unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::ERC20Impl::allowance(@unsafe_state, owner, spender) } - #[external] - fn transfer(recipient: ContractAddress, amount: u256) -> bool { - ERC20::transfer(recipient, amount) + #[external(v0)] + fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) -> bool { + let mut unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::ERC20Impl::transfer(ref unsafe_state, recipient, amount) } - #[external] - fn transferFrom(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { - ERC20::transferFrom(sender, recipient, amount) + #[external(v0)] + fn transferFrom( + ref self: ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool { + let mut unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::ERC20CamelOnlyImpl::transferFrom(ref unsafe_state, sender, recipient, amount) } - #[external] - fn approve(spender: ContractAddress, amount: u256) -> bool { - ERC20::approve(spender, amount) + #[external(v0)] + fn approve(ref self: ContractState, spender: ContractAddress, amount: u256) -> bool { + let mut unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::ERC20Impl::approve(ref unsafe_state, spender, amount) } } diff --git a/src/openzeppelin/tests/mocks/erc20_panic.cairo b/src/openzeppelin/tests/mocks/erc20_panic.cairo index 89389bc74..c2a4fa9d2 100644 --- a/src/openzeppelin/tests/mocks/erc20_panic.cairo +++ b/src/openzeppelin/tests/mocks/erc20_panic.cairo @@ -1,96 +1,97 @@ // Although these modules are designed to panic, functions // still need a valid return value. We chose: // -// 3 for felt252 and u8 +// 3 for felt252, u8, and u256 // zero for ContractAddress // false for bool -// u256 { 3, 3 } for u256 -#[contract] +#[starknet::contract] mod SnakeERC20Panic { use starknet::ContractAddress; - /// - /// Agnostic - /// + #[storage] + struct Storage {} - #[view] - fn name() -> felt252 { + #[external(v0)] + fn name(self: @ContractState) -> felt252 { panic_with_felt252('Some error'); 3 } - #[view] - fn symbol() -> felt252 { + #[external(v0)] + fn symbol(self: @ContractState) -> felt252 { panic_with_felt252('Some error'); 3 } - #[view] - fn decimals() -> u8 { + #[external(v0)] + fn decimals(self: @ContractState) -> u8 { panic_with_felt252('Some error'); - 3_u8 + 3 } - #[view] - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { + #[external(v0)] + fn allowance(self: @ContractState, owner: ContractAddress, spender: ContractAddress) -> u256 { panic_with_felt252('Some error'); - u256 { low: 3, high: 3 } + 3 } - #[external] - fn transfer(recipient: ContractAddress, amount: u256) -> bool { + #[external(v0)] + fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) -> bool { panic_with_felt252('Some error'); false } - #[external] - fn approve(to: ContractAddress, token_id: u256) -> bool { + #[external(v0)] + fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) -> bool { panic_with_felt252('Some error'); false } - /// - /// Snake - /// - - #[view] - fn total_supply() -> u256 { + #[external(v0)] + fn total_supply(self: @ContractState) -> u256 { panic_with_felt252('Some error'); - u256 { low: 3, high: 3 } + 3 } - #[view] - fn balance_of(account: ContractAddress) -> u256 { + #[external(v0)] + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { panic_with_felt252('Some error'); - u256 { low: 3, high: 3 } + 3 } - #[external] - fn transfer_from(from: ContractAddress, to: ContractAddress, amount: u256) -> bool { + #[external(v0)] + fn transfer_from( + ref self: ContractState, from: ContractAddress, to: ContractAddress, amount: u256 + ) -> bool { panic_with_felt252('Some error'); false } } -#[contract] +#[starknet::contract] mod CamelERC20Panic { use starknet::ContractAddress; - #[view] - fn totalSupply() -> u256 { + #[storage] + struct Storage {} + + #[external(v0)] + fn totalSupply(self: @ContractState) -> u256 { panic_with_felt252('Some error'); - u256 { low: 3, high: 3 } + 3 } - #[view] - fn balanceOf(account: ContractAddress) -> u256 { + #[external(v0)] + fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 { panic_with_felt252('Some error'); - u256 { low: 3, high: 3 } + 3 } - #[external] - fn transferFrom(sender: ContractAddress, recipient: ContractAddress, amount: u256) { + #[external(v0)] + fn transferFrom( + ref self: ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) { panic_with_felt252('Some error'); } } diff --git a/src/openzeppelin/tests/mocks/mock_pausable.cairo b/src/openzeppelin/tests/mocks/mock_pausable.cairo deleted file mode 100644 index 799449b4a..000000000 --- a/src/openzeppelin/tests/mocks/mock_pausable.cairo +++ /dev/null @@ -1,34 +0,0 @@ -#[contract] -mod MockPausable { - use openzeppelin::security::pausable::Pausable; - - struct Storage { - counter: felt252 - } - - #[view] - fn is_paused() -> bool { - Pausable::is_paused() - } - - #[view] - fn get_count() -> felt252 { - counter::read() - } - - #[external] - fn assert_unpaused_and_increment() { - Pausable::assert_not_paused(); - counter::write(counter::read() + 1); - } - - #[external] - fn pause() { - Pausable::pause(); - } - - #[external] - fn unpause() { - Pausable::unpause(); - } -} diff --git a/src/openzeppelin/tests/mocks/snake20_mock.cairo b/src/openzeppelin/tests/mocks/snake20_mock.cairo index dd5a034f7..ba20d54b1 100644 --- a/src/openzeppelin/tests/mocks/snake20_mock.cairo +++ b/src/openzeppelin/tests/mocks/snake20_mock.cairo @@ -1,58 +1,77 @@ -#[contract] +#[starknet::contract] mod SnakeERC20Mock { use starknet::ContractAddress; use openzeppelin::token::erc20::ERC20; + #[storage] + struct Storage {} + #[constructor] fn constructor( - name: felt252, symbol: felt252, initial_supply: u256, recipient: ContractAddress + ref self: ContractState, + name: felt252, + symbol: felt252, + initial_supply: u256, + recipient: ContractAddress ) { - ERC20::initializer(name, symbol); - ERC20::_mint(recipient, initial_supply); + let mut unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::InternalImpl::initializer(ref unsafe_state, name, symbol); + ERC20::InternalImpl::_mint(ref unsafe_state, recipient, initial_supply); } - #[view] - fn name() -> felt252 { - ERC20::name() + #[external(v0)] + fn name(self: @ContractState) -> felt252 { + let unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::ERC20Impl::name(@unsafe_state) } - #[view] - fn symbol() -> felt252 { - ERC20::symbol() + #[external(v0)] + fn symbol(self: @ContractState) -> felt252 { + let unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::ERC20Impl::symbol(@unsafe_state) } - #[view] - fn decimals() -> u8 { - ERC20::decimals() + #[external(v0)] + fn decimals(self: @ContractState) -> u8 { + let unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::ERC20Impl::decimals(@unsafe_state) } - #[view] - fn total_supply() -> u256 { - ERC20::total_supply() + #[external(v0)] + fn total_supply(self: @ContractState) -> u256 { + let unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::ERC20Impl::total_supply(@unsafe_state) } - #[view] - fn balance_of(account: ContractAddress) -> u256 { - ERC20::balance_of(account) + #[external(v0)] + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { + let unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::ERC20Impl::balance_of(@unsafe_state, account) } - #[view] - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { - ERC20::allowance(owner, spender) + #[external(v0)] + fn allowance(self: @ContractState, owner: ContractAddress, spender: ContractAddress) -> u256 { + let unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::ERC20Impl::allowance(@unsafe_state, owner, spender) } - #[external] - fn transfer(recipient: ContractAddress, amount: u256) -> bool { - ERC20::transfer(recipient, amount) + #[external(v0)] + fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) -> bool { + let mut unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::ERC20Impl::transfer(ref unsafe_state, recipient, amount) } - #[external] - fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { - ERC20::transfer_from(sender, recipient, amount) + #[external(v0)] + fn transfer_from( + ref self: ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool { + let mut unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::ERC20Impl::transfer_from(ref unsafe_state, sender, recipient, amount) } - #[external] - fn approve(spender: ContractAddress, amount: u256) -> bool { - ERC20::approve(spender, amount) + #[external(v0)] + fn approve(ref self: ContractState, spender: ContractAddress, amount: u256) -> bool { + let mut unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::ERC20Impl::approve(ref unsafe_state, spender, amount) } } diff --git a/src/openzeppelin/tests/token.cairo b/src/openzeppelin/tests/token.cairo index ffee08cb1..fa678fb9c 100644 --- a/src/openzeppelin/tests/token.cairo +++ b/src/openzeppelin/tests/token.cairo @@ -1,5 +1,6 @@ -// mod test_dual20; +mod test_dual20; mod test_dual721; -// mod test_erc20; +mod test_erc20; mod test_erc721; mod test_dual721_receiver; + diff --git a/src/openzeppelin/tests/token/test_dual20.cairo b/src/openzeppelin/tests/token/test_dual20.cairo index 52d0f0906..d8b56c5fa 100644 --- a/src/openzeppelin/tests/token/test_dual20.cairo +++ b/src/openzeppelin/tests/token/test_dual20.cairo @@ -45,7 +45,7 @@ fn OPERATOR() -> ContractAddress { // fn setup_snake() -> (DualERC20, IERC20Dispatcher) { - let mut calldata = ArrayTrait::new(); + let mut calldata = array![]; calldata.append_serde(NAME); calldata.append_serde(SYMBOL); calldata.append_serde(SUPPLY); @@ -55,7 +55,7 @@ fn setup_snake() -> (DualERC20, IERC20Dispatcher) { } fn setup_camel() -> (DualERC20, IERC20CamelDispatcher) { - let mut calldata = ArrayTrait::new(); + let mut calldata = array![]; calldata.append_serde(NAME); calldata.append_serde(SYMBOL); calldata.append_serde(SUPPLY); @@ -65,14 +65,14 @@ fn setup_camel() -> (DualERC20, IERC20CamelDispatcher) { } fn setup_non_erc20() -> DualERC20 { - let calldata = ArrayTrait::new(); + let calldata = array![]; let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); DualERC20 { contract_address: target } } fn setup_erc20_panic() -> (DualERC20, DualERC20) { - let snake_target = utils::deploy(SnakeERC20Panic::TEST_CLASS_HASH, ArrayTrait::new()); - let camel_target = utils::deploy(CamelERC20Panic::TEST_CLASS_HASH, ArrayTrait::new()); + let snake_target = utils::deploy(SnakeERC20Panic::TEST_CLASS_HASH, array![]); + let camel_target = utils::deploy(CamelERC20Panic::TEST_CLASS_HASH, array![]); (DualERC20 { contract_address: snake_target }, DualERC20 { contract_address: camel_target }) } diff --git a/src/openzeppelin/tests/token/test_erc20.cairo b/src/openzeppelin/tests/token/test_erc20.cairo index 775c974fa..55945c0e2 100644 --- a/src/openzeppelin/tests/token/test_erc20.cairo +++ b/src/openzeppelin/tests/token/test_erc20.cairo @@ -1,4 +1,3 @@ -use openzeppelin::token::erc20::ERC20; use integer::BoundedInt; use integer::u256; use integer::u256_from_felt252; @@ -8,6 +7,11 @@ use starknet::testing::set_caller_address; use traits::Into; use zeroable::Zeroable; +use openzeppelin::token::erc20::ERC20; +use openzeppelin::token::erc20::ERC20::ERC20CamelOnlyImpl; +use openzeppelin::token::erc20::ERC20::ERC20Impl; +use openzeppelin::token::erc20::ERC20::InternalImpl; + // // Constants // @@ -18,14 +22,15 @@ const DECIMALS: u8 = 18_u8; const SUPPLY: u256 = 2000; const VALUE: u256 = 300; +fn STATE() -> ERC20::ContractState { + ERC20::contract_state_for_testing() +} fn OWNER() -> ContractAddress { contract_address_const::<1>() } - fn SPENDER() -> ContractAddress { contract_address_const::<2>() } - fn RECIPIENT() -> ContractAddress { contract_address_const::<3>() } @@ -34,8 +39,10 @@ fn RECIPIENT() -> ContractAddress { // Setup // -fn setup() { - ERC20::constructor(NAME, SYMBOL, SUPPLY, OWNER()); +fn setup() -> ERC20::ContractState { + let mut state = STATE(); + ERC20::constructor(ref state, NAME, SYMBOL, SUPPLY, OWNER()); + state } // @@ -45,25 +52,27 @@ fn setup() { #[test] #[available_gas(2000000)] fn test_initializer() { - ERC20::initializer(NAME, SYMBOL); + let mut state = STATE(); + InternalImpl::initializer(ref state, NAME, SYMBOL); - assert(ERC20::name() == NAME, 'Name should be NAME'); - assert(ERC20::symbol() == SYMBOL, 'Symbol should be SYMBOL'); - assert(ERC20::decimals() == DECIMALS, 'Decimals should be 18'); - assert(ERC20::total_supply() == 0, 'Supply should eq 0'); + assert(ERC20Impl::name(@state) == NAME, 'Name should be NAME'); + assert(ERC20Impl::symbol(@state) == SYMBOL, 'Symbol should be SYMBOL'); + assert(ERC20Impl::decimals(@state) == DECIMALS, 'Decimals should be 18'); + assert(ERC20Impl::total_supply(@state) == 0, 'Supply should eq 0'); } #[test] #[available_gas(2000000)] fn test_constructor() { - ERC20::constructor(NAME, SYMBOL, SUPPLY, OWNER()); + let mut state = STATE(); + ERC20::constructor(ref state, NAME, SYMBOL, SUPPLY, OWNER()); - assert(ERC20::balance_of(OWNER()) == SUPPLY, 'Should eq inital_supply'); - assert(ERC20::total_supply() == SUPPLY, 'Should eq inital_supply'); - assert(ERC20::name() == NAME, 'Name should be NAME'); - assert(ERC20::symbol() == SYMBOL, 'Symbol should be SYMBOL'); - assert(ERC20::decimals() == DECIMALS, 'Decimals should be 18'); + assert(ERC20Impl::balance_of(@state, OWNER()) == SUPPLY, 'Should eq inital_supply'); + assert(ERC20Impl::total_supply(@state) == SUPPLY, 'Should eq inital_supply'); + assert(ERC20Impl::name(@state) == NAME, 'Name should be NAME'); + assert(ERC20Impl::symbol(@state) == SYMBOL, 'Symbol should be SYMBOL'); + assert(ERC20Impl::decimals(@state) == DECIMALS, 'Decimals should be 18'); } // @@ -73,39 +82,43 @@ fn test_constructor() { #[test] #[available_gas(2000000)] fn test_total_supply() { - ERC20::_mint(OWNER(), SUPPLY); - assert(ERC20::total_supply() == SUPPLY, 'Should eq SUPPLY'); + let mut state = STATE(); + InternalImpl::_mint(ref state, OWNER(), SUPPLY); + assert(ERC20Impl::total_supply(@state) == SUPPLY, 'Should eq SUPPLY'); } #[test] #[available_gas(2000000)] fn test_totalSupply() { - ERC20::_mint(OWNER(), SUPPLY); - assert(ERC20::totalSupply() == SUPPLY, 'Should eq SUPPLY'); + let mut state = STATE(); + InternalImpl::_mint(ref state, OWNER(), SUPPLY); + assert(ERC20CamelOnlyImpl::totalSupply(@state) == SUPPLY, 'Should eq SUPPLY'); } #[test] #[available_gas(2000000)] fn test_balance_of() { - ERC20::_mint(OWNER(), SUPPLY); - assert(ERC20::balance_of(OWNER()) == SUPPLY, 'Should eq SUPPLY'); + let mut state = STATE(); + InternalImpl::_mint(ref state, OWNER(), SUPPLY); + assert(ERC20Impl::balance_of(@state, OWNER()) == SUPPLY, 'Should eq SUPPLY'); } #[test] #[available_gas(2000000)] fn test_balanceOf() { - ERC20::_mint(OWNER(), SUPPLY); - assert(ERC20::balanceOf(OWNER()) == SUPPLY, 'Should eq SUPPLY'); + let mut state = STATE(); + InternalImpl::_mint(ref state, OWNER(), SUPPLY); + assert(ERC20CamelOnlyImpl::balanceOf(@state, OWNER()) == SUPPLY, 'Should eq SUPPLY'); } #[test] #[available_gas(2000000)] fn test_allowance() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC20::approve(SPENDER(), VALUE); + ERC20Impl::approve(ref state, SPENDER(), VALUE); - assert(ERC20::allowance(OWNER(), SPENDER()) == VALUE, 'Should eq VALUE'); + assert(ERC20Impl::allowance(@state, OWNER(), SPENDER()) == VALUE, 'Should eq VALUE'); } // @@ -115,55 +128,58 @@ fn test_allowance() { #[test] #[available_gas(2000000)] fn test_approve() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - assert(ERC20::approve(SPENDER(), VALUE), 'Should return true'); - - assert(ERC20::allowance(OWNER(), SPENDER()) == VALUE, 'Spender not approved correctly'); + assert(ERC20Impl::approve(ref state, SPENDER(), VALUE), 'Should return true'); + assert( + ERC20Impl::allowance(@state, OWNER(), SPENDER()) == VALUE, 'Spender not approved correctly' + ); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC20: approve from 0', ))] fn test_approve_from_zero() { - setup(); - ERC20::approve(SPENDER(), VALUE); + let mut state = setup(); + ERC20Impl::approve(ref state, SPENDER(), VALUE); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC20: approve to 0', ))] fn test_approve_to_zero() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC20::approve(Zeroable::zero(), VALUE); + ERC20Impl::approve(ref state, Zeroable::zero(), VALUE); } #[test] #[available_gas(2000000)] fn test__approve() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC20::_approve(OWNER(), SPENDER(), VALUE); + InternalImpl::_approve(ref state, OWNER(), SPENDER(), VALUE); - assert(ERC20::allowance(OWNER(), SPENDER()) == VALUE, 'Spender not approved correctly'); + assert( + ERC20Impl::allowance(@state, OWNER(), SPENDER()) == VALUE, 'Spender not approved correctly' + ); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC20: approve from 0', ))] fn test__approve_from_zero() { - setup(); - ERC20::_approve(Zeroable::zero(), SPENDER(), VALUE); + let mut state = setup(); + InternalImpl::_approve(ref state, Zeroable::zero(), SPENDER(), VALUE); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC20: approve to 0', ))] fn test__approve_to_zero() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC20::_approve(OWNER(), Zeroable::zero(), VALUE); + InternalImpl::_approve(ref state, OWNER(), Zeroable::zero(), VALUE); } // @@ -173,51 +189,51 @@ fn test__approve_to_zero() { #[test] #[available_gas(2000000)] fn test_transfer() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - assert(ERC20::transfer(RECIPIENT(), VALUE), 'Should return true'); + assert(ERC20Impl::transfer(ref state, RECIPIENT(), VALUE), 'Should return true'); - assert(ERC20::balance_of(RECIPIENT()) == VALUE, 'Balance should eq VALUE'); - assert(ERC20::balance_of(OWNER()) == SUPPLY - VALUE, 'Should eq supply - VALUE'); - assert(ERC20::total_supply() == SUPPLY, 'Total supply should not change'); + assert(ERC20Impl::balance_of(@state, RECIPIENT()) == VALUE, 'Balance should eq VALUE'); + assert(ERC20Impl::balance_of(@state, OWNER()) == SUPPLY - VALUE, 'Should eq supply - VALUE'); + assert(ERC20Impl::total_supply(@state) == SUPPLY, 'Total supply should not change'); } #[test] #[available_gas(2000000)] fn test__transfer() { - setup(); + let mut state = setup(); - ERC20::_transfer(OWNER(), RECIPIENT(), VALUE); - assert(ERC20::balance_of(RECIPIENT()) == VALUE, 'Balance should eq amount'); - assert(ERC20::balance_of(OWNER()) == SUPPLY - VALUE, 'Should eq supply - amount'); - assert(ERC20::total_supply() == SUPPLY, 'Total supply should not change'); + InternalImpl::_transfer(ref state, OWNER(), RECIPIENT(), VALUE); + assert(ERC20Impl::balance_of(@state, RECIPIENT()) == VALUE, 'Balance should eq amount'); + assert(ERC20Impl::balance_of(@state, OWNER()) == SUPPLY - VALUE, 'Should eq supply - amount'); + assert(ERC20Impl::total_supply(@state) == SUPPLY, 'Total supply should not change'); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('u256_sub Overflow', ))] fn test__transfer_not_enough_balance() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); let balance_plus_one = SUPPLY + 1; - ERC20::_transfer(OWNER(), RECIPIENT(), balance_plus_one); + InternalImpl::_transfer(ref state, OWNER(), RECIPIENT(), balance_plus_one); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC20: transfer from 0', ))] fn test__transfer_from_zero() { - setup(); - ERC20::_transfer(Zeroable::zero(), RECIPIENT(), VALUE); + let mut state = setup(); + InternalImpl::_transfer(ref state, Zeroable::zero(), RECIPIENT(), VALUE); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC20: transfer to 0', ))] fn test__transfer_to_zero() { - setup(); - ERC20::_transfer(OWNER(), Zeroable::zero(), VALUE); + let mut state = setup(); + InternalImpl::_transfer(ref state, OWNER(), Zeroable::zero(), VALUE); } // @@ -227,31 +243,32 @@ fn test__transfer_to_zero() { #[test] #[available_gas(2000000)] fn test_transfer_from() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC20::approve(SPENDER(), VALUE); + ERC20Impl::approve(ref state, SPENDER(), VALUE); set_caller_address(SPENDER()); - assert(ERC20::transfer_from(OWNER(), RECIPIENT(), VALUE), 'Should return true'); + assert(ERC20Impl::transfer_from(ref state, OWNER(), RECIPIENT(), VALUE), 'Should return true'); - assert(ERC20::balance_of(RECIPIENT()) == VALUE, 'Should eq amount'); - assert(ERC20::balance_of(OWNER()) == SUPPLY - VALUE, 'Should eq suppy - amount'); - assert(ERC20::allowance(OWNER(), SPENDER()) == 0, 'Should eq 0'); - assert(ERC20::total_supply() == SUPPLY, 'Total supply should not change'); + assert(ERC20Impl::balance_of(@state, RECIPIENT()) == VALUE, 'Should eq amount'); + assert(ERC20Impl::balance_of(@state, OWNER()) == SUPPLY - VALUE, 'Should eq suppy - amount'); + assert(ERC20Impl::allowance(@state, OWNER(), SPENDER()) == 0, 'Should eq 0'); + assert(ERC20Impl::total_supply(@state) == SUPPLY, 'Total supply should not change'); } #[test] #[available_gas(2000000)] fn test_transfer_from_doesnt_consume_infinite_allowance() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC20::approve(SPENDER(), BoundedInt::max()); + ERC20Impl::approve(ref state, SPENDER(), BoundedInt::max()); set_caller_address(SPENDER()); - ERC20::transfer_from(OWNER(), RECIPIENT(), VALUE); + ERC20Impl::transfer_from(ref state, OWNER(), RECIPIENT(), VALUE); assert( - ERC20::allowance(OWNER(), SPENDER()) == BoundedInt::max(), 'Allowance should not change' + ERC20Impl::allowance(@state, OWNER(), SPENDER()) == BoundedInt::max(), + 'Allowance should not change' ); } @@ -259,63 +276,69 @@ fn test_transfer_from_doesnt_consume_infinite_allowance() { #[available_gas(2000000)] #[should_panic(expected: ('u256_sub Overflow', ))] fn test_transfer_from_greater_than_allowance() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC20::approve(SPENDER(), VALUE); + ERC20Impl::approve(ref state, SPENDER(), VALUE); set_caller_address(SPENDER()); let allowance_plus_one = VALUE + 1; - ERC20::transfer_from(OWNER(), RECIPIENT(), allowance_plus_one); + ERC20Impl::transfer_from(ref state, OWNER(), RECIPIENT(), allowance_plus_one); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC20: transfer to 0', ))] fn test_transfer_from_to_zero_address() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC20::approve(SPENDER(), VALUE); + ERC20Impl::approve(ref state, SPENDER(), VALUE); set_caller_address(SPENDER()); - ERC20::transfer_from(OWNER(), Zeroable::zero(), VALUE); + ERC20Impl::transfer_from(ref state, OWNER(), Zeroable::zero(), VALUE); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('u256_sub Overflow', ))] fn test_transfer_from_from_zero_address() { - setup(); - ERC20::transfer_from(Zeroable::zero(), RECIPIENT(), VALUE); + let mut state = setup(); + ERC20Impl::transfer_from(ref state, Zeroable::zero(), RECIPIENT(), VALUE); } #[test] #[available_gas(2000000)] fn test_transferFrom() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC20::approve(SPENDER(), VALUE); + ERC20Impl::approve(ref state, SPENDER(), VALUE); set_caller_address(SPENDER()); - assert(ERC20::transferFrom(OWNER(), RECIPIENT(), VALUE), 'Should return true'); - assert(ERC20::balanceOf(RECIPIENT()) == VALUE, 'Should eq amount'); - assert(ERC20::balanceOf(OWNER()) == SUPPLY - VALUE, 'Should eq suppy - amount'); - assert(ERC20::allowance(OWNER(), SPENDER()) == 0, 'Should eq 0'); - assert(ERC20::totalSupply() == SUPPLY, 'Total supply should not change'); + assert( + ERC20CamelOnlyImpl::transferFrom(ref state, OWNER(), RECIPIENT(), VALUE), + 'Should return true' + ); + assert(ERC20CamelOnlyImpl::balanceOf(@state, RECIPIENT()) == VALUE, 'Should eq amount'); + assert( + ERC20CamelOnlyImpl::balanceOf(@state, OWNER()) == SUPPLY - VALUE, 'Should eq suppy - amount' + ); + assert(ERC20Impl::allowance(@state, OWNER(), SPENDER()) == 0, 'Should eq 0'); + assert(ERC20CamelOnlyImpl::totalSupply(@state) == SUPPLY, 'Total supply should not change'); } #[test] #[available_gas(2000000)] fn test_transferFrom_doesnt_consume_infinite_allowance() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC20::approve(SPENDER(), BoundedInt::max()); + ERC20Impl::approve(ref state, SPENDER(), BoundedInt::max()); set_caller_address(SPENDER()); - ERC20::transferFrom(OWNER(), RECIPIENT(), VALUE); + ERC20CamelOnlyImpl::transferFrom(ref state, OWNER(), RECIPIENT(), VALUE); assert( - ERC20::allowance(OWNER(), SPENDER()) == BoundedInt::max(), 'Allowance should not change' + ERC20Impl::allowance(@state, OWNER(), SPENDER()) == BoundedInt::max(), + 'Allowance should not change' ); } @@ -323,33 +346,33 @@ fn test_transferFrom_doesnt_consume_infinite_allowance() { #[available_gas(2000000)] #[should_panic(expected: ('u256_sub Overflow', ))] fn test_transferFrom_greater_than_allowance() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC20::approve(SPENDER(), VALUE); + ERC20Impl::approve(ref state, SPENDER(), VALUE); set_caller_address(SPENDER()); let allowance_plus_one = VALUE + 1; - ERC20::transferFrom(OWNER(), RECIPIENT(), allowance_plus_one); + ERC20CamelOnlyImpl::transferFrom(ref state, OWNER(), RECIPIENT(), allowance_plus_one); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC20: transfer to 0', ))] fn test_transferFrom_to_zero_address() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC20::approve(SPENDER(), VALUE); + ERC20Impl::approve(ref state, SPENDER(), VALUE); set_caller_address(SPENDER()); - ERC20::transferFrom(OWNER(), Zeroable::zero(), VALUE); + ERC20CamelOnlyImpl::transferFrom(ref state, OWNER(), Zeroable::zero(), VALUE); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('u256_sub Overflow', ))] fn test_transferFrom_from_zero_address() { - setup(); - ERC20::transferFrom(Zeroable::zero(), RECIPIENT(), VALUE); + let mut state = setup(); + ERC20CamelOnlyImpl::transferFrom(ref state, Zeroable::zero(), RECIPIENT(), VALUE); } // @@ -359,57 +382,57 @@ fn test_transferFrom_from_zero_address() { #[test] #[available_gas(2000000)] fn test_increase_allowance() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC20::approve(SPENDER(), VALUE); + ERC20Impl::approve(ref state, SPENDER(), VALUE); - assert(ERC20::increase_allowance(SPENDER(), VALUE), 'Should return true'); - assert(ERC20::allowance(OWNER(), SPENDER()) == VALUE * 2, 'Should be amount * 2'); + assert(ERC20::increase_allowance(ref state, SPENDER(), VALUE), 'Should return true'); + assert(ERC20Impl::allowance(@state, OWNER(), SPENDER()) == VALUE * 2, 'Should be amount * 2'); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC20: approve to 0', ))] fn test_increase_allowance_to_zero_address() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC20::increase_allowance(Zeroable::zero(), VALUE); + ERC20::increase_allowance(ref state, Zeroable::zero(), VALUE); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC20: approve from 0', ))] fn test_increase_allowance_from_zero_address() { - setup(); - ERC20::increase_allowance(SPENDER(), VALUE); + let mut state = setup(); + ERC20::increase_allowance(ref state, SPENDER(), VALUE); } #[test] #[available_gas(2000000)] fn test_increaseAllowance() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC20::approve(SPENDER(), VALUE); + ERC20Impl::approve(ref state, SPENDER(), VALUE); - assert(ERC20::increaseAllowance(SPENDER(), VALUE), 'Should return true'); - assert(ERC20::allowance(OWNER(), SPENDER()) == VALUE * 2, 'Should be amount * 2'); + assert(ERC20::increaseAllowance(ref state, SPENDER(), VALUE), 'Should return true'); + assert(ERC20Impl::allowance(@state, OWNER(), SPENDER()) == VALUE * 2, 'Should be amount * 2'); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC20: approve to 0', ))] fn test_increaseAllowance_to_zero_address() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC20::increaseAllowance(Zeroable::zero(), VALUE); + ERC20::increaseAllowance(ref state, Zeroable::zero(), VALUE); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC20: approve from 0', ))] fn test_increaseAllowance_from_zero_address() { - setup(); - ERC20::increaseAllowance(SPENDER(), VALUE); + let mut state = setup(); + ERC20::increaseAllowance(ref state, SPENDER(), VALUE); } // @@ -419,57 +442,57 @@ fn test_increaseAllowance_from_zero_address() { #[test] #[available_gas(2000000)] fn test_decrease_allowance() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC20::approve(SPENDER(), VALUE); + ERC20Impl::approve(ref state, SPENDER(), VALUE); - assert(ERC20::decrease_allowance(SPENDER(), VALUE), 'Should return true'); - assert(ERC20::allowance(OWNER(), SPENDER()) == VALUE - VALUE, 'Should be 0'); + assert(ERC20::decrease_allowance(ref state, SPENDER(), VALUE), 'Should return true'); + assert(ERC20Impl::allowance(@state, OWNER(), SPENDER()) == VALUE - VALUE, 'Should be 0'); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('u256_sub Overflow', ))] fn test_decrease_allowance_to_zero_address() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC20::decrease_allowance(Zeroable::zero(), VALUE); + ERC20::decrease_allowance(ref state, Zeroable::zero(), VALUE); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('u256_sub Overflow', ))] fn test_decrease_allowance_from_zero_address() { - setup(); - ERC20::decrease_allowance(SPENDER(), VALUE); + let mut state = setup(); + ERC20::decrease_allowance(ref state, SPENDER(), VALUE); } #[test] #[available_gas(2000000)] fn test_decreaseAllowance() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC20::approve(SPENDER(), VALUE); + ERC20Impl::approve(ref state, SPENDER(), VALUE); - assert(ERC20::decreaseAllowance(SPENDER(), VALUE), 'Should return true'); - assert(ERC20::allowance(OWNER(), SPENDER()) == VALUE - VALUE, 'Should be 0'); + assert(ERC20::decreaseAllowance(ref state, SPENDER(), VALUE), 'Should return true'); + assert(ERC20Impl::allowance(@state, OWNER(), SPENDER()) == VALUE - VALUE, 'Should be 0'); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('u256_sub Overflow', ))] fn test_decreaseAllowance_to_zero_address() { - setup(); + let mut state = setup(); set_caller_address(OWNER()); - ERC20::decreaseAllowance(Zeroable::zero(), VALUE); + ERC20::decreaseAllowance(ref state, Zeroable::zero(), VALUE); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('u256_sub Overflow', ))] fn test_decreaseAllowance_from_zero_address() { - setup(); - ERC20::decreaseAllowance(SPENDER(), VALUE); + let mut state = setup(); + ERC20::decreaseAllowance(ref state, SPENDER(), VALUE); } // @@ -479,24 +502,28 @@ fn test_decreaseAllowance_from_zero_address() { #[test] #[available_gas(2000000)] fn test__spend_allowance_not_unlimited() { - setup(); + let mut state = setup(); - ERC20::_approve(OWNER(), SPENDER(), SUPPLY); - ERC20::_spend_allowance(OWNER(), SPENDER(), VALUE); - assert(ERC20::allowance(OWNER(), SPENDER()) == SUPPLY - VALUE, 'Should eq supply - amount'); + InternalImpl::_approve(ref state, OWNER(), SPENDER(), SUPPLY); + InternalImpl::_spend_allowance(ref state, OWNER(), SPENDER(), VALUE); + assert( + ERC20Impl::allowance(@state, OWNER(), SPENDER()) == SUPPLY - VALUE, + 'Should eq supply - amount' + ); } #[test] #[available_gas(2000000)] fn test__spend_allowance_unlimited() { - setup(); - ERC20::_approve(OWNER(), SPENDER(), BoundedInt::max()); + let mut state = setup(); + InternalImpl::_approve(ref state, OWNER(), SPENDER(), BoundedInt::max()); let max_minus_one: u256 = BoundedInt::max() - 1; - ERC20::_spend_allowance(OWNER(), SPENDER(), max_minus_one); + InternalImpl::_spend_allowance(ref state, OWNER(), SPENDER(), max_minus_one); assert( - ERC20::allowance(OWNER(), SPENDER()) == BoundedInt::max(), 'Allowance should not change' + ERC20Impl::allowance(@state, OWNER(), SPENDER()) == BoundedInt::max(), + 'Allowance should not change' ); } @@ -507,17 +534,19 @@ fn test__spend_allowance_unlimited() { #[test] #[available_gas(2000000)] fn test__mint() { - ERC20::_mint(OWNER(), VALUE); + let mut state = STATE(); + InternalImpl::_mint(ref state, OWNER(), VALUE); - assert(ERC20::balance_of(OWNER()) == VALUE, 'Should eq amount'); - assert(ERC20::total_supply() == VALUE, 'Should eq total supply'); + assert(ERC20Impl::balance_of(@state, OWNER()) == VALUE, 'Should eq amount'); + assert(ERC20Impl::total_supply(@state) == VALUE, 'Should eq total supply'); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC20: mint to 0', ))] fn test__mint_to_zero() { - ERC20::_mint(Zeroable::zero(), VALUE); + let mut state = STATE(); + InternalImpl::_mint(ref state, Zeroable::zero(), VALUE); } // @@ -527,17 +556,17 @@ fn test__mint_to_zero() { #[test] #[available_gas(2000000)] fn test__burn() { - setup(); - ERC20::_burn(OWNER(), VALUE); + let mut state = setup(); + InternalImpl::_burn(ref state, OWNER(), VALUE); - assert(ERC20::total_supply() == SUPPLY - VALUE, 'Should eq supply - amount'); - assert(ERC20::balance_of(OWNER()) == SUPPLY - VALUE, 'Should eq supply - amount'); + assert(ERC20Impl::total_supply(@state) == SUPPLY - VALUE, 'Should eq supply - amount'); + assert(ERC20Impl::balance_of(@state, OWNER()) == SUPPLY - VALUE, 'Should eq supply - amount'); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('ERC20: burn from 0', ))] fn test__burn_from_zero() { - setup(); - ERC20::_burn(Zeroable::zero(), VALUE); + let mut state = setup(); + InternalImpl::_burn(ref state, Zeroable::zero(), VALUE); } diff --git a/src/openzeppelin/token.cairo b/src/openzeppelin/token.cairo index 51f2f6d68..f9a848d01 100644 --- a/src/openzeppelin/token.cairo +++ b/src/openzeppelin/token.cairo @@ -1,2 +1,2 @@ -// mod erc20; +mod erc20; mod erc721; diff --git a/src/openzeppelin/token/erc20.cairo b/src/openzeppelin/token/erc20.cairo index bd33b3953..6784045a0 100644 --- a/src/openzeppelin/token/erc20.cairo +++ b/src/openzeppelin/token/erc20.cairo @@ -1,7 +1,7 @@ mod erc20; -mod interface; mod dual20; +mod interface; use erc20::ERC20; -use erc20::ERC20ABIDispatcher; -use erc20::ERC20ABIDispatcherTrait; +use interface::ERC20ABIDispatcher; +use interface::ERC20ABIDispatcherTrait; diff --git a/src/openzeppelin/token/erc20/dual20.cairo b/src/openzeppelin/token/erc20/dual20.cairo index 99a5045a4..848f53363 100644 --- a/src/openzeppelin/token/erc20/dual20.cairo +++ b/src/openzeppelin/token/erc20/dual20.cairo @@ -29,29 +29,25 @@ trait DualERC20Trait { impl DualERC20Impl of DualERC20Trait { fn name(self: @DualERC20) -> felt252 { - let args = ArrayTrait::new(); - + let args = array![]; call_contract_syscall(*self.contract_address, selectors::name, args.span()) .unwrap_and_cast() } fn symbol(self: @DualERC20) -> felt252 { - let args = ArrayTrait::new(); - + let args = array![]; call_contract_syscall(*self.contract_address, selectors::symbol, args.span()) .unwrap_and_cast() } fn decimals(self: @DualERC20) -> u8 { - let args = ArrayTrait::new(); - + let args = array![]; call_contract_syscall(*self.contract_address, selectors::decimals, args.span()) .unwrap_and_cast() } fn total_supply(self: @DualERC20) -> u256 { - let mut args = ArrayTrait::new(); - + let mut args = array![]; try_selector_with_fallback( *self.contract_address, selectors::total_supply, selectors::totalSupply, args.span() ) @@ -59,7 +55,7 @@ impl DualERC20Impl of DualERC20Trait { } fn balance_of(self: @DualERC20, account: ContractAddress) -> u256 { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(account); try_selector_with_fallback( @@ -69,7 +65,7 @@ impl DualERC20Impl of DualERC20Trait { } fn allowance(self: @DualERC20, owner: ContractAddress, spender: ContractAddress) -> u256 { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(owner); args.append_serde(spender); @@ -78,7 +74,7 @@ impl DualERC20Impl of DualERC20Trait { } fn transfer(self: @DualERC20, recipient: ContractAddress, amount: u256) -> bool { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(recipient); args.append_serde(amount); @@ -89,7 +85,7 @@ impl DualERC20Impl of DualERC20Trait { fn transfer_from( self: @DualERC20, sender: ContractAddress, recipient: ContractAddress, amount: u256 ) -> bool { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(sender); args.append_serde(recipient); args.append_serde(amount); @@ -101,7 +97,7 @@ impl DualERC20Impl of DualERC20Trait { } fn approve(self: @DualERC20, spender: ContractAddress, amount: u256) -> bool { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(spender); args.append_serde(amount); diff --git a/src/openzeppelin/token/erc20/erc20.cairo b/src/openzeppelin/token/erc20/erc20.cairo index f778a0d47..f83a965a8 100644 --- a/src/openzeppelin/token/erc20/erc20.cairo +++ b/src/openzeppelin/token/erc20/erc20.cairo @@ -1,65 +1,14 @@ -use starknet::ContractAddress; - -#[abi] -trait ERC20ABI { - #[view] - fn name() -> felt252; - #[view] - fn symbol() -> felt252; - #[view] - fn decimals() -> u8; - #[view] - fn total_supply() -> u256; - #[view] - fn balance_of(account: ContractAddress) -> u256; - #[view] - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256; - #[external] - fn transfer(recipient: ContractAddress, amount: u256) -> bool; - #[external] - fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool; - #[external] - fn approve(spender: ContractAddress, amount: u256) -> bool; - #[external] - fn increase_allowance(spender: ContractAddress, added_value: u256) -> bool; - #[external] - fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool; -} - -#[abi] -trait ERC20CamelABI { - #[view] - fn name() -> felt252; - #[view] - fn symbol() -> felt252; - #[view] - fn decimals() -> u8; - #[view] - fn totalSupply() -> u256; - #[view] - fn balanceOf(account: ContractAddress) -> u256; - #[view] - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256; - #[external] - fn transfer(recipient: ContractAddress, amount: u256) -> bool; - #[external] - fn transferFrom(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool; - #[external] - fn approve(spender: ContractAddress, amount: u256) -> bool; - #[external] - fn increaseAllowance(spender: ContractAddress, addedValue: u256) -> bool; - #[external] - fn decreaseAllowance(spender: ContractAddress, subtractedValue: u256) -> bool; -} - -#[contract] +#[starknet::contract] mod ERC20 { - use openzeppelin::token::erc20::interface::{IERC20, IERC20Camel}; use integer::BoundedInt; use starknet::ContractAddress; use starknet::get_caller_address; use zeroable::Zeroable; + use openzeppelin::token::erc20::interface::IERC20; + use openzeppelin::token::erc20::interface::IERC20CamelOnly; + + #[storage] struct Storage { _name: felt252, _symbol: felt252, @@ -69,246 +18,216 @@ mod ERC20 { } #[event] - fn Transfer(from: ContractAddress, to: ContractAddress, value: u256) {} + #[derive(Drop, starknet::Event)] + enum Event { + Transfer: Transfer, + Approval: Approval, + } - #[event] - fn Approval(owner: ContractAddress, spender: ContractAddress, value: u256) {} + #[derive(Drop, starknet::Event)] + struct Transfer { + from: ContractAddress, + to: ContractAddress, + value: u256 + } + + #[derive(Drop, starknet::Event)] + struct Approval { + owner: ContractAddress, + spender: ContractAddress, + value: u256 + } - impl ERC20Impl of IERC20 { - fn name() -> felt252 { - _name::read() + #[constructor] + fn constructor( + ref self: ContractState, + name: felt252, + symbol: felt252, + initial_supply: u256, + recipient: ContractAddress + ) { + self.initializer(name, symbol); + self._mint(recipient, initial_supply); + } + + // + // External + // + + #[external(v0)] + impl ERC20Impl of IERC20 { + fn name(self: @ContractState) -> felt252 { + self._name.read() } - fn symbol() -> felt252 { - _symbol::read() + fn symbol(self: @ContractState) -> felt252 { + self._symbol.read() } - fn decimals() -> u8 { - 18_u8 + fn decimals(self: @ContractState) -> u8 { + 18 } - fn total_supply() -> u256 { - _total_supply::read() + fn total_supply(self: @ContractState) -> u256 { + self._total_supply.read() } - fn balance_of(account: ContractAddress) -> u256 { - _balances::read(account) + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { + self._balances.read(account) } - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { - _allowances::read((owner, spender)) + fn allowance( + self: @ContractState, owner: ContractAddress, spender: ContractAddress + ) -> u256 { + self._allowances.read((owner, spender)) } - fn transfer(recipient: ContractAddress, amount: u256) -> bool { + fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) -> bool { let sender = get_caller_address(); - _transfer(sender, recipient, amount); + self._transfer(sender, recipient, amount); true } fn transfer_from( - sender: ContractAddress, recipient: ContractAddress, amount: u256 + ref self: ContractState, + sender: ContractAddress, + recipient: ContractAddress, + amount: u256 ) -> bool { let caller = get_caller_address(); - _spend_allowance(sender, caller, amount); - _transfer(sender, recipient, amount); + self._spend_allowance(sender, caller, amount); + self._transfer(sender, recipient, amount); true } - fn approve(spender: ContractAddress, amount: u256) -> bool { + fn approve(ref self: ContractState, spender: ContractAddress, amount: u256) -> bool { let caller = get_caller_address(); - _approve(caller, spender, amount); + self._approve(caller, spender, amount); true } } - impl ERC20CamelImpl of IERC20Camel { - fn name() -> felt252 { - ERC20Impl::name() - } - - fn symbol() -> felt252 { - ERC20Impl::symbol() + #[external(v0)] + impl ERC20CamelOnlyImpl of IERC20CamelOnly { + fn totalSupply(self: @ContractState) -> u256 { + ERC20Impl::total_supply(self) } - fn decimals() -> u8 { - ERC20Impl::decimals() + fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 { + ERC20Impl::balance_of(self, account) } - fn totalSupply() -> u256 { - ERC20Impl::total_supply() - } - - fn balanceOf(account: ContractAddress) -> u256 { - ERC20Impl::balance_of(account) - } - - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { - ERC20Impl::allowance(owner, spender) - } - - fn transfer(recipient: ContractAddress, amount: u256) -> bool { - ERC20Impl::transfer(recipient, amount) - } - - fn transferFrom(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { - ERC20Impl::transfer_from(sender, recipient, amount) - } - - fn approve(spender: ContractAddress, amount: u256) -> bool { - ERC20Impl::approve(spender, amount) + fn transferFrom( + ref self: ContractState, + sender: ContractAddress, + recipient: ContractAddress, + amount: u256 + ) -> bool { + ERC20Impl::transfer_from(ref self, sender, recipient, amount) } } - #[constructor] - fn constructor( - name: felt252, symbol: felt252, initial_supply: u256, recipient: ContractAddress - ) { - initializer(name, symbol); - _mint(recipient, initial_supply); - } - - #[view] - fn name() -> felt252 { - ERC20Impl::name() - } - - #[view] - fn symbol() -> felt252 { - ERC20Impl::symbol() - } - - #[view] - fn decimals() -> u8 { - ERC20Impl::decimals() + #[external(v0)] + fn increase_allowance( + ref self: ContractState, spender: ContractAddress, added_value: u256 + ) -> bool { + self._increase_allowance(spender, added_value) } - #[view] - fn total_supply() -> u256 { - ERC20Impl::total_supply() + #[external(v0)] + fn increaseAllowance( + ref self: ContractState, spender: ContractAddress, addedValue: u256 + ) -> bool { + increase_allowance(ref self, spender, addedValue) } - #[view] - fn totalSupply() -> u256 { - ERC20CamelImpl::totalSupply() + #[external(v0)] + fn decrease_allowance( + ref self: ContractState, spender: ContractAddress, subtracted_value: u256 + ) -> bool { + self._decrease_allowance(spender, subtracted_value) } - #[view] - fn balance_of(account: ContractAddress) -> u256 { - ERC20Impl::balance_of(account) - } - - #[view] - fn balanceOf(account: ContractAddress) -> u256 { - ERC20CamelImpl::balanceOf(account) - } - - #[view] - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256 { - ERC20Impl::allowance(owner, spender) - } - - #[external] - fn transfer(recipient: ContractAddress, amount: u256) -> bool { - ERC20Impl::transfer(recipient, amount) - } - - #[external] - fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { - ERC20Impl::transfer_from(sender, recipient, amount) - } - - #[external] - fn transferFrom(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool { - ERC20CamelImpl::transferFrom(sender, recipient, amount) - } - - #[external] - fn approve(spender: ContractAddress, amount: u256) -> bool { - ERC20Impl::approve(spender, amount) - } - - #[external] - fn increase_allowance(spender: ContractAddress, added_value: u256) -> bool { - _increase_allowance(spender, added_value) - } - - #[external] - fn increaseAllowance(spender: ContractAddress, addedValue: u256) -> bool { - increase_allowance(spender, addedValue) - } - - #[external] - fn decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { - _decrease_allowance(spender, subtracted_value) - } - - #[external] - fn decreaseAllowance(spender: ContractAddress, subtractedValue: u256) -> bool { - decrease_allowance(spender, subtractedValue) + #[external(v0)] + fn decreaseAllowance( + ref self: ContractState, spender: ContractAddress, subtractedValue: u256 + ) -> bool { + decrease_allowance(ref self, spender, subtractedValue) } // - // Internals + // Internal // - #[internal] - fn initializer(name_: felt252, symbol_: felt252) { - _name::write(name_); - _symbol::write(symbol_); - } - - #[internal] - fn _increase_allowance(spender: ContractAddress, added_value: u256) -> bool { - let caller = get_caller_address(); - _approve(caller, spender, _allowances::read((caller, spender)) + added_value); - true - } - - #[internal] - fn _decrease_allowance(spender: ContractAddress, subtracted_value: u256) -> bool { - let caller = get_caller_address(); - _approve(caller, spender, _allowances::read((caller, spender)) - subtracted_value); - true - } - - #[internal] - fn _mint(recipient: ContractAddress, amount: u256) { - assert(!recipient.is_zero(), 'ERC20: mint to 0'); - _total_supply::write(_total_supply::read() + amount); - _balances::write(recipient, _balances::read(recipient) + amount); - Transfer(Zeroable::zero(), recipient, amount); - } - - #[internal] - fn _burn(account: ContractAddress, amount: u256) { - assert(!account.is_zero(), 'ERC20: burn from 0'); - _total_supply::write(_total_supply::read() - amount); - _balances::write(account, _balances::read(account) - amount); - Transfer(account, Zeroable::zero(), amount); - } + #[generate_trait] + impl InternalImpl of InternalTrait { + fn initializer(ref self: ContractState, name_: felt252, symbol_: felt252) { + self._name.write(name_); + self._symbol.write(symbol_); + } - #[internal] - fn _approve(owner: ContractAddress, spender: ContractAddress, amount: u256) { - assert(!owner.is_zero(), 'ERC20: approve from 0'); - assert(!spender.is_zero(), 'ERC20: approve to 0'); - _allowances::write((owner, spender), amount); - Approval(owner, spender, amount); - } + fn _increase_allowance( + ref self: ContractState, spender: ContractAddress, added_value: u256 + ) -> bool { + let caller = get_caller_address(); + self._approve(caller, spender, self._allowances.read((caller, spender)) + added_value); + true + } - #[internal] - fn _transfer(sender: ContractAddress, recipient: ContractAddress, amount: u256) { - assert(!sender.is_zero(), 'ERC20: transfer from 0'); - assert(!recipient.is_zero(), 'ERC20: transfer to 0'); - _balances::write(sender, _balances::read(sender) - amount); - _balances::write(recipient, _balances::read(recipient) + amount); - Transfer(sender, recipient, amount); - } + fn _decrease_allowance( + ref self: ContractState, spender: ContractAddress, subtracted_value: u256 + ) -> bool { + let caller = get_caller_address(); + self + ._approve( + caller, spender, self._allowances.read((caller, spender)) - subtracted_value + ); + true + } - #[internal] - fn _spend_allowance(owner: ContractAddress, spender: ContractAddress, amount: u256) { - let current_allowance = _allowances::read((owner, spender)); - if current_allowance != BoundedInt::max() { - _approve(owner, spender, current_allowance - amount); + fn _mint(ref self: ContractState, recipient: ContractAddress, amount: u256) { + assert(!recipient.is_zero(), 'ERC20: mint to 0'); + self._total_supply.write(self._total_supply.read() + amount); + self._balances.write(recipient, self._balances.read(recipient) + amount); + self.emit(Transfer { from: Zeroable::zero(), to: recipient, value: amount }); + } + + fn _burn(ref self: ContractState, account: ContractAddress, amount: u256) { + assert(!account.is_zero(), 'ERC20: burn from 0'); + self._total_supply.write(self._total_supply.read() - amount); + self._balances.write(account, self._balances.read(account) - amount); + self.emit(Transfer { from: account, to: Zeroable::zero(), value: amount }); + } + + fn _approve( + ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256 + ) { + assert(!owner.is_zero(), 'ERC20: approve from 0'); + assert(!spender.is_zero(), 'ERC20: approve to 0'); + self._allowances.write((owner, spender), amount); + self.emit(Approval { owner, spender, value: amount }); + } + + fn _transfer( + ref self: ContractState, + sender: ContractAddress, + recipient: ContractAddress, + amount: u256 + ) { + assert(!sender.is_zero(), 'ERC20: transfer from 0'); + assert(!recipient.is_zero(), 'ERC20: transfer to 0'); + self._balances.write(sender, self._balances.read(sender) - amount); + self._balances.write(recipient, self._balances.read(recipient) + amount); + self.emit(Transfer { from: sender, to: recipient, value: amount }); + } + + fn _spend_allowance( + ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256 + ) { + let current_allowance = self._allowances.read((owner, spender)); + if current_allowance != BoundedInt::max() { + self._approve(owner, spender, current_allowance - amount); + } } } } diff --git a/src/openzeppelin/token/erc20/interface.cairo b/src/openzeppelin/token/erc20/interface.cairo index 26b00e63b..8f86d6d75 100644 --- a/src/openzeppelin/token/erc20/interface.cairo +++ b/src/openzeppelin/token/erc20/interface.cairo @@ -1,45 +1,75 @@ use starknet::ContractAddress; -#[abi] -trait IERC20 { - #[view] - fn name() -> felt252; - #[view] - fn symbol() -> felt252; - #[view] - fn decimals() -> u8; - #[view] - fn total_supply() -> u256; - #[view] - fn balance_of(account: ContractAddress) -> u256; - #[view] - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256; - #[external] - fn transfer(recipient: ContractAddress, amount: u256) -> bool; - #[external] - fn transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool; - #[external] - fn approve(spender: ContractAddress, amount: u256) -> bool; +#[starknet::interface] +trait IERC20 { + fn name(self: @TState) -> felt252; + fn symbol(self: @TState) -> felt252; + fn decimals(self: @TState) -> u8; + fn total_supply(self: @TState) -> u256; + fn balance_of(self: @TState, account: ContractAddress) -> u256; + fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256; + fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool; + fn transfer_from( + ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool; + fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool; } -#[abi] -trait IERC20Camel { - #[view] - fn name() -> felt252; - #[view] - fn symbol() -> felt252; - #[view] - fn decimals() -> u8; - #[view] - fn totalSupply() -> u256; - #[view] - fn balanceOf(account: ContractAddress) -> u256; - #[view] - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256; - #[external] - fn transfer(recipient: ContractAddress, amount: u256) -> bool; - #[external] - fn transferFrom(sender: ContractAddress, recipient: ContractAddress, amount: u256) -> bool; - #[external] - fn approve(spender: ContractAddress, amount: u256) -> bool; +#[starknet::interface] +trait IERC20Camel { + fn name(self: @TState) -> felt252; + fn symbol(self: @TState) -> felt252; + fn decimals(self: @TState) -> u8; + fn totalSupply(self: @TState) -> u256; + fn balanceOf(self: @TState, account: ContractAddress) -> u256; + fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256; + fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool; + fn transferFrom( + ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool; + fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool; +} + +trait IERC20CamelOnly { + fn totalSupply(self: @TState) -> u256; + fn balanceOf(self: @TState, account: ContractAddress) -> u256; + fn transferFrom( + ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool; +} + +#[starknet::interface] +trait ERC20ABI { + fn name(self: @TState) -> felt252; + fn symbol(self: @TState) -> felt252; + fn decimals(self: @TState) -> u8; + fn total_supply(self: @TState) -> u256; + fn balance_of(self: @TState, account: ContractAddress) -> u256; + fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256; + fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool; + fn transfer_from( + ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool; + fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool; + fn increase_allowance(ref self: TState, spender: ContractAddress, added_value: u256) -> bool; + fn decrease_allowance( + ref self: TState, spender: ContractAddress, subtracted_value: u256 + ) -> bool; +} + +#[starknet::interface] +trait ERC20CamelABI { + fn name(self: @TState) -> felt252; + fn symbol(self: @TState) -> felt252; + fn decimals(self: @TState) -> u8; + fn totalSupply(self: @TState) -> u256; + fn balanceOf(self: @TState, account: ContractAddress) -> u256; + fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256; + fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool; + fn transferFrom( + ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool; + fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool; + fn increaseAllowance(ref self: TState, spender: ContractAddress, addedValue: u256) -> bool; + fn decreaseAllowance(ref self: TState, spender: ContractAddress, subtractedValue: u256) -> bool; } From 4e64434d5595050547327a6125f129df5a77cc52 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Sat, 22 Jul 2023 15:38:41 -0400 Subject: [PATCH 073/246] Migrate access control to cairo2 (#668) * bump to cairo v2.0.2 * update Cargo * comment out mods * update initializable syntax * move security tests * update initializable tests * fix formatting * remove SpanSerde * update ownable syntax * add ownable_camel impl * update syntax in ownable mocks * update ownable tests * fix formatting * update cairo * update Cargo * simplify and clarify tests * fix formatting * simplify internal fns * add initializer * fix constructor, tidy up code * fix tests * Apply suggestions from code review Co-authored-by: Eric Nordelo * fix tests * change StorageTrait to InternalTrait * update syntax * update mocks * start updating tests * start refactor * change impls to external fns * use IOwnableCamelOnly * fix tests * remove unneeded mut * remove unneeded mut * fix mocks * simplify events * change array syntax * update syntax in tests * fix formatting * Apply suggestions from code review Co-authored-by: Eric Nordelo * add space between events * fix generic state * add space between events * fix formatting --------- Co-authored-by: Eric Nordelo --- src/openzeppelin/access.cairo | 2 +- .../access/accesscontrol/accesscontrol.cairo | 246 +++++------ .../accesscontrol/dual_accesscontrol.cairo | 12 +- .../access/accesscontrol/interface.cairo | 38 +- src/openzeppelin/tests/access.cairo | 4 +- .../tests/access/test_accesscontrol.cairo | 387 ++++++++++-------- .../access/test_dual_accesscontrol.cairo | 15 +- src/openzeppelin/tests/mocks.cairo | 6 +- .../mocks/accesscontrol_panic_mock.cairo | 66 +-- .../mocks/camel_accesscontrol_mock.cairo | 55 ++- .../mocks/snake_accesscontrol_mock.cairo | 56 +-- 11 files changed, 460 insertions(+), 427 deletions(-) diff --git a/src/openzeppelin/access.cairo b/src/openzeppelin/access.cairo index 55beac34c..8f59b4ad1 100644 --- a/src/openzeppelin/access.cairo +++ b/src/openzeppelin/access.cairo @@ -1,2 +1,2 @@ -//mod accesscontrol; +mod accesscontrol; mod ownable; diff --git a/src/openzeppelin/access/accesscontrol/accesscontrol.cairo b/src/openzeppelin/access/accesscontrol/accesscontrol.cairo index fd7dbf36c..78d245945 100644 --- a/src/openzeppelin/access/accesscontrol/accesscontrol.cairo +++ b/src/openzeppelin/access/accesscontrol/accesscontrol.cairo @@ -1,211 +1,165 @@ -#[contract] +#[starknet::contract] mod AccessControl { - use openzeppelin::access::accesscontrol::interface; - use openzeppelin::introspection::src5; use starknet::ContractAddress; use starknet::get_caller_address; + use openzeppelin::access::accesscontrol::interface; + use openzeppelin::introspection::interface::ISRC5; + use openzeppelin::introspection::interface::ISRC5Camel; + use openzeppelin::introspection::src5::SRC5; + #[storage] struct Storage { role_admin: LegacyMap, role_members: LegacyMap<(felt252, ContractAddress), bool>, } + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + RoleGranted: RoleGranted, + RoleRevoked: RoleRevoked, + RoleAdminChanged: RoleAdminChanged, + } + /// Emitted when `account` is granted `role`. /// /// `sender` is the account that originated the contract call, an admin role /// bearer (except if `_grant_role` is called during initialization from the constructor). - #[event] - fn RoleGranted(role: felt252, account: ContractAddress, sender: ContractAddress) {} + #[derive(Drop, starknet::Event)] + struct RoleGranted { + role: felt252, + account: ContractAddress, + sender: ContractAddress + } /// Emitted when `account` is revoked `role`. /// /// `sender` is the account that originated the contract call: /// - If using `revoke_role`, it is the admin role bearer. /// - If using `renounce_role`, it is the role bearer (i.e. `account`). - #[event] - fn RoleRevoked(role: felt252, account: ContractAddress, sender: ContractAddress) {} + #[derive(Drop, starknet::Event)] + struct RoleRevoked { + role: felt252, + account: ContractAddress, + sender: ContractAddress + } /// Emitted when `new_admin_role` is set as `role`'s admin role, replacing `previous_admin_role` /// /// `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite /// {RoleAdminChanged} not being emitted signaling this. - #[event] - fn RoleAdminChanged(role: felt252, previous_admin_role: felt252, new_admin_role: felt252) {} - - impl ISRC5Impl of src5::ISRC5 { - fn supports_interface(interface_id: felt252) -> bool { - src5::SRC5::supports_interface(interface_id) + #[derive(Drop, starknet::Event)] + struct RoleAdminChanged { + role: felt252, + previous_admin_role: felt252, + new_admin_role: felt252 + } + + #[external(v0)] + impl SRC5Impl of ISRC5 { + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { + let unsafe_state = SRC5::unsafe_new_contract_state(); + SRC5::SRC5Impl::supports_interface(@unsafe_state, interface_id) } } - impl ISRC5CamelImpl of src5::ISRC5Camel { - fn supportsInterface(interfaceId: felt252) -> bool { - src5::SRC5::supportsInterface(interfaceId) + #[external(v0)] + impl SRC5CamelImpl of ISRC5Camel { + fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { + let unsafe_state = SRC5::unsafe_new_contract_state(); + SRC5::SRC5CamelImpl::supportsInterface(@unsafe_state, interfaceId) } } - impl AccessControlImpl of interface::IAccessControl { - fn has_role(role: felt252, account: ContractAddress) -> bool { - role_members::read((role, account)) + #[external(v0)] + impl AccessControlImpl of interface::IAccessControl { + fn has_role(self: @ContractState, role: felt252, account: ContractAddress) -> bool { + self.role_members.read((role, account)) } - fn get_role_admin(role: felt252) -> felt252 { - role_admin::read(role) + fn get_role_admin(self: @ContractState, role: felt252) -> felt252 { + self.role_admin.read(role) } - fn grant_role(role: felt252, account: ContractAddress) { - let admin = get_role_admin(role); - assert_only_role(admin); - _grant_role(role, account); + fn grant_role(ref self: ContractState, role: felt252, account: ContractAddress) { + let admin = AccessControlImpl::get_role_admin(@self, role); + self.assert_only_role(admin); + self._grant_role(role, account); } - fn revoke_role(role: felt252, account: ContractAddress) { - let admin: felt252 = get_role_admin(role); - assert_only_role(admin); - _revoke_role(role, account); + fn revoke_role(ref self: ContractState, role: felt252, account: ContractAddress) { + let admin = AccessControlImpl::get_role_admin(@self, role); + self.assert_only_role(admin); + self._revoke_role(role, account); } - fn renounce_role(role: felt252, account: ContractAddress) { + fn renounce_role(ref self: ContractState, role: felt252, account: ContractAddress) { let caller: ContractAddress = get_caller_address(); assert(caller == account, 'Can only renounce role for self'); - _revoke_role(role, account); + self._revoke_role(role, account); } } - impl AccessControlCamelImpl of interface::IAccessControlCamel { - fn hasRole(role: felt252, account: ContractAddress) -> bool { - AccessControlImpl::has_role(role, account) + #[external(v0)] + impl AccessControlCamelImpl of interface::IAccessControlCamel { + fn hasRole(self: @ContractState, role: felt252, account: ContractAddress) -> bool { + AccessControlImpl::has_role(self, role, account) } - fn getRoleAdmin(role: felt252) -> felt252 { - AccessControlImpl::get_role_admin(role) + fn getRoleAdmin(self: @ContractState, role: felt252) -> felt252 { + AccessControlImpl::get_role_admin(self, role) } - fn grantRole(role: felt252, account: ContractAddress) { - AccessControlImpl::grant_role(role, account); + fn grantRole(ref self: ContractState, role: felt252, account: ContractAddress) { + AccessControlImpl::grant_role(ref self, role, account); } - fn revokeRole(role: felt252, account: ContractAddress) { - AccessControlImpl::revoke_role(role, account); + fn revokeRole(ref self: ContractState, role: felt252, account: ContractAddress) { + AccessControlImpl::revoke_role(ref self, role, account); } - fn renounceRole(role: felt252, account: ContractAddress) { - AccessControlImpl::renounce_role(role, account); + fn renounceRole(ref self: ContractState, role: felt252, account: ContractAddress) { + AccessControlImpl::renounce_role(ref self, role, account); } } // - // View - // - - #[view] - fn supports_interface(interface_id: felt252) -> bool { - ISRC5Impl::supports_interface(interface_id) - } - - #[view] - fn supportsInterface(interfaceId: felt252) -> bool { - ISRC5CamelImpl::supportsInterface(interfaceId) - } - - #[view] - fn has_role(role: felt252, account: ContractAddress) -> bool { - AccessControlImpl::has_role(role, account) - } - - #[view] - fn hasRole(role: felt252, account: ContractAddress) -> bool { - AccessControlCamelImpl::hasRole(role, account) - } - - #[view] - fn get_role_admin(role: felt252) -> felt252 { - AccessControlImpl::get_role_admin(role) - } - - #[view] - fn getRoleAdmin(role: felt252) -> felt252 { - AccessControlCamelImpl::getRoleAdmin(role) - } - - // - // External - // - - #[external] - fn grant_role(role: felt252, account: ContractAddress) { - AccessControlImpl::grant_role(role, account); - } - - #[external] - fn grantRole(role: felt252, account: ContractAddress) { - AccessControlCamelImpl::grantRole(role, account); - } - - #[external] - fn revoke_role(role: felt252, account: ContractAddress) { - AccessControlImpl::revoke_role(role, account); - } - - #[external] - fn revokeRole(role: felt252, account: ContractAddress) { - AccessControlCamelImpl::revokeRole(role, account); - } - - #[external] - fn renounce_role(role: felt252, account: ContractAddress) { - AccessControlImpl::renounce_role(role, account); - } - - #[external] - fn renounceRole(role: felt252, account: ContractAddress) { - AccessControlCamelImpl::renounceRole(role, account); - } - - // - // Internals + // Internal // - #[internal] - fn initializer() { - src5::SRC5::register_interface(interface::IACCESSCONTROL_ID); - } - - #[internal] - fn assert_only_role(role: felt252) { - let caller: ContractAddress = get_caller_address(); - let authorized: bool = has_role(role, caller); - assert(authorized, 'Caller is missing role'); - } - - // - // WARNING - // The following internal methods are unprotected and should not be used - // outside of a contract's constructor. - // + #[generate_trait] + impl InternalImpl of InternalTrait { + fn initializer(ref self: ContractState) { + let mut unsafe_state = SRC5::unsafe_new_contract_state(); + SRC5::InternalImpl::register_interface(ref unsafe_state, interface::IACCESSCONTROL_ID); + } - #[internal] - fn _grant_role(role: felt252, account: ContractAddress) { - if !has_role(role, account) { + fn assert_only_role(self: @ContractState, role: felt252) { let caller: ContractAddress = get_caller_address(); - role_members::write((role, account), true); - RoleGranted(role, account, caller); + let authorized: bool = AccessControlImpl::has_role(self, role, caller); + assert(authorized, 'Caller is missing role'); } - } - #[internal] - fn _revoke_role(role: felt252, account: ContractAddress) { - if has_role(role, account) { - let caller: ContractAddress = get_caller_address(); - role_members::write((role, account), false); - RoleRevoked(role, account, caller); + fn _grant_role(ref self: ContractState, role: felt252, account: ContractAddress) { + if !AccessControlImpl::has_role(@self, role, account) { + let caller: ContractAddress = get_caller_address(); + self.role_members.write((role, account), true); + self.emit(RoleGranted { role, account, sender: caller }); + } } - } - #[internal] - fn _set_role_admin(role: felt252, admin_role: felt252) { - let previous_admin_role: felt252 = get_role_admin(role); - role_admin::write(role, admin_role); - RoleAdminChanged(role, previous_admin_role, admin_role); + fn _revoke_role(ref self: ContractState, role: felt252, account: ContractAddress) { + if AccessControlImpl::has_role(@self, role, account) { + let caller: ContractAddress = get_caller_address(); + self.role_members.write((role, account), false); + self.emit(RoleRevoked { role, account, sender: caller }); + } + } + + fn _set_role_admin(ref self: ContractState, role: felt252, admin_role: felt252) { + let previous_admin_role: felt252 = AccessControlImpl::get_role_admin(@self, role); + self.role_admin.write(role, admin_role); + self.emit(RoleAdminChanged { role, previous_admin_role, new_admin_role: admin_role }); + } } } diff --git a/src/openzeppelin/access/accesscontrol/dual_accesscontrol.cairo b/src/openzeppelin/access/accesscontrol/dual_accesscontrol.cairo index e4955afd3..49367b2e5 100644 --- a/src/openzeppelin/access/accesscontrol/dual_accesscontrol.cairo +++ b/src/openzeppelin/access/accesscontrol/dual_accesscontrol.cairo @@ -24,7 +24,7 @@ trait DualCaseAccessControlTrait { impl DualCaseAccessControlImpl of DualCaseAccessControlTrait { fn has_role(self: @DualCaseAccessControl, role: felt252, account: ContractAddress) -> bool { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(role); args.append_serde(account); @@ -35,7 +35,7 @@ impl DualCaseAccessControlImpl of DualCaseAccessControlTrait { } fn get_role_admin(self: @DualCaseAccessControl, role: felt252) -> felt252 { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(role); try_selector_with_fallback( @@ -45,7 +45,7 @@ impl DualCaseAccessControlImpl of DualCaseAccessControlTrait { } fn grant_role(self: @DualCaseAccessControl, role: felt252, account: ContractAddress) { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(role); args.append_serde(account); @@ -56,7 +56,7 @@ impl DualCaseAccessControlImpl of DualCaseAccessControlTrait { } fn revoke_role(self: @DualCaseAccessControl, role: felt252, account: ContractAddress) { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(role); args.append_serde(account); @@ -67,7 +67,7 @@ impl DualCaseAccessControlImpl of DualCaseAccessControlTrait { } fn renounce_role(self: @DualCaseAccessControl, role: felt252, account: ContractAddress) { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(role); args.append_serde(account); @@ -78,7 +78,7 @@ impl DualCaseAccessControlImpl of DualCaseAccessControlTrait { } fn supports_interface(self: @DualCaseAccessControl, interface_id: felt252) -> bool { - let mut args = ArrayTrait::new(); + let mut args = array![]; args.append_serde(interface_id); try_selector_with_fallback( diff --git a/src/openzeppelin/access/accesscontrol/interface.cairo b/src/openzeppelin/access/accesscontrol/interface.cairo index 9f333dc0f..50adbddf7 100644 --- a/src/openzeppelin/access/accesscontrol/interface.cairo +++ b/src/openzeppelin/access/accesscontrol/interface.cairo @@ -3,30 +3,20 @@ use starknet::ContractAddress; const IACCESSCONTROL_ID: felt252 = 0x23700be02858dbe2ac4dc9c9f66d0b6b0ed81ec7f970ca6844500a56ff61751; -#[abi] -trait IAccessControl { - #[view] - fn has_role(role: felt252, account: ContractAddress) -> bool; - #[view] - fn get_role_admin(role: felt252) -> felt252; - #[external] - fn grant_role(role: felt252, account: ContractAddress); - #[external] - fn revoke_role(role: felt252, account: ContractAddress); - #[external] - fn renounce_role(role: felt252, account: ContractAddress); +#[starknet::interface] +trait IAccessControl { + fn has_role(self: @TState, role: felt252, account: ContractAddress) -> bool; + fn get_role_admin(self: @TState, role: felt252) -> felt252; + fn grant_role(ref self: TState, role: felt252, account: ContractAddress); + fn revoke_role(ref self: TState, role: felt252, account: ContractAddress); + fn renounce_role(ref self: TState, role: felt252, account: ContractAddress); } -#[abi] -trait IAccessControlCamel { - #[view] - fn hasRole(role: felt252, account: ContractAddress) -> bool; - #[view] - fn getRoleAdmin(role: felt252) -> felt252; - #[external] - fn grantRole(role: felt252, account: ContractAddress); - #[external] - fn revokeRole(role: felt252, account: ContractAddress); - #[external] - fn renounceRole(role: felt252, account: ContractAddress); +#[starknet::interface] +trait IAccessControlCamel { + fn hasRole(self: @TState, role: felt252, account: ContractAddress) -> bool; + fn getRoleAdmin(self: @TState, role: felt252) -> felt252; + fn grantRole(ref self: TState, role: felt252, account: ContractAddress); + fn revokeRole(ref self: TState, role: felt252, account: ContractAddress); + fn renounceRole(ref self: TState, role: felt252, account: ContractAddress); } diff --git a/src/openzeppelin/tests/access.cairo b/src/openzeppelin/tests/access.cairo index b40958d1d..149c62b59 100644 --- a/src/openzeppelin/tests/access.cairo +++ b/src/openzeppelin/tests/access.cairo @@ -1,4 +1,4 @@ -//mod test_accesscontrol; -//mod test_dual_accesscontrol; +mod test_accesscontrol; +mod test_dual_accesscontrol; mod test_ownable; mod test_dual_ownable; diff --git a/src/openzeppelin/tests/access/test_accesscontrol.cairo b/src/openzeppelin/tests/access/test_accesscontrol.cairo index 5e6c71489..704242309 100644 --- a/src/openzeppelin/tests/access/test_accesscontrol.cairo +++ b/src/openzeppelin/tests/access/test_accesscontrol.cairo @@ -1,13 +1,20 @@ -use openzeppelin::access::accesscontrol::AccessControl; -use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; -use openzeppelin::access::accesscontrol::interface::IACCESSCONTROL_ID; use starknet::contract_address_const; use starknet::ContractAddress; use starknet::testing; +use openzeppelin::access::accesscontrol::AccessControl; +use openzeppelin::access::accesscontrol::AccessControl::InternalImpl; +use openzeppelin::access::accesscontrol::AccessControl::AccessControlImpl; +use openzeppelin::access::accesscontrol::AccessControl::AccessControlCamelImpl; +use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; +use openzeppelin::access::accesscontrol::interface::IACCESSCONTROL_ID; const ROLE: felt252 = 41; const OTHER_ROLE: felt252 = 42; +fn ZERO() -> ContractAddress { + contract_address_const::<0>() +} + fn ADMIN() -> ContractAddress { contract_address_const::<1>() } @@ -24,9 +31,18 @@ fn OTHER_ADMIN() -> ContractAddress { contract_address_const::<4>() } -fn setup() { - AccessControl::_grant_role(DEFAULT_ADMIN_ROLE, ADMIN()); - testing::set_caller_address(ADMIN()); +// +// Setup +// + +fn STATE() -> AccessControl::ContractState { + AccessControl::contract_state_for_testing() +} + +fn setup() -> AccessControl::ContractState { + let mut state = STATE(); + InternalImpl::_grant_role(ref state, DEFAULT_ADMIN_ROLE, ADMIN()); + state } // @@ -36,10 +52,13 @@ fn setup() { #[test] #[available_gas(2000000)] fn test_initializer() { - AccessControl::initializer(); - assert(AccessControl::supports_interface(IACCESSCONTROL_ID), 'Should support own interface'); + let mut state = STATE(); + InternalImpl::initializer(ref state); + assert( + AccessControl::SRC5Impl::supports_interface(@state, IACCESSCONTROL_ID), + 'Should support own interface' + ); } - // // supports_interface & supportsInterface // @@ -47,15 +66,23 @@ fn test_initializer() { #[test] #[available_gas(2000000)] fn test_supports_interface() { - AccessControl::initializer(); - assert(AccessControl::supports_interface(IACCESSCONTROL_ID), 'Should support own interface'); + let mut state = STATE(); + InternalImpl::initializer(ref state); + assert( + AccessControl::SRC5Impl::supports_interface(@state, IACCESSCONTROL_ID), + 'Should support own interface' + ); } #[test] #[available_gas(2000000)] fn test_supportsInterface() { - AccessControl::initializer(); - assert(AccessControl::supportsInterface(IACCESSCONTROL_ID), 'Should support own interface'); + let mut state = STATE(); + InternalImpl::initializer(ref state); + assert( + AccessControl::SRC5CamelImpl::supportsInterface(@state, IACCESSCONTROL_ID), + 'Should support own interface' + ); } // @@ -65,19 +92,19 @@ fn test_supportsInterface() { #[test] #[available_gas(2000000)] fn test_has_role() { - setup(); - assert(!AccessControl::has_role(ROLE, AUTHORIZED()), 'should not have role'); - AccessControl::_grant_role(ROLE, AUTHORIZED()); - assert(AccessControl::has_role(ROLE, AUTHORIZED()), 'should have role'); + let mut state = setup(); + assert(!AccessControlImpl::has_role(@state, ROLE, AUTHORIZED()), 'should not have role'); + InternalImpl::_grant_role(ref state, ROLE, AUTHORIZED()); + assert(AccessControlImpl::has_role(@state, ROLE, AUTHORIZED()), 'should have role'); } #[test] #[available_gas(2000000)] fn test_hasRole() { - setup(); - assert(!AccessControl::hasRole(ROLE, AUTHORIZED()), 'should not have role'); - AccessControl::_grant_role(ROLE, AUTHORIZED()); - assert(AccessControl::hasRole(ROLE, AUTHORIZED()), 'should have role'); + let mut state = setup(); + assert(!AccessControlCamelImpl::hasRole(@state, ROLE, AUTHORIZED()), 'should not have role'); + InternalImpl::_grant_role(ref state, ROLE, AUTHORIZED()); + assert(AccessControlCamelImpl::hasRole(@state, ROLE, AUTHORIZED()), 'should have role'); } @@ -88,29 +115,32 @@ fn test_hasRole() { #[test] #[available_gas(2000000)] fn test_assert_only_role() { - setup(); - AccessControl::grant_role(ROLE, AUTHORIZED()); + let mut state = setup(); + testing::set_caller_address(ADMIN()); + AccessControlImpl::grant_role(ref state, ROLE, AUTHORIZED()); + testing::set_caller_address(AUTHORIZED()); - AccessControl::assert_only_role(ROLE); + InternalImpl::assert_only_role(@state, ROLE); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('Caller is missing role', ))] fn test_assert_only_role_unauthorized() { - setup(); + let state = setup(); testing::set_caller_address(OTHER()); - AccessControl::assert_only_role(ROLE); + InternalImpl::assert_only_role(@state, ROLE); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('Caller is missing role', ))] fn test_assert_only_role_unauthorized_when_authorized_for_another_role() { - setup(); - AccessControl::grant_role(ROLE, AUTHORIZED()); + let mut state = setup(); + AccessControlImpl::grant_role(ref state, ROLE, AUTHORIZED()); + testing::set_caller_address(AUTHORIZED()); - AccessControl::assert_only_role(OTHER_ROLE); + InternalImpl::assert_only_role(@state, OTHER_ROLE); } // @@ -120,53 +150,61 @@ fn test_assert_only_role_unauthorized_when_authorized_for_another_role() { #[test] #[available_gas(2000000)] fn test_grant_role() { - setup(); - AccessControl::grant_role(ROLE, AUTHORIZED()); - assert(AccessControl::has_role(ROLE, AUTHORIZED()), 'Role should be granted'); + let mut state = setup(); + testing::set_caller_address(ADMIN()); + AccessControlImpl::grant_role(ref state, ROLE, AUTHORIZED()); + assert(AccessControlImpl::has_role(@state, ROLE, AUTHORIZED()), 'Role should be granted'); } #[test] #[available_gas(2000000)] -fn test_grant_role_multiple_times_for_granted_role() { - setup(); - AccessControl::grant_role(ROLE, AUTHORIZED()); - AccessControl::grant_role(ROLE, AUTHORIZED()); - assert(AccessControl::has_role(ROLE, AUTHORIZED()), 'Role should still be granted'); +fn test_grantRole() { + let mut state = setup(); + testing::set_caller_address(ADMIN()); + AccessControlCamelImpl::grantRole(ref state, ROLE, AUTHORIZED()); + assert(AccessControlCamelImpl::hasRole(@state, ROLE, AUTHORIZED()), 'Role should be granted'); } #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Caller is missing role', ))] -fn test_grant_role_unauthorized() { - setup(); - testing::set_caller_address(AUTHORIZED()); - AccessControl::grant_role(ROLE, AUTHORIZED()); +fn test_grant_role_multiple_times_for_granted_role() { + let mut state = setup(); + testing::set_caller_address(ADMIN()); + + AccessControlImpl::grant_role(ref state, ROLE, AUTHORIZED()); + AccessControlImpl::grant_role(ref state, ROLE, AUTHORIZED()); + assert(AccessControlImpl::has_role(@state, ROLE, AUTHORIZED()), 'Role should still be granted'); } #[test] #[available_gas(2000000)] -fn test_grantRole() { - setup(); - AccessControl::grantRole(ROLE, AUTHORIZED()); - assert(AccessControl::hasRole(ROLE, AUTHORIZED()), 'Role should be granted'); +fn test_grantRole_multiple_times_for_granted_role() { + let mut state = setup(); + testing::set_caller_address(ADMIN()); + + AccessControlCamelImpl::grantRole(ref state, ROLE, AUTHORIZED()); + AccessControlCamelImpl::grantRole(ref state, ROLE, AUTHORIZED()); + assert( + AccessControlCamelImpl::hasRole(@state, ROLE, AUTHORIZED()), 'Role should still be granted' + ); } #[test] #[available_gas(2000000)] -fn test_grantRole_multiple_times_for_granted_role() { - setup(); - AccessControl::grantRole(ROLE, AUTHORIZED()); - AccessControl::grantRole(ROLE, AUTHORIZED()); - assert(AccessControl::hasRole(ROLE, AUTHORIZED()), 'Role should still be granted'); +#[should_panic(expected: ('Caller is missing role', ))] +fn test_grant_role_unauthorized() { + let mut state = setup(); + testing::set_caller_address(AUTHORIZED()); + AccessControlImpl::grant_role(ref state, ROLE, AUTHORIZED()); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('Caller is missing role', ))] fn test_grantRole_unauthorized() { - setup(); + let mut state = setup(); testing::set_caller_address(AUTHORIZED()); - AccessControl::grantRole(ROLE, AUTHORIZED()); + AccessControlCamelImpl::grantRole(ref state, ROLE, AUTHORIZED()); } // @@ -176,75 +214,85 @@ fn test_grantRole_unauthorized() { #[test] #[available_gas(2000000)] fn test_revoke_role_for_role_not_granted() { - setup(); - AccessControl::revoke_role(ROLE, AUTHORIZED()); + let mut state = setup(); + testing::set_caller_address(ADMIN()); + AccessControlImpl::revoke_role(ref state, ROLE, AUTHORIZED()); } #[test] #[available_gas(2000000)] -fn test_revoke_role_for_granted_role() { - setup(); - AccessControl::grant_role(ROLE, AUTHORIZED()); - AccessControl::revoke_role(ROLE, AUTHORIZED()); - assert(!AccessControl::has_role(ROLE, AUTHORIZED()), 'Role should be revoked'); +fn test_revokeRole_for_role_not_granted() { + let mut state = setup(); + testing::set_caller_address(ADMIN()); + AccessControlCamelImpl::revokeRole(ref state, ROLE, AUTHORIZED()); } #[test] #[available_gas(2000000)] -fn test_revoke_role_multiple_times_for_granted_role() { - setup(); - AccessControl::grant_role(ROLE, AUTHORIZED()); +fn test_revoke_role_for_granted_role() { + let mut state = setup(); + testing::set_caller_address(ADMIN()); - AccessControl::revoke_role(ROLE, AUTHORIZED()); - AccessControl::revoke_role(ROLE, AUTHORIZED()); - assert(!AccessControl::has_role(ROLE, AUTHORIZED()), 'Role should still be revoked'); + AccessControlImpl::grant_role(ref state, ROLE, AUTHORIZED()); + AccessControlImpl::revoke_role(ref state, ROLE, AUTHORIZED()); + assert(!AccessControlImpl::has_role(@state, ROLE, AUTHORIZED()), 'Role should be revoked'); } #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Caller is missing role', ))] -fn test_revoke_role_unauthorized() { - setup(); +fn test_revokeRole_for_granted_role() { + let mut state = setup(); + testing::set_caller_address(ADMIN()); - testing::set_caller_address(OTHER()); - AccessControl::revoke_role(ROLE, AUTHORIZED()); + AccessControlCamelImpl::grantRole(ref state, ROLE, AUTHORIZED()); + AccessControlCamelImpl::revokeRole(ref state, ROLE, AUTHORIZED()); + assert(!AccessControlCamelImpl::hasRole(@state, ROLE, AUTHORIZED()), 'Role should be revoked'); } #[test] #[available_gas(2000000)] -fn test_revokeRole_for_role_not_granted() { - setup(); - AccessControl::revokeRole(ROLE, AUTHORIZED()); +fn test_revoke_role_multiple_times_for_granted_role() { + let mut state = setup(); + testing::set_caller_address(ADMIN()); + + AccessControlImpl::grant_role(ref state, ROLE, AUTHORIZED()); + AccessControlImpl::revoke_role(ref state, ROLE, AUTHORIZED()); + AccessControlImpl::revoke_role(ref state, ROLE, AUTHORIZED()); + assert( + !AccessControlImpl::has_role(@state, ROLE, AUTHORIZED()), 'Role should still be revoked' + ); } #[test] #[available_gas(2000000)] -fn test_revokeRole_for_granted_role() { - setup(); - AccessControl::grantRole(ROLE, AUTHORIZED()); - AccessControl::revokeRole(ROLE, AUTHORIZED()); - assert(!AccessControl::hasRole(ROLE, AUTHORIZED()), 'Role should be revoked'); +fn test_revokeRole_multiple_times_for_granted_role() { + let mut state = setup(); + testing::set_caller_address(ADMIN()); + + AccessControlCamelImpl::grantRole(ref state, ROLE, AUTHORIZED()); + AccessControlCamelImpl::revokeRole(ref state, ROLE, AUTHORIZED()); + AccessControlCamelImpl::revokeRole(ref state, ROLE, AUTHORIZED()); + assert( + !AccessControlCamelImpl::hasRole(@state, ROLE, AUTHORIZED()), 'Role should still be revoked' + ); } #[test] #[available_gas(2000000)] -fn test_revokeRole_multiple_times_for_granted_role() { - setup(); - AccessControl::grantRole(ROLE, AUTHORIZED()); - - AccessControl::revokeRole(ROLE, AUTHORIZED()); - AccessControl::revokeRole(ROLE, AUTHORIZED()); - assert(!AccessControl::hasRole(ROLE, AUTHORIZED()), 'Role should still be revoked'); +#[should_panic(expected: ('Caller is missing role', ))] +fn test_revoke_role_unauthorized() { + let mut state = setup(); + testing::set_caller_address(OTHER()); + AccessControlImpl::revoke_role(ref state, ROLE, AUTHORIZED()); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('Caller is missing role', ))] fn test_revokeRole_unauthorized() { - setup(); - + let mut state = setup(); testing::set_caller_address(OTHER()); - AccessControl::revokeRole(ROLE, AUTHORIZED()); + AccessControlCamelImpl::revokeRole(ref state, ROLE, AUTHORIZED()); } // @@ -254,87 +302,98 @@ fn test_revokeRole_unauthorized() { #[test] #[available_gas(2000000)] fn test_renounce_role_for_role_not_granted() { - setup(); + let mut state = setup(); testing::set_caller_address(AUTHORIZED()); - - AccessControl::renounce_role(ROLE, AUTHORIZED()); + AccessControlImpl::renounce_role(ref state, ROLE, AUTHORIZED()); } #[test] #[available_gas(2000000)] -fn test_renounce_role_for_granted_role() { - setup(); - AccessControl::grant_role(ROLE, AUTHORIZED()); +fn test_renounceRole_for_role_not_granted() { + let mut state = setup(); testing::set_caller_address(AUTHORIZED()); - - AccessControl::renounce_role(ROLE, AUTHORIZED()); - assert(!AccessControl::has_role(ROLE, AUTHORIZED()), 'Role should be renounced'); + AccessControlCamelImpl::renounceRole(ref state, ROLE, AUTHORIZED()); } #[test] #[available_gas(2000000)] -fn test_renounce_role_multiple_times_for_granted_role() { - setup(); - AccessControl::grant_role(ROLE, AUTHORIZED()); - testing::set_caller_address(AUTHORIZED()); +fn test_renounce_role_for_granted_role() { + let mut state = setup(); + testing::set_caller_address(ADMIN()); + AccessControlImpl::grant_role(ref state, ROLE, AUTHORIZED()); - AccessControl::renounce_role(ROLE, AUTHORIZED()); - AccessControl::renounce_role(ROLE, AUTHORIZED()); - assert(!AccessControl::has_role(ROLE, AUTHORIZED()), 'Role should still be renounced'); + testing::set_caller_address(AUTHORIZED()); + AccessControlImpl::renounce_role(ref state, ROLE, AUTHORIZED()); + assert(!AccessControlImpl::has_role(@state, ROLE, AUTHORIZED()), 'Role should be renounced'); } #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Can only renounce role for self', ))] -fn test_renounce_role_unauthorized() { - setup(); - AccessControl::grant_role(ROLE, AUTHORIZED()); +fn test_renounceRole_for_granted_role() { + let mut state = setup(); + testing::set_caller_address(ADMIN()); + AccessControlCamelImpl::grantRole(ref state, ROLE, AUTHORIZED()); - // Admin is unauthorized caller - AccessControl::renounce_role(ROLE, AUTHORIZED()); + testing::set_caller_address(AUTHORIZED()); + AccessControlCamelImpl::renounceRole(ref state, ROLE, AUTHORIZED()); + assert( + !AccessControlCamelImpl::hasRole(@state, ROLE, AUTHORIZED()), 'Role should be renounced' + ); } #[test] #[available_gas(2000000)] -fn test_renounceRole_for_role_not_granted() { - setup(); - testing::set_caller_address(AUTHORIZED()); +fn test_renounce_role_multiple_times_for_granted_role() { + let mut state = setup(); + testing::set_caller_address(ADMIN()); + AccessControlImpl::grant_role(ref state, ROLE, AUTHORIZED()); - AccessControl::renounceRole(ROLE, AUTHORIZED()); + testing::set_caller_address(AUTHORIZED()); + AccessControlImpl::renounce_role(ref state, ROLE, AUTHORIZED()); + AccessControlImpl::renounce_role(ref state, ROLE, AUTHORIZED()); + assert( + !AccessControlImpl::has_role(@state, ROLE, AUTHORIZED()), 'Role should still be renounced' + ); } #[test] #[available_gas(2000000)] -fn test_renounceRole_for_granted_role() { - setup(); - AccessControl::grantRole(ROLE, AUTHORIZED()); - testing::set_caller_address(AUTHORIZED()); +fn test_renounceRole_multiple_times_for_granted_role() { + let mut state = setup(); + testing::set_caller_address(ADMIN()); + AccessControlCamelImpl::grantRole(ref state, ROLE, AUTHORIZED()); - AccessControl::renounceRole(ROLE, AUTHORIZED()); - assert(!AccessControl::hasRole(ROLE, AUTHORIZED()), 'Role should be renounced'); + testing::set_caller_address(AUTHORIZED()); + AccessControlCamelImpl::renounceRole(ref state, ROLE, AUTHORIZED()); + AccessControlCamelImpl::renounceRole(ref state, ROLE, AUTHORIZED()); + assert( + !AccessControlCamelImpl::hasRole(@state, ROLE, AUTHORIZED()), + 'Role should still be renounced' + ); } #[test] #[available_gas(2000000)] -fn test_renounceRole_multiple_times_for_granted_role() { - setup(); - AccessControl::grantRole(ROLE, AUTHORIZED()); - testing::set_caller_address(AUTHORIZED()); +#[should_panic(expected: ('Can only renounce role for self', ))] +fn test_renounce_role_unauthorized() { + let mut state = setup(); + testing::set_caller_address(ADMIN()); + AccessControlImpl::grant_role(ref state, ROLE, AUTHORIZED()); - AccessControl::renounceRole(ROLE, AUTHORIZED()); - AccessControl::renounceRole(ROLE, AUTHORIZED()); - assert(!AccessControl::hasRole(ROLE, AUTHORIZED()), 'Role should still be renounced'); + testing::set_caller_address(ZERO()); + AccessControlImpl::renounce_role(ref state, ROLE, AUTHORIZED()); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('Can only renounce role for self', ))] fn test_renounceRole_unauthorized() { - setup(); - AccessControl::grantRole(ROLE, AUTHORIZED()); + let mut state = setup(); + testing::set_caller_address(ADMIN()); + AccessControlCamelImpl::grantRole(ref state, ROLE, AUTHORIZED()); // Admin is unauthorized caller - AccessControl::renounceRole(ROLE, AUTHORIZED()); + AccessControlCamelImpl::renounceRole(ref state, ROLE, AUTHORIZED()); } // @@ -344,61 +403,67 @@ fn test_renounceRole_unauthorized() { #[test] #[available_gas(2000000)] fn test__set_role_admin() { - setup(); + let mut state = setup(); + assert( + AccessControlImpl::get_role_admin(@state, ROLE) == DEFAULT_ADMIN_ROLE, + 'ROLE admin default should be 0' + ); + InternalImpl::_set_role_admin(ref state, ROLE, OTHER_ROLE); assert( - AccessControl::get_role_admin(ROLE) == DEFAULT_ADMIN_ROLE, 'ROLE admin default should be 0' + AccessControlImpl::get_role_admin(@state, ROLE) == OTHER_ROLE, + 'ROLE admin should be OTHER_ROLE' ); - AccessControl::_set_role_admin(ROLE, OTHER_ROLE); - assert(AccessControl::get_role_admin(ROLE) == OTHER_ROLE, 'ROLE admin should be OTHER_ROLE'); } #[test] #[available_gas(2000000)] fn test_new_admin_can_grant_roles() { - setup(); - AccessControl::_set_role_admin(ROLE, OTHER_ROLE); - AccessControl::grant_role(OTHER_ROLE, OTHER_ADMIN()); + let mut state = setup(); + InternalImpl::_set_role_admin(ref state, ROLE, OTHER_ROLE); - testing::set_caller_address(OTHER_ADMIN()); - AccessControl::grant_role(ROLE, AUTHORIZED()); + testing::set_caller_address(ADMIN()); + AccessControlImpl::grant_role(ref state, OTHER_ROLE, OTHER_ADMIN()); - assert(AccessControl::has_role(ROLE, AUTHORIZED()), 'AUTHORIZED should have ROLE'); + testing::set_caller_address(OTHER_ADMIN()); + AccessControlImpl::grant_role(ref state, ROLE, AUTHORIZED()); + assert(AccessControlImpl::has_role(@state, ROLE, AUTHORIZED()), 'AUTHORIZED should have ROLE'); } #[test] #[available_gas(2000000)] fn test_new_admin_can_revoke_roles() { - setup(); - AccessControl::_set_role_admin(ROLE, OTHER_ROLE); - AccessControl::grant_role(OTHER_ROLE, OTHER_ADMIN()); + let mut state = setup(); + InternalImpl::_set_role_admin(ref state, ROLE, OTHER_ROLE); - testing::set_caller_address(OTHER_ADMIN()); - AccessControl::grant_role(ROLE, AUTHORIZED()); - AccessControl::revoke_role(ROLE, AUTHORIZED()); + testing::set_caller_address(ADMIN()); + AccessControlImpl::grant_role(ref state, OTHER_ROLE, OTHER_ADMIN()); - assert(!AccessControl::has_role(ROLE, AUTHORIZED()), 'AUTHORIZED should not have ROLE'); + testing::set_caller_address(OTHER_ADMIN()); + AccessControlImpl::grant_role(ref state, ROLE, AUTHORIZED()); + AccessControlImpl::revoke_role(ref state, ROLE, AUTHORIZED()); + assert( + !AccessControlImpl::has_role(@state, ROLE, AUTHORIZED()), 'AUTHORIZED should not have ROLE' + ); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('Caller is missing role', ))] fn test_previous_admin_cannot_grant_roles() { - setup(); - AccessControl::_set_role_admin(ROLE, OTHER_ROLE); - - // Caller is ADMIN - AccessControl::grant_role(ROLE, AUTHORIZED()); + let mut state = setup(); + InternalImpl::_set_role_admin(ref state, ROLE, OTHER_ROLE); + testing::set_caller_address(ADMIN()); + AccessControlImpl::grant_role(ref state, ROLE, AUTHORIZED()); } #[test] #[available_gas(2000000)] #[should_panic(expected: ('Caller is missing role', ))] fn test_previous_admin_cannot_revoke_roles() { - setup(); - AccessControl::_set_role_admin(ROLE, OTHER_ROLE); - - // Caller is ADMIN - AccessControl::revoke_role(ROLE, AUTHORIZED()); + let mut state = setup(); + InternalImpl::_set_role_admin(ref state, ROLE, OTHER_ROLE); + testing::set_caller_address(ADMIN()); + AccessControlImpl::revoke_role(ref state, ROLE, AUTHORIZED()); } // @@ -408,8 +473,9 @@ fn test_previous_admin_cannot_revoke_roles() { #[test] #[available_gas(2000000)] fn test_other_role_admin_is_the_default_admin_role() { + let state = setup(); assert( - AccessControl::get_role_admin(OTHER_ROLE) == DEFAULT_ADMIN_ROLE, + AccessControlImpl::get_role_admin(@state, OTHER_ROLE) == DEFAULT_ADMIN_ROLE, 'Should be DEFAULT_ADMIN_ROLE' ); } @@ -417,8 +483,9 @@ fn test_other_role_admin_is_the_default_admin_role() { #[test] #[available_gas(2000000)] fn test_default_admin_role_is_its_own_admin() { + let state = setup(); assert( - AccessControl::get_role_admin(DEFAULT_ADMIN_ROLE) == DEFAULT_ADMIN_ROLE, + AccessControlImpl::get_role_admin(@state, DEFAULT_ADMIN_ROLE) == DEFAULT_ADMIN_ROLE, 'Should be DEFAULT_ADMIN_ROLE' ); } diff --git a/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo b/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo index 7a229576e..7fc3b5049 100644 --- a/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo +++ b/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo @@ -38,7 +38,7 @@ fn AUTHORIZED() -> ContractAddress { // fn setup_snake() -> (DualCaseAccessControl, IAccessControlDispatcher) { - let mut calldata = ArrayTrait::new(); + let mut calldata = array![]; calldata.append_serde(ADMIN()); let target = utils::deploy(SnakeAccessControlMock::TEST_CLASS_HASH, calldata); ( @@ -48,7 +48,7 @@ fn setup_snake() -> (DualCaseAccessControl, IAccessControlDispatcher) { } fn setup_camel() -> (DualCaseAccessControl, IAccessControlCamelDispatcher) { - let mut calldata = ArrayTrait::new(); + let mut calldata = array![]; calldata.append_serde(ADMIN()); let target = utils::deploy(CamelAccessControlMock::TEST_CLASS_HASH, calldata); ( @@ -58,18 +58,13 @@ fn setup_camel() -> (DualCaseAccessControl, IAccessControlCamelDispatcher) { } fn setup_non_accesscontrol() -> DualCaseAccessControl { - let calldata = ArrayTrait::new(); - let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); + let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); DualCaseAccessControl { contract_address: target } } fn setup_accesscontrol_panic() -> (DualCaseAccessControl, DualCaseAccessControl) { - let snake_target = utils::deploy( - SnakeAccessControlPanicMock::TEST_CLASS_HASH, ArrayTrait::new() - ); - let camel_target = utils::deploy( - CamelAccessControlPanicMock::TEST_CLASS_HASH, ArrayTrait::new() - ); + let snake_target = utils::deploy(SnakeAccessControlPanicMock::TEST_CLASS_HASH, array![]); + let camel_target = utils::deploy(CamelAccessControlPanicMock::TEST_CLASS_HASH, array![]); ( DualCaseAccessControl { contract_address: snake_target }, DualCaseAccessControl { contract_address: camel_target } diff --git a/src/openzeppelin/tests/mocks.cairo b/src/openzeppelin/tests/mocks.cairo index 4a8530b24..fd2e8f322 100644 --- a/src/openzeppelin/tests/mocks.cairo +++ b/src/openzeppelin/tests/mocks.cairo @@ -5,11 +5,11 @@ mod erc721_panic_mock; mod camel20_mock; mod snake20_mock; mod erc20_panic; -// mod accesscontrol_panic_mock; +mod accesscontrol_panic_mock; mod account_panic_mock; -// mod camel_accesscontrol_mock; +mod camel_accesscontrol_mock; mod camel_account_mock; -// mod snake_accesscontrol_mock; +mod snake_accesscontrol_mock; mod snake_account_mock; mod dual_ownable_mocks; mod snake721_mock; diff --git a/src/openzeppelin/tests/mocks/accesscontrol_panic_mock.cairo b/src/openzeppelin/tests/mocks/accesscontrol_panic_mock.cairo index 10601ca78..8ccfe80dc 100644 --- a/src/openzeppelin/tests/mocks/accesscontrol_panic_mock.cairo +++ b/src/openzeppelin/tests/mocks/accesscontrol_panic_mock.cairo @@ -4,78 +4,84 @@ // 3 for felt252 // false for bool -#[contract] +#[starknet::contract] mod SnakeAccessControlPanicMock { use starknet::ContractAddress; - #[view] - fn supports_interface(interface_id: felt252) -> bool { + #[storage] + struct Storage {} + + #[external(v0)] + fn has_role(self: @ContractState, role: felt252, account: ContractAddress) -> bool { panic_with_felt252('Some error'); false } - #[view] - fn has_role(role: felt252, account: ContractAddress) -> bool { + #[external(v0)] + fn get_role_admin(self: @ContractState, role: felt252) -> felt252 { panic_with_felt252('Some error'); - false + 3 } - #[view] - fn get_role_admin(role: felt252) -> felt252 { + #[external(v0)] + fn grant_role(ref self: ContractState, role: felt252, account: ContractAddress) { panic_with_felt252('Some error'); - 3 } - #[external] - fn grant_role(role: felt252, account: ContractAddress) { + #[external(v0)] + fn revoke_role(ref self: ContractState, role: felt252, account: ContractAddress) { panic_with_felt252('Some error'); } - #[external] - fn revoke_role(role: felt252, account: ContractAddress) { + #[external(v0)] + fn renounce_role(ref self: ContractState, role: felt252, account: ContractAddress) { panic_with_felt252('Some error'); } - #[external] - fn renounce_role(role: felt252, account: ContractAddress) { + #[external(v0)] + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { panic_with_felt252('Some error'); + false } } -#[contract] +#[starknet::contract] mod CamelAccessControlPanicMock { use starknet::ContractAddress; - #[view] - fn supportsInterface(interfaceId: felt252) -> bool { + #[storage] + struct Storage {} + + #[external(v0)] + fn hasRole(self: @ContractState, role: felt252, account: ContractAddress) -> bool { panic_with_felt252('Some error'); false } - #[view] - fn hasRole(role: felt252, account: ContractAddress) -> bool { + #[external(v0)] + fn getRoleAdmin(self: @ContractState, role: felt252) -> felt252 { panic_with_felt252('Some error'); - false + 3 } - #[view] - fn getRoleAdmin(role: felt252) -> felt252 { + #[external(v0)] + fn grantRole(ref self: ContractState, role: felt252, account: ContractAddress) { panic_with_felt252('Some error'); - 3 } - #[external] - fn grantRole(role: felt252, account: ContractAddress) { + #[external(v0)] + fn revokeRole(ref self: ContractState, role: felt252, account: ContractAddress) { panic_with_felt252('Some error'); } - #[external] - fn revokeRole(role: felt252, account: ContractAddress) { + #[external(v0)] + fn renounceRole(ref self: ContractState, role: felt252, account: ContractAddress) { panic_with_felt252('Some error'); } - #[external] - fn renounceRole(role: felt252, account: ContractAddress) { + #[external(v0)] + fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { panic_with_felt252('Some error'); + false } } diff --git a/src/openzeppelin/tests/mocks/camel_accesscontrol_mock.cairo b/src/openzeppelin/tests/mocks/camel_accesscontrol_mock.cairo index dff5ab1b6..dc0879a60 100644 --- a/src/openzeppelin/tests/mocks/camel_accesscontrol_mock.cairo +++ b/src/openzeppelin/tests/mocks/camel_accesscontrol_mock.cairo @@ -1,43 +1,54 @@ -#[contract] +#[starknet::contract] mod CamelAccessControlMock { use starknet::ContractAddress; use starknet::get_caller_address; use openzeppelin::access::accesscontrol::AccessControl; + use openzeppelin::access::accesscontrol::AccessControl::AccessControlCamelImpl; use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; + #[storage] + struct Storage {} + #[constructor] - fn constructor(admin: ContractAddress) { - AccessControl::initializer(); - AccessControl::_grant_role(DEFAULT_ADMIN_ROLE, admin); + fn constructor(ref self: ContractState, admin: ContractAddress) { + let mut unsafe_state = AccessControl::unsafe_new_contract_state(); + AccessControl::InternalImpl::initializer(ref unsafe_state); + AccessControl::InternalImpl::_grant_role(ref unsafe_state, DEFAULT_ADMIN_ROLE, admin); } - #[view] - fn supportsInterface(interfaceId: felt252) -> bool { - AccessControl::supportsInterface(interfaceId) + #[external(v0)] + fn hasRole(self: @ContractState, role: felt252, account: ContractAddress) -> bool { + let unsafe_state = AccessControl::unsafe_new_contract_state(); + AccessControlCamelImpl::hasRole(@unsafe_state, role, account) } - #[view] - fn hasRole(role: felt252, account: ContractAddress) -> bool { - AccessControl::hasRole(role, account) + #[external(v0)] + fn getRoleAdmin(self: @ContractState, role: felt252) -> felt252 { + let unsafe_state = AccessControl::unsafe_new_contract_state(); + AccessControlCamelImpl::getRoleAdmin(@unsafe_state, role) } - #[view] - fn getRoleAdmin(role: felt252) -> felt252 { - AccessControl::getRoleAdmin(role) + #[external(v0)] + fn grantRole(ref self: ContractState, role: felt252, account: ContractAddress) { + let mut unsafe_state = AccessControl::unsafe_new_contract_state(); + AccessControlCamelImpl::grantRole(ref unsafe_state, role, account); } - #[external] - fn grantRole(role: felt252, account: ContractAddress) { - AccessControl::grantRole(role, account); + #[external(v0)] + fn revokeRole(ref self: ContractState, role: felt252, account: ContractAddress) { + let mut unsafe_state = AccessControl::unsafe_new_contract_state(); + AccessControlCamelImpl::revokeRole(ref unsafe_state, role, account); } - #[external] - fn revokeRole(role: felt252, account: ContractAddress) { - AccessControl::revokeRole(role, account); + #[external(v0)] + fn renounceRole(ref self: ContractState, role: felt252, account: ContractAddress) { + let mut unsafe_state = AccessControl::unsafe_new_contract_state(); + AccessControlCamelImpl::renounceRole(ref unsafe_state, role, account); } - #[external] - fn renounceRole(role: felt252, account: ContractAddress) { - AccessControl::renounceRole(role, account); + #[external(v0)] + fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { + let unsafe_state = AccessControl::unsafe_new_contract_state(); + AccessControl::SRC5CamelImpl::supportsInterface(@unsafe_state, interfaceId) } } diff --git a/src/openzeppelin/tests/mocks/snake_accesscontrol_mock.cairo b/src/openzeppelin/tests/mocks/snake_accesscontrol_mock.cairo index ea3122a67..1d69feaec 100644 --- a/src/openzeppelin/tests/mocks/snake_accesscontrol_mock.cairo +++ b/src/openzeppelin/tests/mocks/snake_accesscontrol_mock.cairo @@ -1,44 +1,54 @@ -#[contract] +#[starknet::contract] mod SnakeAccessControlMock { use starknet::ContractAddress; use starknet::get_caller_address; use openzeppelin::access::accesscontrol::AccessControl; + use openzeppelin::access::accesscontrol::AccessControl::AccessControlImpl; use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; - use openzeppelin::utils::serde::SpanSerde; + + #[storage] + struct Storage {} #[constructor] - fn constructor(admin: ContractAddress) { - AccessControl::initializer(); - AccessControl::_grant_role(DEFAULT_ADMIN_ROLE, admin); + fn constructor(ref self: ContractState, admin: ContractAddress) { + let mut unsafe_state = AccessControl::unsafe_new_contract_state(); + AccessControl::InternalImpl::initializer(ref unsafe_state); + AccessControl::InternalImpl::_grant_role(ref unsafe_state, DEFAULT_ADMIN_ROLE, admin); } - #[view] - fn supports_interface(interface_id: felt252) -> bool { - AccessControl::supports_interface(interface_id) + #[external(v0)] + fn has_role(self: @ContractState, role: felt252, account: ContractAddress) -> bool { + let unsafe_state = AccessControl::unsafe_new_contract_state(); + AccessControlImpl::has_role(@unsafe_state, role, account) } - #[view] - fn has_role(role: felt252, account: ContractAddress) -> bool { - AccessControl::has_role(role, account) + #[external(v0)] + fn get_role_admin(self: @ContractState, role: felt252) -> felt252 { + let unsafe_state = AccessControl::unsafe_new_contract_state(); + AccessControlImpl::get_role_admin(@unsafe_state, role) } - #[view] - fn get_role_admin(role: felt252) -> felt252 { - AccessControl::get_role_admin(role) + #[external(v0)] + fn grant_role(ref self: ContractState, role: felt252, account: ContractAddress) { + let mut unsafe_state = AccessControl::unsafe_new_contract_state(); + AccessControlImpl::grant_role(ref unsafe_state, role, account); } - #[external] - fn grant_role(role: felt252, account: ContractAddress) { - AccessControl::grant_role(role, account); + #[external(v0)] + fn revoke_role(ref self: ContractState, role: felt252, account: ContractAddress) { + let mut unsafe_state = AccessControl::unsafe_new_contract_state(); + AccessControlImpl::revoke_role(ref unsafe_state, role, account); } - #[external] - fn revoke_role(role: felt252, account: ContractAddress) { - AccessControl::revoke_role(role, account); + #[external(v0)] + fn renounce_role(ref self: ContractState, role: felt252, account: ContractAddress) { + let mut unsafe_state = AccessControl::unsafe_new_contract_state(); + AccessControlImpl::renounce_role(ref unsafe_state, role, account); } - #[external] - fn renounce_role(role: felt252, account: ContractAddress) { - AccessControl::renounce_role(role, account); + #[external(v0)] + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { + let unsafe_state = AccessControl::unsafe_new_contract_state(); + AccessControl::SRC5Impl::supports_interface(@unsafe_state, interface_id) } } From 79fe2db03f5f290b98a0ea671a6f4799638a5d8e Mon Sep 17 00:00:00 2001 From: maciektr Date: Fri, 4 Aug 2023 03:24:54 +0200 Subject: [PATCH 074/246] Migrate to Scarb for cairo-2 (#671) * Migrate to Scarb * Hide tests module behind cfg --- .github/workflows/test.yml | 11 +- .gitignore | 8 +- CONTRIBUTING.md | 4 +- Cargo.lock | 3623 ----------------- Cargo.toml | 115 - Makefile | 28 - Scarb.toml | 21 + cairo | 1 - src/{openzeppelin => }/access.cairo | 0 .../access/accesscontrol.cairo | 0 .../access/accesscontrol/accesscontrol.cairo | 0 .../accesscontrol/dual_accesscontrol.cairo | 0 .../access/accesscontrol/interface.cairo | 0 src/{openzeppelin => }/access/ownable.cairo | 0 .../access/ownable/dual_ownable.cairo | 0 .../access/ownable/interface.cairo | 0 .../access/ownable/ownable.cairo | 0 src/{openzeppelin => }/account.cairo | 0 src/{openzeppelin => }/account/account.cairo | 0 .../account/dual_account.cairo | 0 .../account/interface.cairo | 0 src/{openzeppelin => }/cairo_project.toml | 0 src/{openzeppelin => }/introspection.cairo | 0 .../introspection/dual_src5.cairo | 0 .../introspection/interface.cairo | 0 .../introspection/src5.cairo | 0 src/{openzeppelin => }/lib.cairo | 4 +- src/{openzeppelin => }/security.cairo | 0 .../security/initializable.cairo | 0 .../security/pausable.cairo | 0 .../security/reentrancyguard.cairo | 0 src/{openzeppelin => }/tests.cairo | 0 src/{openzeppelin => }/tests/access.cairo | 0 .../tests/access/test_accesscontrol.cairo | 0 .../access/test_dual_accesscontrol.cairo | 21 +- .../tests/access/test_dual_ownable.cairo | 14 +- .../tests/access/test_ownable.cairo | 0 src/{openzeppelin => }/tests/account.cairo | 0 .../tests/account/test_account.cairo | 0 .../tests/account/test_dual_account.cairo | 21 +- .../tests/introspection.cairo | 0 .../tests/introspection/test_dual_src5.cairo | 7 +- .../tests/introspection/test_src5.cairo | 0 src/{openzeppelin => }/tests/mocks.cairo | 0 .../mocks/accesscontrol_panic_mock.cairo | 0 .../tests/mocks/account_panic_mock.cairo | 0 .../tests/mocks/camel20_mock.cairo | 0 .../tests/mocks/camel721_mock.cairo | 0 .../mocks/camel_accesscontrol_mock.cairo | 0 .../tests/mocks/camel_account_mock.cairo | 0 .../tests/mocks/dual721_receiver_mocks.cairo | 0 .../tests/mocks/dual_ownable_mocks.cairo | 0 .../tests/mocks/erc20_panic.cairo | 0 .../tests/mocks/erc721_panic_mock.cairo | 0 .../tests/mocks/erc721_receiver.cairo | 0 .../tests/mocks/non_implementing_mock.cairo | 0 .../mocks/reentrancy_attacker_mock.cairo | 0 .../tests/mocks/reentrancy_mock.cairo | 5 +- .../tests/mocks/snake20_mock.cairo | 0 .../tests/mocks/snake721_mock.cairo | 0 .../mocks/snake_accesscontrol_mock.cairo | 0 .../tests/mocks/snake_account_mock.cairo | 0 .../tests/mocks/src5_mocks.cairo | 0 src/{openzeppelin => }/tests/security.cairo | 0 .../tests/security/test_initializable.cairo | 0 .../tests/security/test_pausable.cairo | 0 .../tests/security/test_reentrancyguard.cairo | 0 src/{openzeppelin => }/tests/token.cairo | 0 .../tests/token/test_dual20.cairo | 0 .../tests/token/test_dual721.cairo | 14 +- .../tests/token/test_dual721_receiver.cairo | 21 +- .../tests/token/test_erc20.cairo | 0 .../tests/token/test_erc721.cairo | 0 src/{openzeppelin => }/tests/utils.cairo | 0 src/{openzeppelin => }/token.cairo | 0 src/{openzeppelin => }/token/erc20.cairo | 0 .../token/erc20/dual20.cairo | 0 .../token/erc20/erc20.cairo | 0 .../token/erc20/interface.cairo | 0 src/{openzeppelin => }/token/erc721.cairo | 0 .../token/erc721/dual721.cairo | 0 .../token/erc721/dual721_receiver.cairo | 0 .../token/erc721/erc721.cairo | 9 +- .../token/erc721/interface.cairo | 0 src/{openzeppelin => }/utils.cairo | 0 src/{openzeppelin => }/utils/constants.cairo | 0 src/{openzeppelin => }/utils/selectors.cairo | 0 src/{openzeppelin => }/utils/serde.cairo | 0 .../utils/unwrap_and_cast.cairo | 0 89 files changed, 114 insertions(+), 3813 deletions(-) delete mode 100644 Cargo.lock delete mode 100644 Cargo.toml delete mode 100644 Makefile create mode 100644 Scarb.toml delete mode 160000 cairo rename src/{openzeppelin => }/access.cairo (100%) rename src/{openzeppelin => }/access/accesscontrol.cairo (100%) rename src/{openzeppelin => }/access/accesscontrol/accesscontrol.cairo (100%) rename src/{openzeppelin => }/access/accesscontrol/dual_accesscontrol.cairo (100%) rename src/{openzeppelin => }/access/accesscontrol/interface.cairo (100%) rename src/{openzeppelin => }/access/ownable.cairo (100%) rename src/{openzeppelin => }/access/ownable/dual_ownable.cairo (100%) rename src/{openzeppelin => }/access/ownable/interface.cairo (100%) rename src/{openzeppelin => }/access/ownable/ownable.cairo (100%) rename src/{openzeppelin => }/account.cairo (100%) rename src/{openzeppelin => }/account/account.cairo (100%) rename src/{openzeppelin => }/account/dual_account.cairo (100%) rename src/{openzeppelin => }/account/interface.cairo (100%) rename src/{openzeppelin => }/cairo_project.toml (100%) rename src/{openzeppelin => }/introspection.cairo (100%) rename src/{openzeppelin => }/introspection/dual_src5.cairo (100%) rename src/{openzeppelin => }/introspection/interface.cairo (100%) rename src/{openzeppelin => }/introspection/src5.cairo (100%) rename src/{openzeppelin => }/lib.cairo (86%) rename src/{openzeppelin => }/security.cairo (100%) rename src/{openzeppelin => }/security/initializable.cairo (100%) rename src/{openzeppelin => }/security/pausable.cairo (100%) rename src/{openzeppelin => }/security/reentrancyguard.cairo (100%) rename src/{openzeppelin => }/tests.cairo (100%) rename src/{openzeppelin => }/tests/access.cairo (100%) rename src/{openzeppelin => }/tests/access/test_accesscontrol.cairo (100%) rename src/{openzeppelin => }/tests/access/test_dual_accesscontrol.cairo (95%) rename src/{openzeppelin => }/tests/access/test_dual_ownable.cairo (94%) rename src/{openzeppelin => }/tests/access/test_ownable.cairo (100%) rename src/{openzeppelin => }/tests/account.cairo (100%) rename src/{openzeppelin => }/tests/account/test_account.cairo (100%) rename src/{openzeppelin => }/tests/account/test_dual_account.cairo (94%) rename src/{openzeppelin => }/tests/introspection.cairo (100%) rename src/{openzeppelin => }/tests/introspection/test_dual_src5.cairo (94%) rename src/{openzeppelin => }/tests/introspection/test_src5.cairo (100%) rename src/{openzeppelin => }/tests/mocks.cairo (100%) rename src/{openzeppelin => }/tests/mocks/accesscontrol_panic_mock.cairo (100%) rename src/{openzeppelin => }/tests/mocks/account_panic_mock.cairo (100%) rename src/{openzeppelin => }/tests/mocks/camel20_mock.cairo (100%) rename src/{openzeppelin => }/tests/mocks/camel721_mock.cairo (100%) rename src/{openzeppelin => }/tests/mocks/camel_accesscontrol_mock.cairo (100%) rename src/{openzeppelin => }/tests/mocks/camel_account_mock.cairo (100%) rename src/{openzeppelin => }/tests/mocks/dual721_receiver_mocks.cairo (100%) rename src/{openzeppelin => }/tests/mocks/dual_ownable_mocks.cairo (100%) rename src/{openzeppelin => }/tests/mocks/erc20_panic.cairo (100%) rename src/{openzeppelin => }/tests/mocks/erc721_panic_mock.cairo (100%) rename src/{openzeppelin => }/tests/mocks/erc721_receiver.cairo (100%) rename src/{openzeppelin => }/tests/mocks/non_implementing_mock.cairo (100%) rename src/{openzeppelin => }/tests/mocks/reentrancy_attacker_mock.cairo (100%) rename src/{openzeppelin => }/tests/mocks/reentrancy_mock.cairo (95%) rename src/{openzeppelin => }/tests/mocks/snake20_mock.cairo (100%) rename src/{openzeppelin => }/tests/mocks/snake721_mock.cairo (100%) rename src/{openzeppelin => }/tests/mocks/snake_accesscontrol_mock.cairo (100%) rename src/{openzeppelin => }/tests/mocks/snake_account_mock.cairo (100%) rename src/{openzeppelin => }/tests/mocks/src5_mocks.cairo (100%) rename src/{openzeppelin => }/tests/security.cairo (100%) rename src/{openzeppelin => }/tests/security/test_initializable.cairo (100%) rename src/{openzeppelin => }/tests/security/test_pausable.cairo (100%) rename src/{openzeppelin => }/tests/security/test_reentrancyguard.cairo (100%) rename src/{openzeppelin => }/tests/token.cairo (100%) rename src/{openzeppelin => }/tests/token/test_dual20.cairo (100%) rename src/{openzeppelin => }/tests/token/test_dual721.cairo (98%) rename src/{openzeppelin => }/tests/token/test_dual721_receiver.cairo (89%) rename src/{openzeppelin => }/tests/token/test_erc20.cairo (100%) rename src/{openzeppelin => }/tests/token/test_erc721.cairo (100%) rename src/{openzeppelin => }/tests/utils.cairo (100%) rename src/{openzeppelin => }/token.cairo (100%) rename src/{openzeppelin => }/token/erc20.cairo (100%) rename src/{openzeppelin => }/token/erc20/dual20.cairo (100%) rename src/{openzeppelin => }/token/erc20/erc20.cairo (100%) rename src/{openzeppelin => }/token/erc20/interface.cairo (100%) rename src/{openzeppelin => }/token/erc721.cairo (100%) rename src/{openzeppelin => }/token/erc721/dual721.cairo (100%) rename src/{openzeppelin => }/token/erc721/dual721_receiver.cairo (100%) rename src/{openzeppelin => }/token/erc721/erc721.cairo (98%) rename src/{openzeppelin => }/token/erc721/interface.cairo (100%) rename src/{openzeppelin => }/utils.cairo (100%) rename src/{openzeppelin => }/utils/constants.cairo (100%) rename src/{openzeppelin => }/utils/selectors.cairo (100%) rename src/{openzeppelin => }/utils/serde.cairo (100%) rename src/{openzeppelin => }/utils/unwrap_and_cast.cairo (100%) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fa705adfa..0a9921611 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,10 +3,10 @@ name: Lint and test on: pull_request: branches: - - cairo-1 + - cairo-2 push: branches: - - cairo-1 + - cairo-2 jobs: lint_and_test: @@ -14,8 +14,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + - uses: software-mansion/setup-scarb@v1 with: - submodules: recursive + scarb-version: "0.6.0-alpha.1" - name: Markdown lint uses: DavidAnson/markdownlint-cli2-action@5b7c9f74fec47e6b15667b2cc23c63dff11e449e # v9 with: @@ -23,6 +24,6 @@ jobs: *.md !PULL_REQUEST_TEMPLATE.md - name: Cairo lint - run: make check-format + run: scarb fmt --check - name: Cairo test - run: make test + run: scarb test diff --git a/.gitignore b/.gitignore index 73cda39c1..869665ba4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,11 @@ -artifacts/ .DS_Store # Cairo 1 - corelib/ +# Scarb +target/ + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -78,9 +79,6 @@ instance/ # Sphinx documentation docs/_build/ -# PyBuilder -target/ - # Jupyter Notebook .ipynb_checkpoints diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3c8aca19d..49e9b88d0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,7 +22,7 @@ func internal_func(param_one, param_two){ } ``` -Compare our preset contracts with the libraries from which they're derived such as the [ERC20 preset](./src/openzeppelin/token/erc20/presets/ERC20.cairo) and [ERC20 library](./src/openzeppelin/token/erc20/presets/ERC20.cairo) for full examples. +Compare our preset contracts with the libraries from which they're derived such as the [ERC20 preset](src/token/erc20/presets/ERC20.cairo) and [ERC20 library](src/token/erc20/presets/ERC20.cairo) for full examples. See [Function names and coding style](https://docs.openzeppelin.com/contracts-cairo/0.4.0/extensibility#function_names_and_coding_style) for more information. And make sure to always include tests and documentation for the new developments. Please consider the following conventions: @@ -54,7 +54,7 @@ And make sure to always include tests and documentation for the new developments ``` - Preset contract testing - - Though, inheritance is not possible in Cairo, this repo utilizes inheritance for testing. This proves useful for testing multiple contracts that stem from the same base library. For example, the preset contracts [ERC20Mintable](./src/openzeppelin/token/erc20/presets/ERC20Mintable.cairo) and [ERC20Burnable](./src/openzeppelin/token/erc20/presets/ERC20Burnable.cairo) both share the base ERC20 functionality. To reduce code repetition, we follow these guidelines: + - Though, inheritance is not possible in Cairo, this repo utilizes inheritance for testing. This proves useful for testing multiple contracts that stem from the same base library. For example, the preset contracts [ERC20Mintable](src/token/erc20/presets/ERC20Mintable.cairo) and [ERC20Burnable](src/token/erc20/presets/ERC20Burnable.cairo) both share the base ERC20 functionality. To reduce code repetition, we follow these guidelines: - `BaseSuites` - module names are not prefixed with `test_` - set base tests inside a class diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 278c09912..000000000 --- a/Cargo.lock +++ /dev/null @@ -1,3623 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - -[[package]] -name = "ahash" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", -] - -[[package]] -name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - -[[package]] -name = "aho-corasick" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" - -[[package]] -name = "anyhow" -version = "1.0.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" - -[[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 0.10.5", - "num-traits 0.2.15", - "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", - "itertools 0.10.5", - "num-bigint", - "num-traits 0.2.15", - "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.103", -] - -[[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 0.2.15", - "proc-macro2", - "quote", - "syn 1.0.103", -] - -[[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-secp256k1" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c02e954eaeb4ddb29613fee20840c2bbc85ca4396d53e33837e11905363c5f2" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-std", -] - -[[package]] -name = "ark-secp256r1" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3975a01b0a6e3eae0f72ec7ca8598a6620fc72fa5981f6f5cca33b7cd788f633" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-std", -] - -[[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", - "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.103", -] - -[[package]] -name = "ark-std" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" -dependencies = [ - "num-traits 0.2.15", - "rand", -] - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - -[[package]] -name = "ascii-canvas" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" -dependencies = [ - "term", -] - -[[package]] -name = "assert_matches" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" - -[[package]] -name = "async-trait" -version = "0.1.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.103", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "auto_impl" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.103", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bimap" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0455254eb5c6964c4545d8bac815e1a1be4f3afe0ae695ea539c12d728d44b" - -[[package]] -name = "bincode" -version = "2.0.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11ea1a0346b94ef188834a65c068a03aec181c94896d481d7a0a40d85b0ce95" -dependencies = [ - "serde", -] - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "block-buffer" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bstr" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45ea9b00a7b3f2988e9a65ad3917e62123c38dba709b666506207be96d1790b" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" - -[[package]] -name = "byte-slice-cast" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" - -[[package]] -name = "bytes" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" - -[[package]] -name = "cairo-compile" -version = "2.0.2" -dependencies = [ - "anyhow", - "cairo-lang-compiler", - "cairo-lang-utils", - "clap", - "log", -] - -[[package]] -name = "cairo-felt" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f8de851723a7d13ed8b0b588a78ffa6b38d8e1f3eb4b6e71a96376510e5504a" -dependencies = [ - "lazy_static", - "num-bigint", - "num-integer", - "num-traits 0.2.15", - "serde", -] - -[[package]] -name = "cairo-format" -version = "2.0.2" -dependencies = [ - "anyhow", - "cairo-lang-formatter", - "cairo-lang-utils", - "clap", - "colored", - "ignore", - "log", -] - -[[package]] -name = "cairo-lang-casm" -version = "2.0.2" -dependencies = [ - "cairo-lang-utils", - "env_logger", - "indoc", - "itertools 0.11.0", - "num-bigint", - "num-traits 0.2.15", - "parity-scale-codec", - "parity-scale-codec-derive", - "pretty_assertions", - "schemars", - "serde", - "test-case", - "test-log", - "thiserror", -] - -[[package]] -name = "cairo-lang-compiler" -version = "2.0.2" -dependencies = [ - "anyhow", - "cairo-lang-defs", - "cairo-lang-diagnostics", - "cairo-lang-filesystem", - "cairo-lang-lowering", - "cairo-lang-parser", - "cairo-lang-plugins", - "cairo-lang-project", - "cairo-lang-semantic", - "cairo-lang-sierra", - "cairo-lang-sierra-generator", - "cairo-lang-syntax", - "cairo-lang-utils", - "log", - "salsa", - "smol_str", - "test-log", - "thiserror", -] - -[[package]] -name = "cairo-lang-debug" -version = "2.0.2" -dependencies = [ - "cairo-lang-proc-macros", - "cairo-lang-utils", - "env_logger", - "salsa", - "test-log", -] - -[[package]] -name = "cairo-lang-defs" -version = "2.0.2" -dependencies = [ - "cairo-lang-debug", - "cairo-lang-diagnostics", - "cairo-lang-filesystem", - "cairo-lang-parser", - "cairo-lang-syntax", - "cairo-lang-test-utils", - "cairo-lang-utils", - "env_logger", - "indexmap 2.0.0", - "indoc", - "itertools 0.11.0", - "pretty_assertions", - "salsa", - "smol_str", - "test-log", -] - -[[package]] -name = "cairo-lang-diagnostics" -version = "2.0.2" -dependencies = [ - "cairo-lang-debug", - "cairo-lang-filesystem", - "cairo-lang-proc-macros", - "cairo-lang-utils", - "env_logger", - "indoc", - "itertools 0.11.0", - "pretty_assertions", - "salsa", - "test-log", -] - -[[package]] -name = "cairo-lang-eq-solver" -version = "2.0.2" -dependencies = [ - "cairo-lang-utils", - "env_logger", - "good_lp", - "indexmap 2.0.0", - "indoc", - "itertools 0.11.0", - "test-case", - "test-log", -] - -[[package]] -name = "cairo-lang-filesystem" -version = "2.0.2" -dependencies = [ - "cairo-lang-debug", - "cairo-lang-utils", - "env_logger", - "path-clean", - "salsa", - "serde", - "serde_json", - "smol_str", - "test-log", -] - -[[package]] -name = "cairo-lang-formatter" -version = "2.0.2" -dependencies = [ - "anyhow", - "cairo-lang-diagnostics", - "cairo-lang-filesystem", - "cairo-lang-parser", - "cairo-lang-syntax", - "cairo-lang-utils", - "colored", - "diffy", - "ignore", - "itertools 0.11.0", - "log", - "pretty_assertions", - "salsa", - "smol_str", - "test-case", - "test-log", -] - -[[package]] -name = "cairo-lang-language-server" -version = "2.0.2" -dependencies = [ - "anyhow", - "cairo-lang-compiler", - "cairo-lang-debug", - "cairo-lang-defs", - "cairo-lang-diagnostics", - "cairo-lang-filesystem", - "cairo-lang-formatter", - "cairo-lang-lowering", - "cairo-lang-parser", - "cairo-lang-plugins", - "cairo-lang-project", - "cairo-lang-semantic", - "cairo-lang-starknet", - "cairo-lang-syntax", - "cairo-lang-utils", - "indoc", - "log", - "lsp-types", - "salsa", - "scarb-metadata", - "serde", - "serde_json", - "smol_str", - "test-log", - "tokio", - "tower-lsp", -] - -[[package]] -name = "cairo-lang-lowering" -version = "2.0.2" -dependencies = [ - "cairo-lang-debug", - "cairo-lang-defs", - "cairo-lang-diagnostics", - "cairo-lang-filesystem", - "cairo-lang-parser", - "cairo-lang-plugins", - "cairo-lang-proc-macros", - "cairo-lang-semantic", - "cairo-lang-syntax", - "cairo-lang-test-utils", - "cairo-lang-utils", - "env_logger", - "id-arena", - "indexmap 2.0.0", - "indoc", - "itertools 0.11.0", - "log", - "num-bigint", - "num-traits 0.2.15", - "pretty_assertions", - "salsa", - "smol_str", - "test-log", -] - -[[package]] -name = "cairo-lang-parser" -version = "2.0.2" -dependencies = [ - "cairo-lang-diagnostics", - "cairo-lang-filesystem", - "cairo-lang-syntax", - "cairo-lang-syntax-codegen", - "cairo-lang-test-utils", - "cairo-lang-utils", - "colored", - "env_logger", - "itertools 0.11.0", - "log", - "num-bigint", - "num-traits 0.2.15", - "pretty_assertions", - "salsa", - "smol_str", - "test-case", - "test-log", - "unescaper", -] - -[[package]] -name = "cairo-lang-plugins" -version = "2.0.2" -dependencies = [ - "cairo-lang-debug", - "cairo-lang-defs", - "cairo-lang-diagnostics", - "cairo-lang-filesystem", - "cairo-lang-parser", - "cairo-lang-semantic", - "cairo-lang-syntax", - "cairo-lang-test-utils", - "cairo-lang-utils", - "env_logger", - "indoc", - "itertools 0.11.0", - "num-bigint", - "salsa", - "serde_json", - "smol_str", - "test-case", - "test-log", -] - -[[package]] -name = "cairo-lang-proc-macros" -version = "2.0.2" -dependencies = [ - "cairo-lang-debug", - "quote", - "syn 2.0.26", -] - -[[package]] -name = "cairo-lang-project" -version = "2.0.2" -dependencies = [ - "cairo-lang-filesystem", - "cairo-lang-utils", - "indoc", - "serde", - "smol_str", - "test-log", - "thiserror", - "toml", -] - -[[package]] -name = "cairo-lang-runner" -version = "2.0.2" -dependencies = [ - "anyhow", - "ark-ff", - "ark-secp256k1", - "ark-secp256r1", - "ark-std", - "cairo-felt", - "cairo-lang-casm", - "cairo-lang-compiler", - "cairo-lang-defs", - "cairo-lang-diagnostics", - "cairo-lang-filesystem", - "cairo-lang-lowering", - "cairo-lang-semantic", - "cairo-lang-sierra", - "cairo-lang-sierra-ap-change", - "cairo-lang-sierra-gas", - "cairo-lang-sierra-generator", - "cairo-lang-sierra-to-casm", - "cairo-lang-sierra-type-size", - "cairo-lang-starknet", - "cairo-lang-utils", - "cairo-vm", - "itertools 0.11.0", - "keccak", - "num-bigint", - "num-integer", - "num-traits 0.2.15", - "pretty_assertions", - "salsa", - "test-case", - "thiserror", -] - -[[package]] -name = "cairo-lang-semantic" -version = "2.0.2" -dependencies = [ - "assert_matches", - "cairo-lang-debug", - "cairo-lang-defs", - "cairo-lang-diagnostics", - "cairo-lang-filesystem", - "cairo-lang-parser", - "cairo-lang-plugins", - "cairo-lang-proc-macros", - "cairo-lang-syntax", - "cairo-lang-test-utils", - "cairo-lang-utils", - "env_logger", - "id-arena", - "indoc", - "itertools 0.11.0", - "log", - "num-bigint", - "num-traits 0.2.15", - "pretty_assertions", - "salsa", - "smol_str", - "test-case", - "test-log", -] - -[[package]] -name = "cairo-lang-sierra" -version = "2.0.2" -dependencies = [ - "assert_matches", - "bimap", - "cairo-lang-utils", - "const-fnv1a-hash", - "convert_case", - "derivative", - "env_logger", - "indoc", - "itertools 0.11.0", - "lalrpop", - "lalrpop-util", - "num-bigint", - "num-traits 0.2.15", - "pretty_assertions", - "regex", - "salsa", - "serde", - "sha3", - "smol_str", - "test-case", - "test-log", - "thiserror", -] - -[[package]] -name = "cairo-lang-sierra-ap-change" -version = "2.0.2" -dependencies = [ - "cairo-lang-eq-solver", - "cairo-lang-sierra", - "cairo-lang-sierra-type-size", - "cairo-lang-utils", - "env_logger", - "indoc", - "itertools 0.11.0", - "test-case", - "test-log", - "thiserror", -] - -[[package]] -name = "cairo-lang-sierra-gas" -version = "2.0.2" -dependencies = [ - "cairo-lang-eq-solver", - "cairo-lang-sierra", - "cairo-lang-sierra-type-size", - "cairo-lang-test-utils", - "cairo-lang-utils", - "env_logger", - "indoc", - "itertools 0.11.0", - "pretty_assertions", - "test-case", - "test-log", - "thiserror", -] - -[[package]] -name = "cairo-lang-sierra-generator" -version = "2.0.2" -dependencies = [ - "cairo-lang-debug", - "cairo-lang-defs", - "cairo-lang-diagnostics", - "cairo-lang-filesystem", - "cairo-lang-lowering", - "cairo-lang-parser", - "cairo-lang-plugins", - "cairo-lang-proc-macros", - "cairo-lang-semantic", - "cairo-lang-sierra", - "cairo-lang-syntax", - "cairo-lang-test-utils", - "cairo-lang-utils", - "env_logger", - "id-arena", - "indexmap 2.0.0", - "indoc", - "itertools 0.11.0", - "log", - "num-bigint", - "pretty_assertions", - "salsa", - "smol_str", - "test-case", - "test-log", -] - -[[package]] -name = "cairo-lang-sierra-to-casm" -version = "2.0.2" -dependencies = [ - "assert_matches", - "cairo-felt", - "cairo-lang-casm", - "cairo-lang-sierra", - "cairo-lang-sierra-ap-change", - "cairo-lang-sierra-gas", - "cairo-lang-sierra-type-size", - "cairo-lang-utils", - "env_logger", - "indoc", - "itertools 0.11.0", - "log", - "num-bigint", - "num-traits 0.2.15", - "pretty_assertions", - "test-case", - "test-log", - "thiserror", -] - -[[package]] -name = "cairo-lang-sierra-type-size" -version = "2.0.2" -dependencies = [ - "cairo-lang-sierra", - "cairo-lang-utils", -] - -[[package]] -name = "cairo-lang-starknet" -version = "2.0.2" -dependencies = [ - "anyhow", - "cairo-felt", - "cairo-lang-casm", - "cairo-lang-compiler", - "cairo-lang-defs", - "cairo-lang-diagnostics", - "cairo-lang-filesystem", - "cairo-lang-lowering", - "cairo-lang-parser", - "cairo-lang-plugins", - "cairo-lang-semantic", - "cairo-lang-sierra", - "cairo-lang-sierra-ap-change", - "cairo-lang-sierra-gas", - "cairo-lang-sierra-generator", - "cairo-lang-sierra-to-casm", - "cairo-lang-syntax", - "cairo-lang-test-utils", - "cairo-lang-utils", - "convert_case", - "env_logger", - "genco", - "indent", - "indoc", - "itertools 0.11.0", - "log", - "num-bigint", - "num-integer", - "num-traits 0.2.15", - "once_cell", - "pretty_assertions", - "serde", - "serde_json", - "sha3", - "smol_str", - "test-case", - "test-log", - "thiserror", -] - -[[package]] -name = "cairo-lang-syntax" -version = "2.0.2" -dependencies = [ - "cairo-lang-debug", - "cairo-lang-filesystem", - "cairo-lang-utils", - "env_logger", - "num-bigint", - "num-traits 0.2.15", - "pretty_assertions", - "salsa", - "smol_str", - "test-log", - "thiserror", - "unescaper", -] - -[[package]] -name = "cairo-lang-syntax-codegen" -version = "2.0.2" -dependencies = [ - "env_logger", - "genco", - "test-log", - "xshell", -] - -[[package]] -name = "cairo-lang-test-runner" -version = "2.0.2" -dependencies = [ - "anyhow", - "cairo-felt", - "cairo-lang-casm", - "cairo-lang-compiler", - "cairo-lang-debug", - "cairo-lang-defs", - "cairo-lang-diagnostics", - "cairo-lang-filesystem", - "cairo-lang-lowering", - "cairo-lang-plugins", - "cairo-lang-project", - "cairo-lang-runner", - "cairo-lang-semantic", - "cairo-lang-sierra", - "cairo-lang-sierra-generator", - "cairo-lang-sierra-to-casm", - "cairo-lang-starknet", - "cairo-lang-syntax", - "cairo-lang-utils", - "cairo-vm", - "colored", - "itertools 0.11.0", - "num-bigint", - "num-traits 0.2.15", - "rayon", - "salsa", - "thiserror", -] - -[[package]] -name = "cairo-lang-test-utils" -version = "2.0.2" -dependencies = [ - "cairo-lang-utils", - "env_logger", - "log", - "pretty_assertions", - "test-log", -] - -[[package]] -name = "cairo-lang-utils" -version = "2.0.2" -dependencies = [ - "env_logger", - "indexmap 2.0.0", - "itertools 0.11.0", - "log", - "num-bigint", - "num-integer", - "num-traits 0.2.15", - "parity-scale-codec", - "schemars", - "serde", - "serde_json", - "test-case", - "test-log", - "time", -] - -[[package]] -name = "cairo-language-server" -version = "2.0.2" -dependencies = [ - "cairo-lang-language-server", - "cairo-lang-utils", - "log", - "tokio", -] - -[[package]] -name = "cairo-run" -version = "2.0.2" -dependencies = [ - "anyhow", - "cairo-lang-compiler", - "cairo-lang-diagnostics", - "cairo-lang-runner", - "cairo-lang-sierra", - "cairo-lang-sierra-generator", - "cairo-lang-starknet", - "cairo-lang-utils", - "clap", -] - -[[package]] -name = "cairo-test" -version = "2.0.2" -dependencies = [ - "anyhow", - "cairo-lang-compiler", - "cairo-lang-test-runner", - "clap", -] - -[[package]] -name = "cairo-vm" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "656c25c13b6ffcc75e081292f3c23f27e429b3d4b8d69ecdc327a441798e91f4" -dependencies = [ - "anyhow", - "bincode", - "bitvec", - "cairo-felt", - "generic-array", - "hashbrown 0.14.0", - "hex", - "keccak", - "lazy_static", - "mimalloc", - "nom", - "num-bigint", - "num-integer", - "num-prime", - "num-traits 0.2.15", - "rand", - "serde", - "serde_json", - "sha2", - "sha3", - "starknet-crypto", - "thiserror-no-std", -] - -[[package]] -name = "camino" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2" -dependencies = [ - "serde", -] - -[[package]] -name = "cc" -version = "1.0.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "clap" -version = "4.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2148adefda54e14492fb9bddcc600b4344c5d1a3123bd666dcb939c6f0e0e57e" -dependencies = [ - "atty", - "bitflags", - "clap_derive", - "clap_lex", - "once_cell", - "strsim", - "termcolor", -] - -[[package]] -name = "clap_derive" -version = "4.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" -dependencies = [ - "heck 0.4.0", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.103", -] - -[[package]] -name = "clap_lex" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "colored" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" -dependencies = [ - "atty", - "lazy_static", - "winapi", -] - -[[package]] -name = "const-fnv1a-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b13ea120a812beba79e34316b3942a857c86ec1593cb34f27bb28272ce2cca" - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "cpufeatures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" -dependencies = [ - "libc", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" -dependencies = [ - "generic-array", - "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.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" -dependencies = [ - "quote", - "syn 1.0.103", -] - -[[package]] -name = "dashmap" -version = "5.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" -dependencies = [ - "cfg-if", - "hashbrown 0.12.3", - "lock_api", - "once_cell", - "parking_lot_core 0.9.4", -] - -[[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.103", -] - -[[package]] -name = "diff" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" - -[[package]] -name = "diffy" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e616e59155c92257e84970156f506287853355f58cd4a6eb167385722c32b790" -dependencies = [ - "nu-ansi-term", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", - "subtle", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dyn-clone" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" - -[[package]] -name = "either" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" - -[[package]] -name = "ena" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" -dependencies = [ - "log", -] - -[[package]] -name = "env_logger" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" -dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" - -[[package]] -name = "futures-executor" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" - -[[package]] -name = "futures-macro" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.103", -] - -[[package]] -name = "futures-sink" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" - -[[package]] -name = "futures-task" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" - -[[package]] -name = "futures-timer" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" - -[[package]] -name = "futures-util" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "genco" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8598ff0782dbc5231cf9eb727c1c5e398515b7b62ee68761c2c73950a1de1f4" -dependencies = [ - "genco-macros", - "relative-path", - "smallvec", -] - -[[package]] -name = "genco-macros" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40803f2757f84c877f088e62420931f6e05a72514f1f03630384aa30b91d667b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.103", -] - -[[package]] -name = "generate-syntax" -version = "2.0.2" -dependencies = [ - "cairo-lang-syntax-codegen", - "cairo-lang-utils", - "log", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "globset" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" -dependencies = [ - "aho-corasick 0.7.20", - "bstr", - "fnv", - "log", - "regex", -] - -[[package]] -name = "good_lp" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b51d78cbb7b734379eea7f811ddb33b2b13defefa1dab50068d7bc7f781a3056" -dependencies = [ - "fnv", - "minilp", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.6", -] - -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash 0.8.3", -] - -[[package]] -name = "hashbrown" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" -dependencies = [ - "ahash 0.8.3", - "allocator-api2", - "serde", -] - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "heck" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "id-arena" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" - -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "ignore" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" -dependencies = [ - "globset", - "lazy_static", - "log", - "memchr", - "regex", - "same-file", - "thread_local", - "walkdir", - "winapi-util", -] - -[[package]] -name = "impl-trait-for-tuples" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.103", -] - -[[package]] -name = "indent" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f1a0777d972970f204fdf8ef319f1f4f8459131636d7e3c96c5d59570d0fa6" - -[[package]] -name = "indexmap" -version = "1.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" -dependencies = [ - "equivalent", - "hashbrown 0.14.0", - "serde", -] - -[[package]] -name = "indoc" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f2cb48b81b1dc9f39676bf99f5499babfec7cd8fe14307f7b3d747208fb5690" - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.45.0", -] - -[[package]] -name = "is-terminal" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" -dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys 0.45.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" - -[[package]] -name = "js-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "keccak" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "lalrpop" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da4081d44f4611b66c6dd725e6de3169f9f63905421e8626fcb86b6a898998b8" -dependencies = [ - "ascii-canvas", - "bit-set", - "diff", - "ena", - "is-terminal", - "itertools 0.10.5", - "lalrpop-util", - "petgraph", - "pico-args", - "regex", - "regex-syntax", - "string_cache", - "term", - "tiny-keccak", - "unicode-xid", -] - -[[package]] -name = "lalrpop-util" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f35c735096c0293d313e8f2a641627472b83d01b937177fe76e5e2708d31e0d" -dependencies = [ - "regex", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -dependencies = [ - "spin", -] - -[[package]] -name = "libc" -version = "0.2.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" - -[[package]] -name = "libmimalloc-sys" -version = "0.1.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ac0e912c8ef1b735e92369695618dc5b1819f5a7bf3f167301a3ba1cea515e" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "linux-raw-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" - -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "lru" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" -dependencies = [ - "hashbrown 0.12.3", -] - -[[package]] -name = "lsp-types" -version = "0.94.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b63735a13a1f9cd4f4835223d828ed9c2e35c8c5e61837774399f558b6a1237" -dependencies = [ - "bitflags", - "serde", - "serde_json", - "serde_repr", - "url", -] - -[[package]] -name = "matrixmultiply" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "916806ba0031cd542105d916a97c8572e1fa6dd79c9c51e7eb43a09ec2dd84c1" -dependencies = [ - "rawpointer", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] - -[[package]] -name = "mimalloc" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2894987a3459f3ffb755608bd82188f8ed00d0ae077f1edea29c068d639d98" -dependencies = [ - "libmimalloc-sys", -] - -[[package]] -name = "minilp" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a7750a9e5076c660b7bec5e6457b4dbff402b9863c8d112891434e18fd5385" -dependencies = [ - "log", - "sprs", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "mio" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.42.0", -] - -[[package]] -name = "ndarray" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac06db03ec2f46ee0ecdca1a1c34a99c0d188a0d83439b84bf0cb4b386e4ab09" -dependencies = [ - "matrixmultiply", - "num-complex", - "num-integer", - "num-traits 0.2.15", - "rawpointer", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" - -[[package]] -name = "nom" -version = "7.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-bigint" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits 0.2.15", - "rand", - "serde", -] - -[[package]] -name = "num-complex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" -dependencies = [ - "autocfg", - "num-traits 0.2.15", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits 0.2.15", -] - -[[package]] -name = "num-modular" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a5fe11d4135c3bcdf3a95b18b194afa9608a5f6ff034f5d857bc9a27fb0119" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits 0.2.15", -] - -[[package]] -name = "num-prime" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4e3bc495f6e95bc15a6c0c55ac00421504a5a43d09e3cc455d1fea7015581d" -dependencies = [ - "bitvec", - "either", - "lru", - "num-bigint", - "num-integer", - "num-modular", - "num-traits 0.2.15", - "rand", -] - -[[package]] -name = "num-traits" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" -dependencies = [ - "num-traits 0.2.15", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" -dependencies = [ - "hermit-abi 0.1.19", - "libc", -] - -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - -[[package]] -name = "once_cell" -version = "1.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" - -[[package]] -name = "oorandom" -version = "11.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" - -[[package]] -name = "os_str_bytes" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" - -[[package]] -name = "output_vt100" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" -dependencies = [ - "winapi", -] - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "parity-scale-codec" -version = "3.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756d439303e94fae44f288ba881ad29670c65b0c4b0e05674ca81061bb65f2c5" -dependencies = [ - "arrayvec", - "bitvec", - "byte-slice-cast", - "impl-trait-for-tuples", - "serde", -] - -[[package]] -name = "parity-scale-codec-derive" -version = "3.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d884d78fcf214d70b1e239fcd1c6e5e95aa3be1881918da2e488cc946c7a476" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.103", -] - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.5", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core 0.9.4", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall", - "smallvec", - "winapi", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-sys 0.42.0", -] - -[[package]] -name = "paste" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" - -[[package]] -name = "path-clean" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef" - -[[package]] -name = "percent-encoding" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" - -[[package]] -name = "petgraph" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" -dependencies = [ - "fixedbitset", - "indexmap 1.9.2", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pico-args" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" - -[[package]] -name = "pin-project" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.103", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "pretty_assertions" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" -dependencies = [ - "ctor", - "diff", - "output_vt100", - "yansi", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.103", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[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", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rawpointer" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" - -[[package]] -name = "rayon" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_users" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" -dependencies = [ - "getrandom", - "redox_syscall", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" -dependencies = [ - "aho-corasick 1.0.2", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" -dependencies = [ - "aho-corasick 1.0.2", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" - -[[package]] -name = "relative-path" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf2521270932c3c7bed1a59151222bd7643c79310f2916f01925e1e16255698" - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "rstest" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b96577ca10cb3eade7b337eb46520108a67ca2818a24d0b63f41fd62bc9651c" -dependencies = [ - "futures", - "futures-timer", - "rstest_macros", - "rustc_version", -] - -[[package]] -name = "rstest_macros" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225e674cf31712b8bb15fdbca3ec0c1b9d825c5a24407ff2b7e005fb6a29ba03" -dependencies = [ - "cfg-if", - "glob", - "proc-macro2", - "quote", - "regex", - "relative-path", - "rustc_version", - "syn 2.0.26", - "unicode-ident", -] - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.36.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys 0.45.0", -] - -[[package]] -name = "rustversion" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" - -[[package]] -name = "ryu" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" - -[[package]] -name = "salsa" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b84d9f96071f3f3be0dc818eae3327625d8ebc95b58da37d6850724f31d3403" -dependencies = [ - "crossbeam-utils", - "indexmap 1.9.2", - "lock_api", - "log", - "oorandom", - "parking_lot 0.11.2", - "rustc-hash", - "salsa-macros", - "smallvec", -] - -[[package]] -name = "salsa-macros" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3904a4ba0a9d0211816177fd34b04c7095443f8cdacd11175064fe541c8fe2" -dependencies = [ - "heck 0.3.3", - "proc-macro2", - "quote", - "syn 1.0.103", -] - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scarb-metadata" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26ba10c59bd4ebde82de954e3a3d6d503eb17bd634106ef144af1356b28b8f0f" -dependencies = [ - "camino", - "semver", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "schemars" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" -dependencies = [ - "dyn-clone", - "indexmap 1.9.2", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 1.0.103", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "semver" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" -dependencies = [ - "serde", -] - -[[package]] -name = "serde" -version = "1.0.171" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.171" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.26", -] - -[[package]] -name = "serde_derive_internals" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.103", -] - -[[package]] -name = "serde_json" -version = "1.0.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_repr" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.103", -] - -[[package]] -name = "serde_spanned" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" -dependencies = [ - "serde", -] - -[[package]] -name = "sha2" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest", - "keccak", -] - -[[package]] -name = "sierra-compile" -version = "2.0.2" -dependencies = [ - "anyhow", - "cairo-lang-sierra", - "cairo-lang-sierra-to-casm", - "cairo-lang-utils", - "clap", - "indoc", - "log", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" -dependencies = [ - "libc", -] - -[[package]] -name = "siphasher" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" - -[[package]] -name = "slab" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "smol_str" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" -dependencies = [ - "serde", -] - -[[package]] -name = "socket2" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "sprs" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec63571489873d4506683915840eeb1bb16b3198ee4894cc6f2fe3013d505e56" -dependencies = [ - "ndarray", - "num-complex", - "num-traits 0.1.43", -] - -[[package]] -name = "starknet-compile" -version = "2.0.2" -dependencies = [ - "anyhow", - "cairo-lang-compiler", - "cairo-lang-starknet", - "clap", -] - -[[package]] -name = "starknet-crypto" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693e6362f150f9276e429a910481fb7f3bcb8d6aa643743f587cfece0b374874" -dependencies = [ - "crypto-bigint", - "hex", - "hmac", - "num-bigint", - "num-integer", - "num-traits 0.2.15", - "rfc6979", - "sha2", - "starknet-crypto-codegen", - "starknet-curve 0.3.0", - "starknet-ff", - "zeroize", -] - -[[package]] -name = "starknet-crypto-codegen" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af6527b845423542c8a16e060ea1bc43f67229848e7cd4c4d80be994a84220ce" -dependencies = [ - "starknet-curve 0.4.0", - "starknet-ff", - "syn 2.0.26", -] - -[[package]] -name = "starknet-curve" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "252610baff59e4c4332ce3569f7469c5d3f9b415a2240d698fb238b2b4fc0942" -dependencies = [ - "starknet-ff", -] - -[[package]] -name = "starknet-curve" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68a0d87ae56572abf83ddbfd44259a7c90dbeeee1629a1ffe223e7f9a8f3052" -dependencies = [ - "starknet-ff", -] - -[[package]] -name = "starknet-ff" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2cb1d9c0a50380cddab99cb202c6bfb3332728a2769bd0ca2ee80b0b390dd4" -dependencies = [ - "ark-ff", - "crypto-bigint", - "getrandom", - "hex", -] - -[[package]] -name = "starknet-sierra-compile" -version = "2.0.2" -dependencies = [ - "anyhow", - "cairo-lang-sierra", - "cairo-lang-starknet", - "cairo-lang-utils", - "clap", - "serde", - "serde_json", -] - -[[package]] -name = "string_cache" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213494b7a2b503146286049378ce02b482200519accc31872ee8be91fa820a08" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot 0.12.1", - "phf_shared", - "precomputed-hash", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "syn" -version = "1.0.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.103", - "unicode-xid", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "term" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" -dependencies = [ - "dirs-next", - "rustversion", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "test-case" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a1d6e7bde536b0412f20765b76e921028059adfd1b90d8974d33fd3c91b25df" -dependencies = [ - "test-case-macros", -] - -[[package]] -name = "test-case-core" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d10394d5d1e27794f772b6fc854c7e91a2dc26e2cbf807ad523370c2a59c0cee" -dependencies = [ - "cfg-if", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.103", -] - -[[package]] -name = "test-case-macros" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeb9a44b1c6a54c1ba58b152797739dba2a83ca74e18168a68c980eb142f9404" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.103", - "test-case-core", -] - -[[package]] -name = "test-log" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f0c854faeb68a048f0f2dc410c5ddae3bf83854ef0e4977d58306a5edef50e" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.103", -] - -[[package]] -name = "tests" -version = "2.0.2" -dependencies = [ - "assert_matches", - "cairo-felt", - "cairo-lang-casm", - "cairo-lang-compiler", - "cairo-lang-defs", - "cairo-lang-diagnostics", - "cairo-lang-filesystem", - "cairo-lang-lowering", - "cairo-lang-parser", - "cairo-lang-plugins", - "cairo-lang-runner", - "cairo-lang-semantic", - "cairo-lang-sierra", - "cairo-lang-sierra-gas", - "cairo-lang-sierra-generator", - "cairo-lang-sierra-to-casm", - "cairo-lang-syntax", - "cairo-lang-test-utils", - "cairo-lang-utils", - "env_logger", - "itertools 0.11.0", - "log", - "num-bigint", - "once_cell", - "pretty_assertions", - "rstest", - "salsa", - "test-case", - "test-log", -] - -[[package]] -name = "thiserror" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.26", -] - -[[package]] -name = "thiserror-impl-no-std" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58e6318948b519ba6dc2b442a6d0b904ebfb8d411a3ad3e07843615a72249758" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.103", -] - -[[package]] -name = "thiserror-no-std" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3ad459d94dd517257cc96add8a43190ee620011bb6e6cdc82dafd97dfafafea" -dependencies = [ - "thiserror-impl-no-std", -] - -[[package]] -name = "thread_local" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" -dependencies = [ - "once_cell", -] - -[[package]] -name = "time" -version = "0.3.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" -dependencies = [ - "itoa", - "libc", - "num_threads", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" - -[[package]] -name = "time-macros" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" -dependencies = [ - "time-core", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - -[[package]] -name = "tokio" -version = "1.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3" -dependencies = [ - "autocfg", - "bytes", - "libc", - "memchr", - "mio", - "num_cpus", - "parking_lot 0.12.1", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "winapi", -] - -[[package]] -name = "tokio-macros" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.103", -] - -[[package]] -name = "tokio-util" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.19.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" -dependencies = [ - "indexmap 2.0.0", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "pin-project", - "pin-project-lite", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" - -[[package]] -name = "tower-lsp" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b38fb0e6ce037835174256518aace3ca621c4f96383c56bb846cfc11b341910" -dependencies = [ - "async-trait", - "auto_impl", - "bytes", - "dashmap", - "futures", - "httparse", - "lsp-types", - "memchr", - "serde", - "serde_json", - "tokio", - "tokio-util", - "tower", - "tower-lsp-macros", - "tracing", -] - -[[package]] -name = "tower-lsp-macros" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34723c06344244474fdde365b76aebef8050bf6be61a935b91ee9ff7c4e91157" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.103", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.26", -] - -[[package]] -name = "tracing-core" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "typenum" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" - -[[package]] -name = "unescaper" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "995483205de764db1185c9461a000fff73fa4b9ee2bbe4c8b4027a94692700fe" -dependencies = [ - "thiserror", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" - -[[package]] -name = "unicode-ident" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "url" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "walkdir" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" -dependencies = [ - "same-file", - "winapi", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 1.0.103", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.103", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "winnow" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" -dependencies = [ - "memchr", -] - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "xshell" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d47097dc5c85234b1e41851b3422dd6d19b3befdd35b4ae5ce386724aeca981" -dependencies = [ - "xshell-macros", -] - -[[package]] -name = "xshell-macros" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88301b56c26dd9bf5c43d858538f82d6f3f7764767defbc5d34e59459901c41a" - -[[package]] -name = "yansi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" - -[[package]] -name = "zeroize" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.103", - "synstructure", -] diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 753694083..000000000 --- a/Cargo.toml +++ /dev/null @@ -1,115 +0,0 @@ -[profile.release] -overflow-checks = true # Enable integer overflow checks. - -[workspace] - -members = [ - "cairo/crates/cairo-lang-casm", - "cairo/crates/cairo-lang-compiler", - "cairo/crates/cairo-lang-debug", - "cairo/crates/cairo-lang-defs", - "cairo/crates/cairo-lang-diagnostics", - "cairo/crates/cairo-lang-eq-solver", - "cairo/crates/cairo-lang-filesystem", - "cairo/crates/cairo-lang-formatter", - "cairo/crates/cairo-lang-language-server", - "cairo/crates/cairo-lang-lowering", - "cairo/crates/cairo-lang-parser", - "cairo/crates/cairo-lang-plugins", - "cairo/crates/cairo-lang-proc-macros", - "cairo/crates/cairo-lang-project", - "cairo/crates/cairo-lang-runner", - "cairo/crates/cairo-lang-semantic", - "cairo/crates/cairo-lang-sierra", - "cairo/crates/cairo-lang-sierra-ap-change", - "cairo/crates/cairo-lang-sierra-gas", - "cairo/crates/cairo-lang-sierra-generator", - "cairo/crates/cairo-lang-sierra-to-casm", - "cairo/crates/cairo-lang-sierra-type-size", - "cairo/crates/cairo-lang-starknet", - "cairo/crates/cairo-lang-syntax", - "cairo/crates/cairo-lang-syntax-codegen", - "cairo/crates/cairo-lang-test-runner", - "cairo/crates/cairo-lang-utils", - "cairo/crates/bin/cairo-language-server", - "cairo/crates/bin/cairo-compile", - "cairo/crates/bin/cairo-format", - "cairo/crates/bin/cairo-test", - "cairo/crates/bin/cairo-run", - "cairo/crates/bin/sierra-compile", - "cairo/crates/bin/starknet-compile", - "cairo/crates/bin/starknet-sierra-compile", - "cairo/crates/bin/generate-syntax", - "cairo/tests", -] - -[workspace.package] -version = "2.0.2" -edition = "2021" -repository = "https://github.com/starkware-libs/cairo/" -license = "Apache-2.0" -license-file = "LICENSE" - -[workspace.dependencies] -anyhow = "1.0.66" -ark-ff = "0.4.0-alpha.7" -ark-secp256k1 = "0.4.0" -ark-secp256r1 = "0.4.0" -ark-std = "0.4.0" -assert_matches = "1.5" -bimap = "0.6.2" -cairo-felt = "0.8.2" -cairo-vm = "0.8.2" -clap = { version = "4.0", features = ["derive"] } -colored = "2" -const-fnv1a-hash = "1.1.0" -convert_case = "0.6.0" -derivative = "2.2.0" -diffy = "0.3.0" -env_logger = "0.10.0" -genco = "0.17.0" -good_lp = { version = "1.3.2", features = ["minilp"], default-features = false } -id-arena = "2.2.1" -ignore = "0.4.20" -indent = "0.1.1" -indexmap = { version = "2.0.0", features = ["serde"] } -indoc = "2.0.1" -itertools = "0.11.0" -keccak = "0.1.3" -lalrpop-util = { version = "0.20.0", features = ["lexer"] } -log = "0.4" -lsp = { version = "0.94", package = "lsp-types" } -num-bigint = "0.4" -num-integer = "0.1" -num-traits = "0.2" -once_cell = "1.17.1" -parity-scale-codec = "3.5.0" -parity-scale-codec-derive = "3.1.4" -path-clean = "1.0.1" -pretty_assertions = "1.2.1" -proc-macro2 = "1.0" -quote = "1.0.21" -rayon = "1.7.0" -rstest = "0.18.1" -salsa = "0.16.1" -scarb-metadata = "1.4.2" -schemars = { version = "0.8.12", features = ["preserve_order"] } -serde = { version = "1.0.130", features = ["derive"] } -serde_json = "1.0" -sha3 = "0.10.6" -smol_str = { version = "0.2.0", features = ["serde"] } -syn = { version = "2.0.25", features = ["full", "extra-traits"] } -test-case = "3.1.0" -test-case-macros = "2.2.2" -test-log = "0.2.11" -thiserror = "1.0.32" -time = { version = "0.3.20", features = [ - "formatting", - "macros", - "local-offset", -] } -tokio = { version = "1.18.2", features = ["full", "sync"] } -toml = "0.7.6" -tower-lsp = "0.19.0" -unescaper = "0.1.1" -xshell = "0.2.2" diff --git a/Makefile b/Makefile deleted file mode 100644 index 0ed75752e..000000000 --- a/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -.SILENT: -.PHONY: compile -SOURCE_FOLDER=./src/openzeppelin - -install: - git submodule init && git submodule update && cp -rf cairo/corelib . - -update: - git submodule update && cp -rf cairo/corelib . - -build: - cargo build - -test: - cargo run --bin cairo-test -- --starknet $(SOURCE_FOLDER) - -format: - cargo run --bin cairo-format -- --recursive $(SOURCE_FOLDER) --print-parsing-errors - -check-format: - cargo run --bin cairo-format -- --check --recursive $(SOURCE_FOLDER) - -starknet-compile: - mkdir -p artifacts && \ - cargo run --bin starknet-compile -- ${dir} artifacts/$(shell basename $(dir)).json --allowed-libfuncs-list-name experimental_v0.1.0 - -language-server: - cargo build --bin cairo-language-server --release diff --git a/Scarb.toml b/Scarb.toml new file mode 100644 index 000000000..e36e29c60 --- /dev/null +++ b/Scarb.toml @@ -0,0 +1,21 @@ +[package] +name = "openzeppelin" +version = "0.1.0" +cairo-version = "2.1.0-rc1" +authors = ["OpenZeppelin Community "] +description = "OpenZeppelin Contracts written in Cairo for StarkNet, a decentralized ZK Rollup" +documentation = "https://docs.openzeppelin.com/contracts-cairo" +readme = "README.md" +repository = "https://github.com/OpenZeppelin/cairo-contracts" +license-file = "LICENSE" +keywords = ["openzeppelin", "starknet", "cairo", "contracts", "security", "standards"] + +[dependencies] +starknet = ">=2.1.0-rc0" + +[lib] + +[[target.starknet-contract]] +allowed-libfuncs-list.name = "experimental" +sierra = true +casm = false diff --git a/cairo b/cairo deleted file mode 160000 index 06722bd86..000000000 --- a/cairo +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 06722bd868b675500f61e87125cc5db519592e63 diff --git a/src/openzeppelin/access.cairo b/src/access.cairo similarity index 100% rename from src/openzeppelin/access.cairo rename to src/access.cairo diff --git a/src/openzeppelin/access/accesscontrol.cairo b/src/access/accesscontrol.cairo similarity index 100% rename from src/openzeppelin/access/accesscontrol.cairo rename to src/access/accesscontrol.cairo diff --git a/src/openzeppelin/access/accesscontrol/accesscontrol.cairo b/src/access/accesscontrol/accesscontrol.cairo similarity index 100% rename from src/openzeppelin/access/accesscontrol/accesscontrol.cairo rename to src/access/accesscontrol/accesscontrol.cairo diff --git a/src/openzeppelin/access/accesscontrol/dual_accesscontrol.cairo b/src/access/accesscontrol/dual_accesscontrol.cairo similarity index 100% rename from src/openzeppelin/access/accesscontrol/dual_accesscontrol.cairo rename to src/access/accesscontrol/dual_accesscontrol.cairo diff --git a/src/openzeppelin/access/accesscontrol/interface.cairo b/src/access/accesscontrol/interface.cairo similarity index 100% rename from src/openzeppelin/access/accesscontrol/interface.cairo rename to src/access/accesscontrol/interface.cairo diff --git a/src/openzeppelin/access/ownable.cairo b/src/access/ownable.cairo similarity index 100% rename from src/openzeppelin/access/ownable.cairo rename to src/access/ownable.cairo diff --git a/src/openzeppelin/access/ownable/dual_ownable.cairo b/src/access/ownable/dual_ownable.cairo similarity index 100% rename from src/openzeppelin/access/ownable/dual_ownable.cairo rename to src/access/ownable/dual_ownable.cairo diff --git a/src/openzeppelin/access/ownable/interface.cairo b/src/access/ownable/interface.cairo similarity index 100% rename from src/openzeppelin/access/ownable/interface.cairo rename to src/access/ownable/interface.cairo diff --git a/src/openzeppelin/access/ownable/ownable.cairo b/src/access/ownable/ownable.cairo similarity index 100% rename from src/openzeppelin/access/ownable/ownable.cairo rename to src/access/ownable/ownable.cairo diff --git a/src/openzeppelin/account.cairo b/src/account.cairo similarity index 100% rename from src/openzeppelin/account.cairo rename to src/account.cairo diff --git a/src/openzeppelin/account/account.cairo b/src/account/account.cairo similarity index 100% rename from src/openzeppelin/account/account.cairo rename to src/account/account.cairo diff --git a/src/openzeppelin/account/dual_account.cairo b/src/account/dual_account.cairo similarity index 100% rename from src/openzeppelin/account/dual_account.cairo rename to src/account/dual_account.cairo diff --git a/src/openzeppelin/account/interface.cairo b/src/account/interface.cairo similarity index 100% rename from src/openzeppelin/account/interface.cairo rename to src/account/interface.cairo diff --git a/src/openzeppelin/cairo_project.toml b/src/cairo_project.toml similarity index 100% rename from src/openzeppelin/cairo_project.toml rename to src/cairo_project.toml diff --git a/src/openzeppelin/introspection.cairo b/src/introspection.cairo similarity index 100% rename from src/openzeppelin/introspection.cairo rename to src/introspection.cairo diff --git a/src/openzeppelin/introspection/dual_src5.cairo b/src/introspection/dual_src5.cairo similarity index 100% rename from src/openzeppelin/introspection/dual_src5.cairo rename to src/introspection/dual_src5.cairo diff --git a/src/openzeppelin/introspection/interface.cairo b/src/introspection/interface.cairo similarity index 100% rename from src/openzeppelin/introspection/interface.cairo rename to src/introspection/interface.cairo diff --git a/src/openzeppelin/introspection/src5.cairo b/src/introspection/src5.cairo similarity index 100% rename from src/openzeppelin/introspection/src5.cairo rename to src/introspection/src5.cairo diff --git a/src/openzeppelin/lib.cairo b/src/lib.cairo similarity index 86% rename from src/openzeppelin/lib.cairo rename to src/lib.cairo index e03376525..235e12392 100644 --- a/src/openzeppelin/lib.cairo +++ b/src/lib.cairo @@ -2,6 +2,8 @@ mod access; mod account; mod introspection; mod security; -mod tests; mod token; mod utils; + +#[cfg(test)] +mod tests; diff --git a/src/openzeppelin/security.cairo b/src/security.cairo similarity index 100% rename from src/openzeppelin/security.cairo rename to src/security.cairo diff --git a/src/openzeppelin/security/initializable.cairo b/src/security/initializable.cairo similarity index 100% rename from src/openzeppelin/security/initializable.cairo rename to src/security/initializable.cairo diff --git a/src/openzeppelin/security/pausable.cairo b/src/security/pausable.cairo similarity index 100% rename from src/openzeppelin/security/pausable.cairo rename to src/security/pausable.cairo diff --git a/src/openzeppelin/security/reentrancyguard.cairo b/src/security/reentrancyguard.cairo similarity index 100% rename from src/openzeppelin/security/reentrancyguard.cairo rename to src/security/reentrancyguard.cairo diff --git a/src/openzeppelin/tests.cairo b/src/tests.cairo similarity index 100% rename from src/openzeppelin/tests.cairo rename to src/tests.cairo diff --git a/src/openzeppelin/tests/access.cairo b/src/tests/access.cairo similarity index 100% rename from src/openzeppelin/tests/access.cairo rename to src/tests/access.cairo diff --git a/src/openzeppelin/tests/access/test_accesscontrol.cairo b/src/tests/access/test_accesscontrol.cairo similarity index 100% rename from src/openzeppelin/tests/access/test_accesscontrol.cairo rename to src/tests/access/test_accesscontrol.cairo diff --git a/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo b/src/tests/access/test_dual_accesscontrol.cairo similarity index 95% rename from src/openzeppelin/tests/access/test_dual_accesscontrol.cairo rename to src/tests/access/test_dual_accesscontrol.cairo index 7fc3b5049..fb3c916d5 100644 --- a/src/openzeppelin/tests/access/test_dual_accesscontrol.cairo +++ b/src/tests/access/test_dual_accesscontrol.cairo @@ -42,8 +42,11 @@ fn setup_snake() -> (DualCaseAccessControl, IAccessControlDispatcher) { calldata.append_serde(ADMIN()); let target = utils::deploy(SnakeAccessControlMock::TEST_CLASS_HASH, calldata); ( - DualCaseAccessControl { contract_address: target }, - IAccessControlDispatcher { contract_address: target } + DualCaseAccessControl { + contract_address: target + }, IAccessControlDispatcher { + contract_address: target + } ) } @@ -52,8 +55,11 @@ fn setup_camel() -> (DualCaseAccessControl, IAccessControlCamelDispatcher) { calldata.append_serde(ADMIN()); let target = utils::deploy(CamelAccessControlMock::TEST_CLASS_HASH, calldata); ( - DualCaseAccessControl { contract_address: target }, - IAccessControlCamelDispatcher { contract_address: target } + DualCaseAccessControl { + contract_address: target + }, IAccessControlCamelDispatcher { + contract_address: target + } ) } @@ -66,8 +72,11 @@ fn setup_accesscontrol_panic() -> (DualCaseAccessControl, DualCaseAccessControl) let snake_target = utils::deploy(SnakeAccessControlPanicMock::TEST_CLASS_HASH, array![]); let camel_target = utils::deploy(CamelAccessControlPanicMock::TEST_CLASS_HASH, array![]); ( - DualCaseAccessControl { contract_address: snake_target }, - DualCaseAccessControl { contract_address: camel_target } + DualCaseAccessControl { + contract_address: snake_target + }, DualCaseAccessControl { + contract_address: camel_target + } ) } diff --git a/src/openzeppelin/tests/access/test_dual_ownable.cairo b/src/tests/access/test_dual_ownable.cairo similarity index 94% rename from src/openzeppelin/tests/access/test_dual_ownable.cairo rename to src/tests/access/test_dual_ownable.cairo index 672df30ce..470709dc6 100644 --- a/src/openzeppelin/tests/access/test_dual_ownable.cairo +++ b/src/tests/access/test_dual_ownable.cairo @@ -46,8 +46,11 @@ fn setup_camel() -> (DualCaseOwnable, IOwnableCamelOnlyDispatcher) { calldata.append_serde(OWNER()); let target = utils::deploy(CamelOwnableMock::TEST_CLASS_HASH, calldata); ( - DualCaseOwnable { contract_address: target }, - IOwnableCamelOnlyDispatcher { contract_address: target } + DualCaseOwnable { + contract_address: target + }, IOwnableCamelOnlyDispatcher { + contract_address: target + } ) } @@ -61,8 +64,11 @@ fn setup_ownable_panic() -> (DualCaseOwnable, DualCaseOwnable) { let snake_target = utils::deploy(SnakeOwnablePanicMock::TEST_CLASS_HASH, ArrayTrait::new()); let camel_target = utils::deploy(CamelOwnablePanicMock::TEST_CLASS_HASH, ArrayTrait::new()); ( - DualCaseOwnable { contract_address: snake_target }, - DualCaseOwnable { contract_address: camel_target } + DualCaseOwnable { + contract_address: snake_target + }, DualCaseOwnable { + contract_address: camel_target + } ) } diff --git a/src/openzeppelin/tests/access/test_ownable.cairo b/src/tests/access/test_ownable.cairo similarity index 100% rename from src/openzeppelin/tests/access/test_ownable.cairo rename to src/tests/access/test_ownable.cairo diff --git a/src/openzeppelin/tests/account.cairo b/src/tests/account.cairo similarity index 100% rename from src/openzeppelin/tests/account.cairo rename to src/tests/account.cairo diff --git a/src/openzeppelin/tests/account/test_account.cairo b/src/tests/account/test_account.cairo similarity index 100% rename from src/openzeppelin/tests/account/test_account.cairo rename to src/tests/account/test_account.cairo diff --git a/src/openzeppelin/tests/account/test_dual_account.cairo b/src/tests/account/test_dual_account.cairo similarity index 94% rename from src/openzeppelin/tests/account/test_dual_account.cairo rename to src/tests/account/test_dual_account.cairo index 76ea9fb99..c1178b22a 100644 --- a/src/openzeppelin/tests/account/test_dual_account.cairo +++ b/src/tests/account/test_dual_account.cairo @@ -30,8 +30,11 @@ fn setup_snake() -> (DualCaseAccount, AccountABIDispatcher) { let mut calldata = array![PUBLIC_KEY]; let target = utils::deploy(SnakeAccountMock::TEST_CLASS_HASH, calldata); ( - DualCaseAccount { contract_address: target }, - AccountABIDispatcher { contract_address: target } + DualCaseAccount { + contract_address: target + }, AccountABIDispatcher { + contract_address: target + } ) } @@ -39,8 +42,11 @@ fn setup_camel() -> (DualCaseAccount, AccountCamelABIDispatcher) { let mut calldata = array![PUBLIC_KEY]; let target = utils::deploy(CamelAccountMock::TEST_CLASS_HASH, calldata); ( - DualCaseAccount { contract_address: target }, - AccountCamelABIDispatcher { contract_address: target } + DualCaseAccount { + contract_address: target + }, AccountCamelABIDispatcher { + contract_address: target + } ) } @@ -54,8 +60,11 @@ fn setup_account_panic() -> (DualCaseAccount, DualCaseAccount) { let snake_target = utils::deploy(SnakeAccountPanicMock::TEST_CLASS_HASH, array![]); let camel_target = utils::deploy(CamelAccountPanicMock::TEST_CLASS_HASH, array![]); ( - DualCaseAccount { contract_address: snake_target }, - DualCaseAccount { contract_address: camel_target } + DualCaseAccount { + contract_address: snake_target + }, DualCaseAccount { + contract_address: camel_target + } ) } diff --git a/src/openzeppelin/tests/introspection.cairo b/src/tests/introspection.cairo similarity index 100% rename from src/openzeppelin/tests/introspection.cairo rename to src/tests/introspection.cairo diff --git a/src/openzeppelin/tests/introspection/test_dual_src5.cairo b/src/tests/introspection/test_dual_src5.cairo similarity index 94% rename from src/openzeppelin/tests/introspection/test_dual_src5.cairo rename to src/tests/introspection/test_dual_src5.cairo index 4e47773cc..629cd4977 100644 --- a/src/openzeppelin/tests/introspection/test_dual_src5.cairo +++ b/src/tests/introspection/test_dual_src5.cairo @@ -39,8 +39,11 @@ fn setup_src5_panic() -> (DualCaseSRC5, DualCaseSRC5) { let snake_target = utils::deploy(SnakeSRC5PanicMock::TEST_CLASS_HASH, array![]); let camel_target = utils::deploy(CamelSRC5PanicMock::TEST_CLASS_HASH, array![]); ( - DualCaseSRC5 { contract_address: snake_target }, - DualCaseSRC5 { contract_address: camel_target } + DualCaseSRC5 { + contract_address: snake_target + }, DualCaseSRC5 { + contract_address: camel_target + } ) } diff --git a/src/openzeppelin/tests/introspection/test_src5.cairo b/src/tests/introspection/test_src5.cairo similarity index 100% rename from src/openzeppelin/tests/introspection/test_src5.cairo rename to src/tests/introspection/test_src5.cairo diff --git a/src/openzeppelin/tests/mocks.cairo b/src/tests/mocks.cairo similarity index 100% rename from src/openzeppelin/tests/mocks.cairo rename to src/tests/mocks.cairo diff --git a/src/openzeppelin/tests/mocks/accesscontrol_panic_mock.cairo b/src/tests/mocks/accesscontrol_panic_mock.cairo similarity index 100% rename from src/openzeppelin/tests/mocks/accesscontrol_panic_mock.cairo rename to src/tests/mocks/accesscontrol_panic_mock.cairo diff --git a/src/openzeppelin/tests/mocks/account_panic_mock.cairo b/src/tests/mocks/account_panic_mock.cairo similarity index 100% rename from src/openzeppelin/tests/mocks/account_panic_mock.cairo rename to src/tests/mocks/account_panic_mock.cairo diff --git a/src/openzeppelin/tests/mocks/camel20_mock.cairo b/src/tests/mocks/camel20_mock.cairo similarity index 100% rename from src/openzeppelin/tests/mocks/camel20_mock.cairo rename to src/tests/mocks/camel20_mock.cairo diff --git a/src/openzeppelin/tests/mocks/camel721_mock.cairo b/src/tests/mocks/camel721_mock.cairo similarity index 100% rename from src/openzeppelin/tests/mocks/camel721_mock.cairo rename to src/tests/mocks/camel721_mock.cairo diff --git a/src/openzeppelin/tests/mocks/camel_accesscontrol_mock.cairo b/src/tests/mocks/camel_accesscontrol_mock.cairo similarity index 100% rename from src/openzeppelin/tests/mocks/camel_accesscontrol_mock.cairo rename to src/tests/mocks/camel_accesscontrol_mock.cairo diff --git a/src/openzeppelin/tests/mocks/camel_account_mock.cairo b/src/tests/mocks/camel_account_mock.cairo similarity index 100% rename from src/openzeppelin/tests/mocks/camel_account_mock.cairo rename to src/tests/mocks/camel_account_mock.cairo diff --git a/src/openzeppelin/tests/mocks/dual721_receiver_mocks.cairo b/src/tests/mocks/dual721_receiver_mocks.cairo similarity index 100% rename from src/openzeppelin/tests/mocks/dual721_receiver_mocks.cairo rename to src/tests/mocks/dual721_receiver_mocks.cairo diff --git a/src/openzeppelin/tests/mocks/dual_ownable_mocks.cairo b/src/tests/mocks/dual_ownable_mocks.cairo similarity index 100% rename from src/openzeppelin/tests/mocks/dual_ownable_mocks.cairo rename to src/tests/mocks/dual_ownable_mocks.cairo diff --git a/src/openzeppelin/tests/mocks/erc20_panic.cairo b/src/tests/mocks/erc20_panic.cairo similarity index 100% rename from src/openzeppelin/tests/mocks/erc20_panic.cairo rename to src/tests/mocks/erc20_panic.cairo diff --git a/src/openzeppelin/tests/mocks/erc721_panic_mock.cairo b/src/tests/mocks/erc721_panic_mock.cairo similarity index 100% rename from src/openzeppelin/tests/mocks/erc721_panic_mock.cairo rename to src/tests/mocks/erc721_panic_mock.cairo diff --git a/src/openzeppelin/tests/mocks/erc721_receiver.cairo b/src/tests/mocks/erc721_receiver.cairo similarity index 100% rename from src/openzeppelin/tests/mocks/erc721_receiver.cairo rename to src/tests/mocks/erc721_receiver.cairo diff --git a/src/openzeppelin/tests/mocks/non_implementing_mock.cairo b/src/tests/mocks/non_implementing_mock.cairo similarity index 100% rename from src/openzeppelin/tests/mocks/non_implementing_mock.cairo rename to src/tests/mocks/non_implementing_mock.cairo diff --git a/src/openzeppelin/tests/mocks/reentrancy_attacker_mock.cairo b/src/tests/mocks/reentrancy_attacker_mock.cairo similarity index 100% rename from src/openzeppelin/tests/mocks/reentrancy_attacker_mock.cairo rename to src/tests/mocks/reentrancy_attacker_mock.cairo diff --git a/src/openzeppelin/tests/mocks/reentrancy_mock.cairo b/src/tests/mocks/reentrancy_mock.cairo similarity index 95% rename from src/openzeppelin/tests/mocks/reentrancy_mock.cairo rename to src/tests/mocks/reentrancy_mock.cairo index 0850954c5..718a7db39 100644 --- a/src/openzeppelin/tests/mocks/reentrancy_mock.cairo +++ b/src/tests/mocks/reentrancy_mock.cairo @@ -72,8 +72,9 @@ mod ReentrancyMock { if n != 0 { self.count(); let this: ContractAddress = get_contract_address(); - IReentrancyGuardedDispatcher { contract_address: this } - .count_external_recursive(n - 1) + IReentrancyGuardedDispatcher { + contract_address: this + }.count_external_recursive(n - 1) } ReentrancyGuard::InternalImpl::end(ref unsafe_state); diff --git a/src/openzeppelin/tests/mocks/snake20_mock.cairo b/src/tests/mocks/snake20_mock.cairo similarity index 100% rename from src/openzeppelin/tests/mocks/snake20_mock.cairo rename to src/tests/mocks/snake20_mock.cairo diff --git a/src/openzeppelin/tests/mocks/snake721_mock.cairo b/src/tests/mocks/snake721_mock.cairo similarity index 100% rename from src/openzeppelin/tests/mocks/snake721_mock.cairo rename to src/tests/mocks/snake721_mock.cairo diff --git a/src/openzeppelin/tests/mocks/snake_accesscontrol_mock.cairo b/src/tests/mocks/snake_accesscontrol_mock.cairo similarity index 100% rename from src/openzeppelin/tests/mocks/snake_accesscontrol_mock.cairo rename to src/tests/mocks/snake_accesscontrol_mock.cairo diff --git a/src/openzeppelin/tests/mocks/snake_account_mock.cairo b/src/tests/mocks/snake_account_mock.cairo similarity index 100% rename from src/openzeppelin/tests/mocks/snake_account_mock.cairo rename to src/tests/mocks/snake_account_mock.cairo diff --git a/src/openzeppelin/tests/mocks/src5_mocks.cairo b/src/tests/mocks/src5_mocks.cairo similarity index 100% rename from src/openzeppelin/tests/mocks/src5_mocks.cairo rename to src/tests/mocks/src5_mocks.cairo diff --git a/src/openzeppelin/tests/security.cairo b/src/tests/security.cairo similarity index 100% rename from src/openzeppelin/tests/security.cairo rename to src/tests/security.cairo diff --git a/src/openzeppelin/tests/security/test_initializable.cairo b/src/tests/security/test_initializable.cairo similarity index 100% rename from src/openzeppelin/tests/security/test_initializable.cairo rename to src/tests/security/test_initializable.cairo diff --git a/src/openzeppelin/tests/security/test_pausable.cairo b/src/tests/security/test_pausable.cairo similarity index 100% rename from src/openzeppelin/tests/security/test_pausable.cairo rename to src/tests/security/test_pausable.cairo diff --git a/src/openzeppelin/tests/security/test_reentrancyguard.cairo b/src/tests/security/test_reentrancyguard.cairo similarity index 100% rename from src/openzeppelin/tests/security/test_reentrancyguard.cairo rename to src/tests/security/test_reentrancyguard.cairo diff --git a/src/openzeppelin/tests/token.cairo b/src/tests/token.cairo similarity index 100% rename from src/openzeppelin/tests/token.cairo rename to src/tests/token.cairo diff --git a/src/openzeppelin/tests/token/test_dual20.cairo b/src/tests/token/test_dual20.cairo similarity index 100% rename from src/openzeppelin/tests/token/test_dual20.cairo rename to src/tests/token/test_dual20.cairo diff --git a/src/openzeppelin/tests/token/test_dual721.cairo b/src/tests/token/test_dual721.cairo similarity index 98% rename from src/openzeppelin/tests/token/test_dual721.cairo rename to src/tests/token/test_dual721.cairo index dfaa53bad..8ff48f036 100644 --- a/src/openzeppelin/tests/token/test_dual721.cairo +++ b/src/tests/token/test_dual721.cairo @@ -76,8 +76,11 @@ fn setup_camel() -> (DualCaseERC721, IERC721CamelOnlyDispatcher) { set_caller_address(OWNER()); let target = utils::deploy(CamelERC721Mock::TEST_CLASS_HASH, calldata); ( - DualCaseERC721 { contract_address: target }, - IERC721CamelOnlyDispatcher { contract_address: target } + DualCaseERC721 { + contract_address: target + }, IERC721CamelOnlyDispatcher { + contract_address: target + } ) } @@ -91,8 +94,11 @@ fn setup_erc721_panic() -> (DualCaseERC721, DualCaseERC721) { let snake_target = utils::deploy(SnakeERC721PanicMock::TEST_CLASS_HASH, array![]); let camel_target = utils::deploy(CamelERC721PanicMock::TEST_CLASS_HASH, array![]); ( - DualCaseERC721 { contract_address: snake_target }, - DualCaseERC721 { contract_address: camel_target } + DualCaseERC721 { + contract_address: snake_target + }, DualCaseERC721 { + contract_address: camel_target + } ) } diff --git a/src/openzeppelin/tests/token/test_dual721_receiver.cairo b/src/tests/token/test_dual721_receiver.cairo similarity index 89% rename from src/openzeppelin/tests/token/test_dual721_receiver.cairo rename to src/tests/token/test_dual721_receiver.cairo index a3197dddc..ef02380a9 100644 --- a/src/openzeppelin/tests/token/test_dual721_receiver.cairo +++ b/src/tests/token/test_dual721_receiver.cairo @@ -49,8 +49,11 @@ fn setup_snake() -> (DualCaseERC721Receiver, IERC721ReceiverDispatcher) { let mut calldata = ArrayTrait::new(); let target = utils::deploy(SnakeERC721ReceiverMock::TEST_CLASS_HASH, calldata); ( - DualCaseERC721Receiver { contract_address: target }, - IERC721ReceiverDispatcher { contract_address: target } + DualCaseERC721Receiver { + contract_address: target + }, IERC721ReceiverDispatcher { + contract_address: target + } ) } @@ -58,8 +61,11 @@ fn setup_camel() -> (DualCaseERC721Receiver, IERC721ReceiverCamelDispatcher) { let mut calldata = ArrayTrait::new(); let target = utils::deploy(CamelERC721ReceiverMock::TEST_CLASS_HASH, calldata); ( - DualCaseERC721Receiver { contract_address: target }, - IERC721ReceiverCamelDispatcher { contract_address: target } + DualCaseERC721Receiver { + contract_address: target + }, IERC721ReceiverCamelDispatcher { + contract_address: target + } ) } @@ -77,8 +83,11 @@ fn setup_erc721_receiver_panic() -> (DualCaseERC721Receiver, DualCaseERC721Recei CamelERC721ReceiverPanicMock::TEST_CLASS_HASH, ArrayTrait::new() ); ( - DualCaseERC721Receiver { contract_address: snake_target }, - DualCaseERC721Receiver { contract_address: camel_target } + DualCaseERC721Receiver { + contract_address: snake_target + }, DualCaseERC721Receiver { + contract_address: camel_target + } ) } diff --git a/src/openzeppelin/tests/token/test_erc20.cairo b/src/tests/token/test_erc20.cairo similarity index 100% rename from src/openzeppelin/tests/token/test_erc20.cairo rename to src/tests/token/test_erc20.cairo diff --git a/src/openzeppelin/tests/token/test_erc721.cairo b/src/tests/token/test_erc721.cairo similarity index 100% rename from src/openzeppelin/tests/token/test_erc721.cairo rename to src/tests/token/test_erc721.cairo diff --git a/src/openzeppelin/tests/utils.cairo b/src/tests/utils.cairo similarity index 100% rename from src/openzeppelin/tests/utils.cairo rename to src/tests/utils.cairo diff --git a/src/openzeppelin/token.cairo b/src/token.cairo similarity index 100% rename from src/openzeppelin/token.cairo rename to src/token.cairo diff --git a/src/openzeppelin/token/erc20.cairo b/src/token/erc20.cairo similarity index 100% rename from src/openzeppelin/token/erc20.cairo rename to src/token/erc20.cairo diff --git a/src/openzeppelin/token/erc20/dual20.cairo b/src/token/erc20/dual20.cairo similarity index 100% rename from src/openzeppelin/token/erc20/dual20.cairo rename to src/token/erc20/dual20.cairo diff --git a/src/openzeppelin/token/erc20/erc20.cairo b/src/token/erc20/erc20.cairo similarity index 100% rename from src/openzeppelin/token/erc20/erc20.cairo rename to src/token/erc20/erc20.cairo diff --git a/src/openzeppelin/token/erc20/interface.cairo b/src/token/erc20/interface.cairo similarity index 100% rename from src/openzeppelin/token/erc20/interface.cairo rename to src/token/erc20/interface.cairo diff --git a/src/openzeppelin/token/erc721.cairo b/src/token/erc721.cairo similarity index 100% rename from src/openzeppelin/token/erc721.cairo rename to src/token/erc721.cairo diff --git a/src/openzeppelin/token/erc721/dual721.cairo b/src/token/erc721/dual721.cairo similarity index 100% rename from src/openzeppelin/token/erc721/dual721.cairo rename to src/token/erc721/dual721.cairo diff --git a/src/openzeppelin/token/erc721/dual721_receiver.cairo b/src/token/erc721/dual721_receiver.cairo similarity index 100% rename from src/openzeppelin/token/erc721/dual721_receiver.cairo rename to src/token/erc721/dual721_receiver.cairo diff --git a/src/openzeppelin/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo similarity index 98% rename from src/openzeppelin/token/erc721/erc721.cairo rename to src/token/erc721/erc721.cairo index 4e2a1bb80..c2429a676 100644 --- a/src/openzeppelin/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -342,9 +342,12 @@ mod ERC721 { fn _check_on_erc721_received( from: ContractAddress, to: ContractAddress, token_id: u256, data: Span ) -> bool { - if (DualCaseSRC5 { contract_address: to } - .supports_interface(interface::IERC721_RECEIVER_ID)) { - DualCaseERC721Receiver { contract_address: to } + if (DualCaseSRC5 { + contract_address: to + }.supports_interface(interface::IERC721_RECEIVER_ID)) { + DualCaseERC721Receiver { + contract_address: to + } .on_erc721_received( get_caller_address(), from, token_id, data ) == interface::IERC721_RECEIVER_ID diff --git a/src/openzeppelin/token/erc721/interface.cairo b/src/token/erc721/interface.cairo similarity index 100% rename from src/openzeppelin/token/erc721/interface.cairo rename to src/token/erc721/interface.cairo diff --git a/src/openzeppelin/utils.cairo b/src/utils.cairo similarity index 100% rename from src/openzeppelin/utils.cairo rename to src/utils.cairo diff --git a/src/openzeppelin/utils/constants.cairo b/src/utils/constants.cairo similarity index 100% rename from src/openzeppelin/utils/constants.cairo rename to src/utils/constants.cairo diff --git a/src/openzeppelin/utils/selectors.cairo b/src/utils/selectors.cairo similarity index 100% rename from src/openzeppelin/utils/selectors.cairo rename to src/utils/selectors.cairo diff --git a/src/openzeppelin/utils/serde.cairo b/src/utils/serde.cairo similarity index 100% rename from src/openzeppelin/utils/serde.cairo rename to src/utils/serde.cairo diff --git a/src/openzeppelin/utils/unwrap_and_cast.cairo b/src/utils/unwrap_and_cast.cairo similarity index 100% rename from src/openzeppelin/utils/unwrap_and_cast.cairo rename to src/utils/unwrap_and_cast.cairo From 177ad630dcf98d44428a9ddb8c46e3193a791018 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Fri, 4 Aug 2023 10:06:36 -0400 Subject: [PATCH 075/246] Add spdx (#684) * add spdx * add spdx --- src/access/accesscontrol/accesscontrol.cairo | 3 +++ src/access/accesscontrol/dual_accesscontrol.cairo | 3 +++ src/access/accesscontrol/interface.cairo | 3 +++ src/access/ownable/dual_ownable.cairo | 3 +++ src/access/ownable/interface.cairo | 3 +++ src/access/ownable/ownable.cairo | 3 +++ src/account/account.cairo | 3 +++ src/account/dual_account.cairo | 3 +++ src/account/interface.cairo | 3 +++ src/introspection/dual_src5.cairo | 3 +++ src/introspection/interface.cairo | 3 +++ src/introspection/src5.cairo | 3 +++ src/security/initializable.cairo | 3 +++ src/security/pausable.cairo | 3 +++ src/security/reentrancyguard.cairo | 3 +++ src/token/erc20/dual20.cairo | 3 +++ src/token/erc20/erc20.cairo | 3 +++ src/token/erc20/interface.cairo | 3 +++ src/token/erc721/dual721.cairo | 3 +++ src/token/erc721/dual721_receiver.cairo | 3 +++ src/token/erc721/erc721.cairo | 3 +++ src/token/erc721/interface.cairo | 3 +++ src/utils.cairo | 3 +++ src/utils/constants.cairo | 3 +++ src/utils/selectors.cairo | 3 +++ src/utils/serde.cairo | 3 +++ src/utils/unwrap_and_cast.cairo | 3 +++ 27 files changed, 81 insertions(+) diff --git a/src/access/accesscontrol/accesscontrol.cairo b/src/access/accesscontrol/accesscontrol.cairo index 78d245945..c1f85d985 100644 --- a/src/access/accesscontrol/accesscontrol.cairo +++ b/src/access/accesscontrol/accesscontrol.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (access/accesscontrol/accesscontrol.cairo) + #[starknet::contract] mod AccessControl { use starknet::ContractAddress; diff --git a/src/access/accesscontrol/dual_accesscontrol.cairo b/src/access/accesscontrol/dual_accesscontrol.cairo index 49367b2e5..711413022 100644 --- a/src/access/accesscontrol/dual_accesscontrol.cairo +++ b/src/access/accesscontrol/dual_accesscontrol.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (access/accesscontrol/dual_accesscontrol.cairo) + use array::ArrayTrait; use starknet::ContractAddress; use starknet::SyscallResultTrait; diff --git a/src/access/accesscontrol/interface.cairo b/src/access/accesscontrol/interface.cairo index 50adbddf7..1a2f0a59b 100644 --- a/src/access/accesscontrol/interface.cairo +++ b/src/access/accesscontrol/interface.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (access/accesscontrol/interface.cairo) + use starknet::ContractAddress; const IACCESSCONTROL_ID: felt252 = diff --git a/src/access/ownable/dual_ownable.cairo b/src/access/ownable/dual_ownable.cairo index 583d96ffe..ccff1582c 100644 --- a/src/access/ownable/dual_ownable.cairo +++ b/src/access/ownable/dual_ownable.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (access/ownable/dual_ownable.cairo) + use array::SpanTrait; use array::ArrayTrait; use core::result::ResultTrait; diff --git a/src/access/ownable/interface.cairo b/src/access/ownable/interface.cairo index 4641093dc..7047ffcf8 100644 --- a/src/access/ownable/interface.cairo +++ b/src/access/ownable/interface.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (access/ownable/interface.cairo) + use starknet::ContractAddress; #[starknet::interface] diff --git a/src/access/ownable/ownable.cairo b/src/access/ownable/ownable.cairo index a7670a100..b249d7528 100644 --- a/src/access/ownable/ownable.cairo +++ b/src/access/ownable/ownable.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (access/ownable/ownable.cairo) + #[starknet::contract] mod Ownable { use openzeppelin::access::ownable::interface; diff --git a/src/account/account.cairo b/src/account/account.cairo index 21b2d79d7..56204122e 100644 --- a/src/account/account.cairo +++ b/src/account/account.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (account/account.cairo) + use array::ArrayTrait; use array::SpanTrait; use option::OptionTrait; diff --git a/src/account/dual_account.cairo b/src/account/dual_account.cairo index ee4e1c0b6..a55600814 100644 --- a/src/account/dual_account.cairo +++ b/src/account/dual_account.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (account/dual_account.cairo) + use array::ArrayTrait; use array::SpanTrait; use starknet::ContractAddress; diff --git a/src/account/interface.cairo b/src/account/interface.cairo index e557b6dec..fdf4563a7 100644 --- a/src/account/interface.cairo +++ b/src/account/interface.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (account/interface.cairo) + use array::ArrayTrait; use array::SpanTrait; use starknet::account::Call; diff --git a/src/introspection/dual_src5.cairo b/src/introspection/dual_src5.cairo index ca6868cae..708bc9ecc 100644 --- a/src/introspection/dual_src5.cairo +++ b/src/introspection/dual_src5.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (introspection/dual_src5.cairo) + use array::ArrayTrait; use starknet::ContractAddress; diff --git a/src/introspection/interface.cairo b/src/introspection/interface.cairo index 68909245c..831071bce 100644 --- a/src/introspection/interface.cairo +++ b/src/introspection/interface.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (introspection/interface.cairo) + const ISRC5_ID: felt252 = 0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055; #[starknet::interface] diff --git a/src/introspection/src5.cairo b/src/introspection/src5.cairo index 2f9a9389e..75ad1d3b2 100644 --- a/src/introspection/src5.cairo +++ b/src/introspection/src5.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (introspection/src5.cairo) + #[starknet::contract] mod SRC5 { use openzeppelin::introspection::interface; diff --git a/src/security/initializable.cairo b/src/security/initializable.cairo index 1be5ab059..e0e7e658f 100644 --- a/src/security/initializable.cairo +++ b/src/security/initializable.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (security/initializable.cairo) + #[starknet::contract] mod Initializable { #[storage] diff --git a/src/security/pausable.cairo b/src/security/pausable.cairo index 66e159181..73480add7 100644 --- a/src/security/pausable.cairo +++ b/src/security/pausable.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (security/pausable.cairo) + #[starknet::interface] trait IPausable { fn is_paused(self: @TState) -> bool; diff --git a/src/security/reentrancyguard.cairo b/src/security/reentrancyguard.cairo index 17a2e8d9c..f08713776 100644 --- a/src/security/reentrancyguard.cairo +++ b/src/security/reentrancyguard.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (security/reentrancyguard.cairo) + #[starknet::contract] mod ReentrancyGuard { use starknet::get_caller_address; diff --git a/src/token/erc20/dual20.cairo b/src/token/erc20/dual20.cairo index 848f53363..1a867c0f7 100644 --- a/src/token/erc20/dual20.cairo +++ b/src/token/erc20/dual20.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (token/erc20/dual20.cairo) + use array::ArrayTrait; use starknet::call_contract_syscall; use starknet::ContractAddress; diff --git a/src/token/erc20/erc20.cairo b/src/token/erc20/erc20.cairo index f83a965a8..ebdc5af0f 100644 --- a/src/token/erc20/erc20.cairo +++ b/src/token/erc20/erc20.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (token/erc20/erc20.cairo) + #[starknet::contract] mod ERC20 { use integer::BoundedInt; diff --git a/src/token/erc20/interface.cairo b/src/token/erc20/interface.cairo index 8f86d6d75..8f0526299 100644 --- a/src/token/erc20/interface.cairo +++ b/src/token/erc20/interface.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (token/erc20/interface.cairo) + use starknet::ContractAddress; #[starknet::interface] diff --git a/src/token/erc721/dual721.cairo b/src/token/erc721/dual721.cairo index 55655d9c7..8d4ce8ffa 100644 --- a/src/token/erc721/dual721.cairo +++ b/src/token/erc721/dual721.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/dual721.cairo) + use array::ArrayTrait; use starknet::ContractAddress; use starknet::SyscallResultTrait; diff --git a/src/token/erc721/dual721_receiver.cairo b/src/token/erc721/dual721_receiver.cairo index 6c13fc931..55029bdd1 100644 --- a/src/token/erc721/dual721_receiver.cairo +++ b/src/token/erc721/dual721_receiver.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/dual721_receiver.cairo) + use array::ArrayTrait; use starknet::ContractAddress; use starknet::SyscallResultTrait; diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index c2429a676..0ea2598c7 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/erc721.cairo) + use starknet::ContractAddress; #[starknet::contract] diff --git a/src/token/erc721/interface.cairo b/src/token/erc721/interface.cairo index 5e22d6f07..44ff80a5e 100644 --- a/src/token/erc721/interface.cairo +++ b/src/token/erc721/interface.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/interface.cairo) + use starknet::ContractAddress; use array::SpanTrait; diff --git a/src/utils.cairo b/src/utils.cairo index e8d356c77..721dfdfee 100644 --- a/src/utils.cairo +++ b/src/utils.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (utils.cairo) + mod constants; mod selectors; mod serde; diff --git a/src/utils/constants.cairo b/src/utils/constants.cairo index 9d2414bac..0747ddb3c 100644 --- a/src/utils/constants.cairo +++ b/src/utils/constants.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (utils/constants.cairo) + // // Interface ids // diff --git a/src/utils/selectors.cairo b/src/utils/selectors.cairo index ee89772f9..1695e17cb 100644 --- a/src/utils/selectors.cairo +++ b/src/utils/selectors.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (utils/selectors.cairo) + // // AccessControl // diff --git a/src/utils/serde.cairo b/src/utils/serde.cairo index 1b3ea8cd8..23fdad21b 100644 --- a/src/utils/serde.cairo +++ b/src/utils/serde.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (utils/serde.cairo) + use serde::Serde; trait SerializedAppend { diff --git a/src/utils/unwrap_and_cast.cairo b/src/utils/unwrap_and_cast.cairo index 0e3959664..185242dc1 100644 --- a/src/utils/unwrap_and_cast.cairo +++ b/src/utils/unwrap_and_cast.cairo @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (utils/unwrap_and_cast.cairo) + use array::SpanTrait; use option::OptionTrait; use starknet::ContractAddress; From 495ed8a66cc6f3e6ca7eb8cb741ff4eba2265807 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 4 Aug 2023 17:43:46 +0200 Subject: [PATCH 076/246] feat: sort imports (#679) --- src/access/accesscontrol.cairo | 4 +- src/access/accesscontrol/accesscontrol.cairo | 4 +- .../accesscontrol/dual_accesscontrol.cairo | 6 +-- src/access/ownable.cairo | 6 +-- src/access/ownable/dual_ownable.cairo | 14 +++---- src/account.cairo | 6 +-- src/account/account.cairo | 14 +++---- src/account/dual_account.cairo | 7 ++-- src/account/interface.cairo | 2 +- src/introspection.cairo | 2 +- src/introspection/dual_src5.cairo | 5 +-- src/security.cairo | 4 +- src/tests/access.cairo | 2 +- src/tests/access/test_accesscontrol.cairo | 12 +++--- .../access/test_dual_accesscontrol.cairo | 21 +++++------ src/tests/access/test_dual_ownable.cairo | 23 ++++++------ src/tests/access/test_ownable.cairo | 10 ++--- src/tests/account/test_account.cairo | 15 ++++---- src/tests/account/test_dual_account.cairo | 11 +++--- src/tests/introspection.cairo | 2 +- src/tests/introspection/test_dual_src5.cairo | 16 ++++---- src/tests/introspection/test_src5.cairo | 6 +-- src/tests/mocks.cairo | 24 ++++++------ src/tests/mocks/camel20_mock.cairo | 2 +- src/tests/mocks/camel721_mock.cairo | 6 +-- .../mocks/camel_accesscontrol_mock.cairo | 6 +-- src/tests/mocks/dual721_receiver_mocks.cairo | 2 +- src/tests/mocks/erc721_receiver.cairo | 9 ++--- .../mocks/reentrancy_attacker_mock.cairo | 4 +- src/tests/mocks/reentrancy_mock.cairo | 4 +- src/tests/mocks/snake20_mock.cairo | 2 +- src/tests/mocks/snake721_mock.cairo | 6 +-- .../mocks/snake_accesscontrol_mock.cairo | 6 +-- src/tests/security.cairo | 2 +- src/tests/security/test_initializable.cairo | 2 +- src/tests/security/test_pausable.cairo | 4 +- src/tests/security/test_reentrancyguard.cairo | 6 +-- src/tests/token.cairo | 2 +- src/tests/token/test_dual20.cairo | 11 +++--- src/tests/token/test_dual721.cairo | 30 +++++++-------- src/tests/token/test_dual721_receiver.cairo | 26 ++++++------- src/tests/token/test_erc20.cairo | 9 ++--- src/tests/token/test_erc721.cairo | 37 +++++++++---------- src/tests/utils.cairo | 2 +- src/token/erc20.cairo | 2 +- src/token/erc20/dual20.cairo | 11 +++--- src/token/erc20/erc20.cairo | 5 +-- src/token/erc721/dual721.cairo | 8 ++-- src/token/erc721/dual721_receiver.cairo | 8 ++-- src/token/erc721/erc721.cairo | 9 ++--- src/token/erc721/interface.cairo | 2 +- src/utils.cairo | 2 +- src/utils/unwrap_and_cast.cairo | 4 +- 53 files changed, 216 insertions(+), 229 deletions(-) diff --git a/src/access/accesscontrol.cairo b/src/access/accesscontrol.cairo index 025142e68..6c2df16c2 100644 --- a/src/access/accesscontrol.cairo +++ b/src/access/accesscontrol.cairo @@ -1,7 +1,7 @@ mod accesscontrol; -use accesscontrol::AccessControl; - mod dual_accesscontrol; mod interface; +use accesscontrol::AccessControl; + const DEFAULT_ADMIN_ROLE: felt252 = 0; diff --git a/src/access/accesscontrol/accesscontrol.cairo b/src/access/accesscontrol/accesscontrol.cairo index c1f85d985..718137ecf 100644 --- a/src/access/accesscontrol/accesscontrol.cairo +++ b/src/access/accesscontrol/accesscontrol.cairo @@ -3,12 +3,12 @@ #[starknet::contract] mod AccessControl { - use starknet::ContractAddress; - use starknet::get_caller_address; use openzeppelin::access::accesscontrol::interface; use openzeppelin::introspection::interface::ISRC5; use openzeppelin::introspection::interface::ISRC5Camel; use openzeppelin::introspection::src5::SRC5; + use starknet::ContractAddress; + use starknet::get_caller_address; #[storage] struct Storage { diff --git a/src/access/accesscontrol/dual_accesscontrol.cairo b/src/access/accesscontrol/dual_accesscontrol.cairo index 711413022..eb086840e 100644 --- a/src/access/accesscontrol/dual_accesscontrol.cairo +++ b/src/access/accesscontrol/dual_accesscontrol.cairo @@ -2,14 +2,14 @@ // OpenZeppelin Contracts for Cairo v0.7.0 (access/accesscontrol/dual_accesscontrol.cairo) use array::ArrayTrait; -use starknet::ContractAddress; -use starknet::SyscallResultTrait; use openzeppelin::utils::Felt252TryIntoBool; +use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; use openzeppelin::utils::try_selector_with_fallback; -use openzeppelin::utils::UnwrapAndCast; +use starknet::ContractAddress; +use starknet::SyscallResultTrait; #[derive(Copy, Drop)] struct DualCaseAccessControl { diff --git a/src/access/ownable.cairo b/src/access/ownable.cairo index 7b8e812b9..4c70a8a9d 100644 --- a/src/access/ownable.cairo +++ b/src/access/ownable.cairo @@ -1,5 +1,5 @@ +mod dual_ownable; +mod interface; mod ownable; -use ownable::Ownable; -mod interface; -mod dual_ownable; +use ownable::Ownable; diff --git a/src/access/ownable/dual_ownable.cairo b/src/access/ownable/dual_ownable.cairo index ccff1582c..03d2ba51b 100644 --- a/src/access/ownable/dual_ownable.cairo +++ b/src/access/ownable/dual_ownable.cairo @@ -1,9 +1,15 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.7.0 (access/ownable/dual_ownable.cairo) -use array::SpanTrait; use array::ArrayTrait; +use array::SpanTrait; use core::result::ResultTrait; + +use openzeppelin::utils::Felt252TryIntoBool; +use openzeppelin::utils::UnwrapAndCast; +use openzeppelin::utils::selectors; +use openzeppelin::utils::serde::SerializedAppend; +use openzeppelin::utils::try_selector_with_fallback; use option::OptionTrait; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; @@ -11,12 +17,6 @@ use starknet::SyscallResultTrait; use starknet::call_contract_syscall; use traits::TryInto; -use openzeppelin::utils::Felt252TryIntoBool; -use openzeppelin::utils::selectors; -use openzeppelin::utils::serde::SerializedAppend; -use openzeppelin::utils::try_selector_with_fallback; -use openzeppelin::utils::UnwrapAndCast; - #[derive(Copy, Drop)] struct DualCaseOwnable { contract_address: ContractAddress diff --git a/src/account.cairo b/src/account.cairo index d8f6cab7a..1ca2c1578 100644 --- a/src/account.cairo +++ b/src/account.cairo @@ -3,9 +3,9 @@ mod dual_account; mod interface; use account::Account; -use account::TRANSACTION_VERSION; use account::QUERY_VERSION; -use interface::AccountCamelABIDispatcher; -use interface::AccountCamelABIDispatcherTrait; +use account::TRANSACTION_VERSION; use interface::AccountABIDispatcher; use interface::AccountABIDispatcherTrait; +use interface::AccountCamelABIDispatcher; +use interface::AccountCamelABIDispatcherTrait; diff --git a/src/account/account.cairo b/src/account/account.cairo index 56204122e..8d8105521 100644 --- a/src/account/account.cairo +++ b/src/account/account.cairo @@ -5,8 +5,8 @@ use array::ArrayTrait; use array::SpanTrait; use option::OptionTrait; use serde::Serde; -use starknet::account::Call; use starknet::ContractAddress; +use starknet::account::Call; const TRANSACTION_VERSION: felt252 = 1; @@ -25,24 +25,24 @@ trait PublicKeyCamelTrait { #[starknet::contract] mod Account { - use array::SpanTrait; use array::ArrayTrait; + use array::SpanTrait; use box::BoxTrait; use ecdsa::check_ecdsa_signature; - use option::OptionTrait; - use starknet::get_tx_info; - use starknet::get_caller_address; - use starknet::get_contract_address; - use zeroable::Zeroable; use openzeppelin::account::interface; use openzeppelin::introspection::interface::ISRC5; use openzeppelin::introspection::interface::ISRC5Camel; use openzeppelin::introspection::src5::SRC5; + use option::OptionTrait; + use starknet::get_caller_address; + use starknet::get_contract_address; + use starknet::get_tx_info; use super::Call; use super::QUERY_VERSION; use super::TRANSACTION_VERSION; + use zeroable::Zeroable; #[storage] struct Storage { diff --git a/src/account/dual_account.cairo b/src/account/dual_account.cairo index a55600814..9e4d321c8 100644 --- a/src/account/dual_account.cairo +++ b/src/account/dual_account.cairo @@ -3,13 +3,12 @@ use array::ArrayTrait; use array::SpanTrait; -use starknet::ContractAddress; -use starknet::SyscallResultTrait; - +use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; use openzeppelin::utils::try_selector_with_fallback; -use openzeppelin::utils::UnwrapAndCast; +use starknet::ContractAddress; +use starknet::SyscallResultTrait; #[derive(Copy, Drop)] struct DualCaseAccount { diff --git a/src/account/interface.cairo b/src/account/interface.cairo index fdf4563a7..b9a5abbef 100644 --- a/src/account/interface.cairo +++ b/src/account/interface.cairo @@ -3,8 +3,8 @@ use array::ArrayTrait; use array::SpanTrait; -use starknet::account::Call; use starknet::ContractAddress; +use starknet::account::Call; const ISRC6_ID: felt252 = 0x2ceccef7f994940b3962a6c67e0ba4fcd37df7d131417c604f91e03caecc1cd; diff --git a/src/introspection.cairo b/src/introspection.cairo index 184f921e7..5eb468c65 100644 --- a/src/introspection.cairo +++ b/src/introspection.cairo @@ -1,3 +1,3 @@ -mod src5; mod dual_src5; mod interface; +mod src5; diff --git a/src/introspection/dual_src5.cairo b/src/introspection/dual_src5.cairo index 708bc9ecc..48d7c9c68 100644 --- a/src/introspection/dual_src5.cairo +++ b/src/introspection/dual_src5.cairo @@ -2,11 +2,10 @@ // OpenZeppelin Contracts for Cairo v0.7.0 (introspection/dual_src5.cairo) use array::ArrayTrait; -use starknet::ContractAddress; - +use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; use openzeppelin::utils::try_selector_with_fallback; -use openzeppelin::utils::UnwrapAndCast; +use starknet::ContractAddress; #[derive(Copy, Drop)] struct DualCaseSRC5 { diff --git a/src/security.cairo b/src/security.cairo index 45a9c998a..4713c3cff 100644 --- a/src/security.cairo +++ b/src/security.cairo @@ -1,3 +1,3 @@ -mod reentrancyguard; -mod pausable; mod initializable; +mod pausable; +mod reentrancyguard; diff --git a/src/tests/access.cairo b/src/tests/access.cairo index 149c62b59..2599d6d1f 100644 --- a/src/tests/access.cairo +++ b/src/tests/access.cairo @@ -1,4 +1,4 @@ mod test_accesscontrol; mod test_dual_accesscontrol; -mod test_ownable; mod test_dual_ownable; +mod test_ownable; diff --git a/src/tests/access/test_accesscontrol.cairo b/src/tests/access/test_accesscontrol.cairo index 704242309..86ef607e0 100644 --- a/src/tests/access/test_accesscontrol.cairo +++ b/src/tests/access/test_accesscontrol.cairo @@ -1,12 +1,12 @@ -use starknet::contract_address_const; -use starknet::ContractAddress; -use starknet::testing; -use openzeppelin::access::accesscontrol::AccessControl; -use openzeppelin::access::accesscontrol::AccessControl::InternalImpl; -use openzeppelin::access::accesscontrol::AccessControl::AccessControlImpl; use openzeppelin::access::accesscontrol::AccessControl::AccessControlCamelImpl; +use openzeppelin::access::accesscontrol::AccessControl::AccessControlImpl; +use openzeppelin::access::accesscontrol::AccessControl::InternalImpl; +use openzeppelin::access::accesscontrol::AccessControl; use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; use openzeppelin::access::accesscontrol::interface::IACCESSCONTROL_ID; +use starknet::ContractAddress; +use starknet::contract_address_const; +use starknet::testing; const ROLE: felt252 = 41; const OTHER_ROLE: felt252 = 42; diff --git a/src/tests/access/test_dual_accesscontrol.cairo b/src/tests/access/test_dual_accesscontrol.cairo index fb3c916d5..a20657902 100644 --- a/src/tests/access/test_dual_accesscontrol.cairo +++ b/src/tests/access/test_dual_accesscontrol.cairo @@ -1,23 +1,22 @@ use array::ArrayTrait; -use starknet::ContractAddress; -use starknet::contract_address_const; -use starknet::testing::set_contract_address; - use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; +use openzeppelin::access::accesscontrol::dual_accesscontrol::DualCaseAccessControl; +use openzeppelin::access::accesscontrol::dual_accesscontrol::DualCaseAccessControlTrait; use openzeppelin::access::accesscontrol::interface::IACCESSCONTROL_ID; -use openzeppelin::access::accesscontrol::interface::IAccessControlDispatcher; use openzeppelin::access::accesscontrol::interface::IAccessControlCamelDispatcher; -use openzeppelin::access::accesscontrol::interface::IAccessControlDispatcherTrait; use openzeppelin::access::accesscontrol::interface::IAccessControlCamelDispatcherTrait; -use openzeppelin::access::accesscontrol::dual_accesscontrol::DualCaseAccessControlTrait; -use openzeppelin::access::accesscontrol::dual_accesscontrol::DualCaseAccessControl; -use openzeppelin::tests::mocks::snake_accesscontrol_mock::SnakeAccessControlMock; -use openzeppelin::tests::mocks::camel_accesscontrol_mock::CamelAccessControlMock; -use openzeppelin::tests::mocks::accesscontrol_panic_mock::SnakeAccessControlPanicMock; +use openzeppelin::access::accesscontrol::interface::IAccessControlDispatcher; +use openzeppelin::access::accesscontrol::interface::IAccessControlDispatcherTrait; use openzeppelin::tests::mocks::accesscontrol_panic_mock::CamelAccessControlPanicMock; +use openzeppelin::tests::mocks::accesscontrol_panic_mock::SnakeAccessControlPanicMock; +use openzeppelin::tests::mocks::camel_accesscontrol_mock::CamelAccessControlMock; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; +use openzeppelin::tests::mocks::snake_accesscontrol_mock::SnakeAccessControlMock; use openzeppelin::tests::utils; use openzeppelin::utils::serde::SerializedAppend; +use starknet::ContractAddress; +use starknet::contract_address_const; +use starknet::testing::set_contract_address; // // Constants diff --git a/src/tests/access/test_dual_ownable.cairo b/src/tests/access/test_dual_ownable.cairo index 470709dc6..380672757 100644 --- a/src/tests/access/test_dual_ownable.cairo +++ b/src/tests/access/test_dual_ownable.cairo @@ -1,22 +1,21 @@ -use starknet::ContractAddress; -use starknet::contract_address_const; -use starknet::testing::set_caller_address; -use starknet::testing::set_contract_address; -use zeroable::Zeroable; - -use openzeppelin::access::ownable::interface::IOwnableDispatcher; +use openzeppelin::access::ownable::dual_ownable::DualCaseOwnable; +use openzeppelin::access::ownable::dual_ownable::DualCaseOwnableTrait; use openzeppelin::access::ownable::interface::IOwnableCamelOnlyDispatcher; -use openzeppelin::access::ownable::interface::IOwnableDispatcherTrait; use openzeppelin::access::ownable::interface::IOwnableCamelOnlyDispatcherTrait; -use openzeppelin::access::ownable::dual_ownable::DualCaseOwnableTrait; -use openzeppelin::access::ownable::dual_ownable::DualCaseOwnable; -use openzeppelin::tests::mocks::dual_ownable_mocks::SnakeOwnableMock; +use openzeppelin::access::ownable::interface::IOwnableDispatcher; +use openzeppelin::access::ownable::interface::IOwnableDispatcherTrait; use openzeppelin::tests::mocks::dual_ownable_mocks::CamelOwnableMock; -use openzeppelin::tests::mocks::dual_ownable_mocks::SnakeOwnablePanicMock; use openzeppelin::tests::mocks::dual_ownable_mocks::CamelOwnablePanicMock; +use openzeppelin::tests::mocks::dual_ownable_mocks::SnakeOwnableMock; +use openzeppelin::tests::mocks::dual_ownable_mocks::SnakeOwnablePanicMock; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils; use openzeppelin::utils::serde::SerializedAppend; +use starknet::ContractAddress; +use starknet::contract_address_const; +use starknet::testing::set_caller_address; +use starknet::testing::set_contract_address; +use zeroable::Zeroable; // // Constants diff --git a/src/tests/access/test_ownable.cairo b/src/tests/access/test_ownable.cairo index 2b906dbed..d88d0f786 100644 --- a/src/tests/access/test_ownable.cairo +++ b/src/tests/access/test_ownable.cairo @@ -1,12 +1,12 @@ +use openzeppelin::access::ownable::Ownable::InternalImpl; +use openzeppelin::access::ownable::Ownable::OwnableCamelOnlyImpl; +use openzeppelin::access::ownable::Ownable::OwnableImpl; +use openzeppelin::access::ownable::Ownable::_owner::InternalContractStateTrait; +use openzeppelin::access::ownable::Ownable; use starknet::ContractAddress; use starknet::contract_address_const; use starknet::testing; use zeroable::Zeroable; -use openzeppelin::access::ownable::Ownable; -use openzeppelin::access::ownable::Ownable::OwnableImpl; -use openzeppelin::access::ownable::Ownable::OwnableCamelOnlyImpl; -use openzeppelin::access::ownable::Ownable::InternalImpl; -use openzeppelin::access::ownable::Ownable::_owner::InternalContractStateTrait; fn ZERO() -> ContractAddress { contract_address_const::<0>() diff --git a/src/tests/account/test_account.cairo b/src/tests/account/test_account.cairo index b5d9828f7..b2fe45ceb 100644 --- a/src/tests/account/test_account.cairo +++ b/src/tests/account/test_account.cairo @@ -1,18 +1,11 @@ use array::ArrayTrait; use core::traits::Into; -use option::OptionTrait; -use serde::Serde; -use starknet::account::Call; -use starknet::contract_address_const; -use starknet::ContractAddress; -use starknet::testing; - use openzeppelin::account::Account; use openzeppelin::account::AccountABIDispatcher; use openzeppelin::account::AccountABIDispatcherTrait; -use openzeppelin::account::interface::ISRC6_ID; use openzeppelin::account::QUERY_VERSION; use openzeppelin::account::TRANSACTION_VERSION; +use openzeppelin::account::interface::ISRC6_ID; use openzeppelin::introspection::interface::ISRC5_ID; use openzeppelin::tests::utils; use openzeppelin::token::erc20::ERC20; @@ -20,6 +13,12 @@ use openzeppelin::token::erc20::interface::IERC20Dispatcher; use openzeppelin::token::erc20::interface::IERC20DispatcherTrait; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; +use option::OptionTrait; +use serde::Serde; +use starknet::ContractAddress; +use starknet::account::Call; +use starknet::contract_address_const; +use starknet::testing; // // Constants diff --git a/src/tests/account/test_dual_account.cairo b/src/tests/account/test_dual_account.cairo index c1178b22a..f38c308c5 100644 --- a/src/tests/account/test_dual_account.cairo +++ b/src/tests/account/test_dual_account.cairo @@ -1,11 +1,9 @@ -use starknet::testing; - -use openzeppelin::account::dual_account::DualCaseAccount; -use openzeppelin::account::dual_account::DualCaseAccountABI; -use openzeppelin::account::AccountCamelABIDispatcher; -use openzeppelin::account::AccountCamelABIDispatcherTrait; use openzeppelin::account::AccountABIDispatcher; use openzeppelin::account::AccountABIDispatcherTrait; +use openzeppelin::account::AccountCamelABIDispatcher; +use openzeppelin::account::AccountCamelABIDispatcherTrait; +use openzeppelin::account::dual_account::DualCaseAccount; +use openzeppelin::account::dual_account::DualCaseAccountABI; use openzeppelin::introspection::interface::ISRC5_ID; use openzeppelin::tests::account::test_account::SIGNED_TX_DATA; use openzeppelin::tests::mocks::account_panic_mock::CamelAccountPanicMock; @@ -14,6 +12,7 @@ use openzeppelin::tests::mocks::camel_account_mock::CamelAccountMock; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::mocks::snake_account_mock::SnakeAccountMock; use openzeppelin::tests::utils; +use starknet::testing; // // Constants diff --git a/src/tests/introspection.cairo b/src/tests/introspection.cairo index d46bcfe7d..010db98a8 100644 --- a/src/tests/introspection.cairo +++ b/src/tests/introspection.cairo @@ -1,2 +1,2 @@ -mod test_src5; mod test_dual_src5; +mod test_src5; diff --git a/src/tests/introspection/test_dual_src5.cairo b/src/tests/introspection/test_dual_src5.cairo index 629cd4977..84157605b 100644 --- a/src/tests/introspection/test_dual_src5.cairo +++ b/src/tests/introspection/test_dual_src5.cairo @@ -1,16 +1,16 @@ use array::ArrayTrait; -use openzeppelin::introspection::interface::ISRC5_ID; -use openzeppelin::introspection::interface::ISRC5Dispatcher; -use openzeppelin::introspection::interface::ISRC5DispatcherTrait; -use openzeppelin::introspection::interface::ISRC5CamelDispatcher; -use openzeppelin::introspection::interface::ISRC5CamelDispatcherTrait; use openzeppelin::introspection::dual_src5::DualCaseSRC5; use openzeppelin::introspection::dual_src5::DualCaseSRC5Trait; -use openzeppelin::tests::mocks::src5_mocks::SnakeSRC5Mock; +use openzeppelin::introspection::interface::ISRC5CamelDispatcher; +use openzeppelin::introspection::interface::ISRC5CamelDispatcherTrait; +use openzeppelin::introspection::interface::ISRC5Dispatcher; +use openzeppelin::introspection::interface::ISRC5DispatcherTrait; +use openzeppelin::introspection::interface::ISRC5_ID; +use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::mocks::src5_mocks::CamelSRC5Mock; -use openzeppelin::tests::mocks::src5_mocks::SnakeSRC5PanicMock; use openzeppelin::tests::mocks::src5_mocks::CamelSRC5PanicMock; -use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; +use openzeppelin::tests::mocks::src5_mocks::SnakeSRC5Mock; +use openzeppelin::tests::mocks::src5_mocks::SnakeSRC5PanicMock; use openzeppelin::tests::utils; // diff --git a/src/tests/introspection/test_src5.cairo b/src/tests/introspection/test_src5.cairo index 7b0ea5418..d81cf3e37 100644 --- a/src/tests/introspection/test_src5.cairo +++ b/src/tests/introspection/test_src5.cairo @@ -1,7 +1,7 @@ -use openzeppelin::introspection::src5::SRC5; -use openzeppelin::introspection::src5::SRC5::SRC5Impl; -use openzeppelin::introspection::src5::SRC5::InternalImpl; use openzeppelin::introspection::interface::ISRC5_ID; +use openzeppelin::introspection::src5::SRC5::InternalImpl; +use openzeppelin::introspection::src5::SRC5::SRC5Impl; +use openzeppelin::introspection::src5::SRC5; const OTHER_ID: felt252 = 0x12345678; diff --git a/src/tests/mocks.cairo b/src/tests/mocks.cairo index fd2e8f322..cb3112db1 100644 --- a/src/tests/mocks.cairo +++ b/src/tests/mocks.cairo @@ -1,19 +1,19 @@ -mod reentrancy_attacker_mock; -mod reentrancy_mock; -mod erc721_receiver; -mod erc721_panic_mock; -mod camel20_mock; -mod snake20_mock; -mod erc20_panic; mod accesscontrol_panic_mock; mod account_panic_mock; +mod camel20_mock; +mod camel721_mock; mod camel_accesscontrol_mock; mod camel_account_mock; -mod snake_accesscontrol_mock; -mod snake_account_mock; +mod dual721_receiver_mocks; mod dual_ownable_mocks; -mod snake721_mock; -mod camel721_mock; +mod erc20_panic; +mod erc721_panic_mock; +mod erc721_receiver; mod non_implementing_mock; -mod dual721_receiver_mocks; +mod reentrancy_attacker_mock; +mod reentrancy_mock; +mod snake20_mock; +mod snake721_mock; +mod snake_accesscontrol_mock; +mod snake_account_mock; mod src5_mocks; diff --git a/src/tests/mocks/camel20_mock.cairo b/src/tests/mocks/camel20_mock.cairo index 6eab0e06f..7c6e55637 100644 --- a/src/tests/mocks/camel20_mock.cairo +++ b/src/tests/mocks/camel20_mock.cairo @@ -1,7 +1,7 @@ #[starknet::contract] mod CamelERC20Mock { - use starknet::ContractAddress; use openzeppelin::token::erc20::ERC20; + use starknet::ContractAddress; #[storage] struct Storage {} diff --git a/src/tests/mocks/camel721_mock.cairo b/src/tests/mocks/camel721_mock.cairo index 553114f7e..9fcbbb248 100644 --- a/src/tests/mocks/camel721_mock.cairo +++ b/src/tests/mocks/camel721_mock.cairo @@ -1,10 +1,10 @@ #[starknet::contract] mod CamelERC721Mock { + use openzeppelin::token::erc721::ERC721::ERC721CamelOnlyImpl; + use openzeppelin::token::erc721::ERC721::InternalImpl; + use openzeppelin::token::erc721::ERC721; use starknet::ContractAddress; use starknet::get_caller_address; - use openzeppelin::token::erc721::ERC721; - use openzeppelin::token::erc721::ERC721::InternalImpl; - use openzeppelin::token::erc721::ERC721::ERC721CamelOnlyImpl; #[storage] struct Storage {} diff --git a/src/tests/mocks/camel_accesscontrol_mock.cairo b/src/tests/mocks/camel_accesscontrol_mock.cairo index dc0879a60..55835f50c 100644 --- a/src/tests/mocks/camel_accesscontrol_mock.cairo +++ b/src/tests/mocks/camel_accesscontrol_mock.cairo @@ -1,10 +1,10 @@ #[starknet::contract] mod CamelAccessControlMock { - use starknet::ContractAddress; - use starknet::get_caller_address; - use openzeppelin::access::accesscontrol::AccessControl; use openzeppelin::access::accesscontrol::AccessControl::AccessControlCamelImpl; + use openzeppelin::access::accesscontrol::AccessControl; use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; + use starknet::ContractAddress; + use starknet::get_caller_address; #[storage] struct Storage {} diff --git a/src/tests/mocks/dual721_receiver_mocks.cairo b/src/tests/mocks/dual721_receiver_mocks.cairo index 897918b68..a6e4f5f2a 100644 --- a/src/tests/mocks/dual721_receiver_mocks.cairo +++ b/src/tests/mocks/dual721_receiver_mocks.cairo @@ -1,6 +1,6 @@ use openzeppelin::introspection::src5::SRC5; -use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver::IERC721_RECEIVER_ID; +use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; #[starknet::contract] mod SnakeERC721ReceiverMock { diff --git a/src/tests/mocks/erc721_receiver.cairo b/src/tests/mocks/erc721_receiver.cairo index 673e4100e..9205f6d97 100644 --- a/src/tests/mocks/erc721_receiver.cairo +++ b/src/tests/mocks/erc721_receiver.cairo @@ -4,14 +4,13 @@ const FAILURE: felt252 = 456456; #[starknet::contract] mod ERC721Receiver { use array::SpanTrait; - use starknet::ContractAddress; - - use openzeppelin::token::erc721::interface::IERC721Receiver; - use openzeppelin::token::erc721::interface::IERC721ReceiverCamel; - use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; use openzeppelin::introspection::interface::ISRC5; use openzeppelin::introspection::interface::ISRC5Camel; use openzeppelin::introspection::src5::SRC5; + use openzeppelin::token::erc721::interface::IERC721Receiver; + use openzeppelin::token::erc721::interface::IERC721ReceiverCamel; + use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; + use starknet::ContractAddress; #[storage] struct Storage {} diff --git a/src/tests/mocks/reentrancy_attacker_mock.cairo b/src/tests/mocks/reentrancy_attacker_mock.cairo index 39560f902..6657cc145 100644 --- a/src/tests/mocks/reentrancy_attacker_mock.cairo +++ b/src/tests/mocks/reentrancy_attacker_mock.cairo @@ -5,10 +5,10 @@ trait IAttacker { #[starknet::contract] mod Attacker { - use starknet::ContractAddress; - use starknet::get_caller_address; use openzeppelin::tests::mocks::reentrancy_mock::IReentrancyMockDispatcher; use openzeppelin::tests::mocks::reentrancy_mock::IReentrancyMockDispatcherTrait; + use starknet::ContractAddress; + use starknet::get_caller_address; #[storage] struct Storage {} diff --git a/src/tests/mocks/reentrancy_mock.cairo b/src/tests/mocks/reentrancy_mock.cairo index 718a7db39..1204afa47 100644 --- a/src/tests/mocks/reentrancy_mock.cairo +++ b/src/tests/mocks/reentrancy_mock.cairo @@ -16,11 +16,11 @@ trait IReentrancyMock { #[starknet::contract] mod ReentrancyMock { - use starknet::ContractAddress; - use starknet::get_contract_address; use openzeppelin::security::reentrancyguard::ReentrancyGuard; use openzeppelin::tests::mocks::reentrancy_attacker_mock::IAttackerDispatcher; use openzeppelin::tests::mocks::reentrancy_attacker_mock::IAttackerDispatcherTrait; + use starknet::ContractAddress; + use starknet::get_contract_address; use super::IReentrancyGuardedDispatcher; use super::IReentrancyGuardedDispatcherTrait; diff --git a/src/tests/mocks/snake20_mock.cairo b/src/tests/mocks/snake20_mock.cairo index ba20d54b1..17c7b32ef 100644 --- a/src/tests/mocks/snake20_mock.cairo +++ b/src/tests/mocks/snake20_mock.cairo @@ -1,7 +1,7 @@ #[starknet::contract] mod SnakeERC20Mock { - use starknet::ContractAddress; use openzeppelin::token::erc20::ERC20; + use starknet::ContractAddress; #[storage] struct Storage {} diff --git a/src/tests/mocks/snake721_mock.cairo b/src/tests/mocks/snake721_mock.cairo index 13335a52f..6f1382c61 100644 --- a/src/tests/mocks/snake721_mock.cairo +++ b/src/tests/mocks/snake721_mock.cairo @@ -1,10 +1,10 @@ #[starknet::contract] mod SnakeERC721Mock { + use openzeppelin::token::erc721::ERC721::ERC721Impl; + use openzeppelin::token::erc721::ERC721::InternalImpl; + use openzeppelin::token::erc721::ERC721; use starknet::ContractAddress; use starknet::get_caller_address; - use openzeppelin::token::erc721::ERC721; - use openzeppelin::token::erc721::ERC721::InternalImpl; - use openzeppelin::token::erc721::ERC721::ERC721Impl; #[storage] struct Storage {} diff --git a/src/tests/mocks/snake_accesscontrol_mock.cairo b/src/tests/mocks/snake_accesscontrol_mock.cairo index 1d69feaec..48b291979 100644 --- a/src/tests/mocks/snake_accesscontrol_mock.cairo +++ b/src/tests/mocks/snake_accesscontrol_mock.cairo @@ -1,10 +1,10 @@ #[starknet::contract] mod SnakeAccessControlMock { - use starknet::ContractAddress; - use starknet::get_caller_address; - use openzeppelin::access::accesscontrol::AccessControl; use openzeppelin::access::accesscontrol::AccessControl::AccessControlImpl; + use openzeppelin::access::accesscontrol::AccessControl; use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; + use starknet::ContractAddress; + use starknet::get_caller_address; #[storage] struct Storage {} diff --git a/src/tests/security.cairo b/src/tests/security.cairo index 1efdf53c8..35c30a0e8 100644 --- a/src/tests/security.cairo +++ b/src/tests/security.cairo @@ -1,3 +1,3 @@ mod test_initializable; -mod test_reentrancyguard; mod test_pausable; +mod test_reentrancyguard; diff --git a/src/tests/security/test_initializable.cairo b/src/tests/security/test_initializable.cairo index 86bf68fbc..66d2b89dc 100644 --- a/src/tests/security/test_initializable.cairo +++ b/src/tests/security/test_initializable.cairo @@ -1,5 +1,5 @@ -use openzeppelin::security::initializable::Initializable; use openzeppelin::security::initializable::Initializable::InternalImpl; +use openzeppelin::security::initializable::Initializable; fn STATE() -> Initializable::ContractState { Initializable::contract_state_for_testing() diff --git a/src/tests/security/test_pausable.cairo b/src/tests/security/test_pausable.cairo index 79bafced6..ac84fedc2 100644 --- a/src/tests/security/test_pausable.cairo +++ b/src/tests/security/test_pausable.cairo @@ -1,6 +1,6 @@ -use openzeppelin::security::pausable::Pausable; -use openzeppelin::security::pausable::Pausable::PausableImpl; use openzeppelin::security::pausable::Pausable::InternalImpl; +use openzeppelin::security::pausable::Pausable::PausableImpl; +use openzeppelin::security::pausable::Pausable; fn STATE() -> Pausable::ContractState { Pausable::contract_state_for_testing() diff --git a/src/tests/security/test_reentrancyguard.cairo b/src/tests/security/test_reentrancyguard.cairo index f0891b213..b608706db 100644 --- a/src/tests/security/test_reentrancyguard.cairo +++ b/src/tests/security/test_reentrancyguard.cairo @@ -1,10 +1,10 @@ -use openzeppelin::security::reentrancyguard::ReentrancyGuard; use openzeppelin::security::reentrancyguard::ReentrancyGuard::InternalImpl; use openzeppelin::security::reentrancyguard::ReentrancyGuard::entered::InternalContractStateTrait; -use openzeppelin::tests::mocks::reentrancy_mock::ReentrancyMock; +use openzeppelin::security::reentrancyguard::ReentrancyGuard; +use openzeppelin::tests::mocks::reentrancy_attacker_mock::Attacker; use openzeppelin::tests::mocks::reentrancy_mock::IReentrancyMockDispatcher; use openzeppelin::tests::mocks::reentrancy_mock::IReentrancyMockDispatcherTrait; -use openzeppelin::tests::mocks::reentrancy_attacker_mock::Attacker; +use openzeppelin::tests::mocks::reentrancy_mock::ReentrancyMock; use openzeppelin::tests::utils; fn STATE() -> ReentrancyGuard::ContractState { diff --git a/src/tests/token.cairo b/src/tests/token.cairo index fa678fb9c..458159c98 100644 --- a/src/tests/token.cairo +++ b/src/tests/token.cairo @@ -1,6 +1,6 @@ mod test_dual20; mod test_dual721; +mod test_dual721_receiver; mod test_erc20; mod test_erc721; -mod test_dual721_receiver; diff --git a/src/tests/token/test_dual20.cairo b/src/tests/token/test_dual20.cairo index d8b56c5fa..6e1e5dc86 100644 --- a/src/tests/token/test_dual20.cairo +++ b/src/tests/token/test_dual20.cairo @@ -1,21 +1,20 @@ use array::ArrayTrait; -use starknet::ContractAddress; -use starknet::contract_address_const; -use starknet::testing::set_contract_address; - use openzeppelin::tests::mocks::camel20_mock::CamelERC20Mock; -use openzeppelin::tests::mocks::erc20_panic::SnakeERC20Panic; use openzeppelin::tests::mocks::erc20_panic::CamelERC20Panic; +use openzeppelin::tests::mocks::erc20_panic::SnakeERC20Panic; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::mocks::snake20_mock::SnakeERC20Mock; +use openzeppelin::tests::utils; use openzeppelin::token::erc20::dual20::DualERC20; use openzeppelin::token::erc20::dual20::DualERC20Trait; use openzeppelin::token::erc20::interface::IERC20CamelDispatcher; use openzeppelin::token::erc20::interface::IERC20CamelDispatcherTrait; use openzeppelin::token::erc20::interface::IERC20Dispatcher; use openzeppelin::token::erc20::interface::IERC20DispatcherTrait; -use openzeppelin::tests::utils; use openzeppelin::utils::serde::SerializedAppend; +use starknet::ContractAddress; +use starknet::contract_address_const; +use starknet::testing::set_contract_address; // // Constants diff --git a/src/tests/token/test_dual721.cairo b/src/tests/token/test_dual721.cairo index 8ff48f036..55349de11 100644 --- a/src/tests/token/test_dual721.cairo +++ b/src/tests/token/test_dual721.cairo @@ -1,25 +1,25 @@ use array::ArrayTrait; -use starknet::ContractAddress; -use starknet::contract_address_const; -use starknet::testing::set_caller_address; -use starknet::testing::set_contract_address; -use openzeppelin::token::erc721::interface::IERC721_ID; -use openzeppelin::token::erc721::interface::IERC721Dispatcher; -use openzeppelin::token::erc721::interface::IERC721CamelOnlyDispatcher; -use openzeppelin::token::erc721::interface::IERC721DispatcherTrait; -use openzeppelin::token::erc721::interface::IERC721CamelOnlyDispatcherTrait; -use openzeppelin::token::erc721::dual721::DualCaseERC721Trait; -use openzeppelin::token::erc721::dual721::DualCaseERC721; -use openzeppelin::tests::mocks::snake721_mock::SnakeERC721Mock; use openzeppelin::tests::mocks::camel721_mock::CamelERC721Mock; +use openzeppelin::tests::mocks::erc721_panic_mock::CamelERC721PanicMock; +use openzeppelin::tests::mocks::erc721_panic_mock::SnakeERC721PanicMock; use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; -use openzeppelin::tests::mocks::erc721_receiver::SUCCESS; use openzeppelin::tests::mocks::erc721_receiver::FAILURE; -use openzeppelin::tests::mocks::erc721_panic_mock::SnakeERC721PanicMock; -use openzeppelin::tests::mocks::erc721_panic_mock::CamelERC721PanicMock; +use openzeppelin::tests::mocks::erc721_receiver::SUCCESS; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; +use openzeppelin::tests::mocks::snake721_mock::SnakeERC721Mock; use openzeppelin::tests::utils; +use openzeppelin::token::erc721::dual721::DualCaseERC721; +use openzeppelin::token::erc721::dual721::DualCaseERC721Trait; +use openzeppelin::token::erc721::interface::IERC721CamelOnlyDispatcher; +use openzeppelin::token::erc721::interface::IERC721CamelOnlyDispatcherTrait; +use openzeppelin::token::erc721::interface::IERC721Dispatcher; +use openzeppelin::token::erc721::interface::IERC721DispatcherTrait; +use openzeppelin::token::erc721::interface::IERC721_ID; use openzeppelin::utils::serde::SerializedAppend; +use starknet::ContractAddress; +use starknet::contract_address_const; +use starknet::testing::set_caller_address; +use starknet::testing::set_contract_address; // // Constants diff --git a/src/tests/token/test_dual721_receiver.cairo b/src/tests/token/test_dual721_receiver.cairo index ef02380a9..9fde537c0 100644 --- a/src/tests/token/test_dual721_receiver.cairo +++ b/src/tests/token/test_dual721_receiver.cairo @@ -1,21 +1,21 @@ use array::ArrayTrait; -use starknet::ContractAddress; -use starknet::contract_address_const; -use openzeppelin::tests::mocks::erc721_receiver::SUCCESS; -use openzeppelin::tests::mocks::erc721_receiver::FAILURE; -use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; -use openzeppelin::token::erc721::interface::IERC721ReceiverDispatcher; -use openzeppelin::token::erc721::interface::IERC721ReceiverCamelDispatcher; -use openzeppelin::token::erc721::interface::IERC721ReceiverDispatcherTrait; -use openzeppelin::token::erc721::interface::IERC721ReceiverCamelDispatcherTrait; -use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721ReceiverTrait; -use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721Receiver; -use openzeppelin::tests::mocks::dual721_receiver_mocks::SnakeERC721ReceiverMock; use openzeppelin::tests::mocks::dual721_receiver_mocks::CamelERC721ReceiverMock; -use openzeppelin::tests::mocks::dual721_receiver_mocks::SnakeERC721ReceiverPanicMock; use openzeppelin::tests::mocks::dual721_receiver_mocks::CamelERC721ReceiverPanicMock; +use openzeppelin::tests::mocks::dual721_receiver_mocks::SnakeERC721ReceiverMock; +use openzeppelin::tests::mocks::dual721_receiver_mocks::SnakeERC721ReceiverPanicMock; +use openzeppelin::tests::mocks::erc721_receiver::FAILURE; +use openzeppelin::tests::mocks::erc721_receiver::SUCCESS; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils; +use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721Receiver; +use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721ReceiverTrait; +use openzeppelin::token::erc721::interface::IERC721ReceiverCamelDispatcher; +use openzeppelin::token::erc721::interface::IERC721ReceiverCamelDispatcherTrait; +use openzeppelin::token::erc721::interface::IERC721ReceiverDispatcher; +use openzeppelin::token::erc721::interface::IERC721ReceiverDispatcherTrait; +use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; +use starknet::ContractAddress; +use starknet::contract_address_const; // // Constants diff --git a/src/tests/token/test_erc20.cairo b/src/tests/token/test_erc20.cairo index 55945c0e2..1303c3e1c 100644 --- a/src/tests/token/test_erc20.cairo +++ b/src/tests/token/test_erc20.cairo @@ -1,17 +1,16 @@ use integer::BoundedInt; use integer::u256; use integer::u256_from_felt252; +use openzeppelin::token::erc20::ERC20::ERC20CamelOnlyImpl; +use openzeppelin::token::erc20::ERC20::ERC20Impl; +use openzeppelin::token::erc20::ERC20::InternalImpl; +use openzeppelin::token::erc20::ERC20; use starknet::ContractAddress; use starknet::contract_address_const; use starknet::testing::set_caller_address; use traits::Into; use zeroable::Zeroable; -use openzeppelin::token::erc20::ERC20; -use openzeppelin::token::erc20::ERC20::ERC20CamelOnlyImpl; -use openzeppelin::token::erc20::ERC20::ERC20Impl; -use openzeppelin::token::erc20::ERC20::InternalImpl; - // // Constants // diff --git a/src/tests/token/test_erc721.cairo b/src/tests/token/test_erc721.cairo index 5c7fdc4f9..45bd6c39e 100644 --- a/src/tests/token/test_erc721.cairo +++ b/src/tests/token/test_erc721.cairo @@ -1,17 +1,19 @@ -use starknet::contract_address_const; -use starknet::ContractAddress; -use starknet::testing::set_caller_address; +use ERC721::_owners::InternalContractStateTrait as OwnersTrait; +use ERC721::_token_approvals::InternalContractStateTrait as TokenApprovalsTrait; +use array::ArrayTrait; use integer::u256; use integer::u256_from_felt252; -use array::ArrayTrait; -use traits::Into; -use zeroable::Zeroable; use openzeppelin::account::Account; -use openzeppelin::introspection; use openzeppelin::introspection::src5; -use openzeppelin::token::erc721; -use openzeppelin::token::erc721::ERC721; +use openzeppelin::introspection; +use openzeppelin::tests::mocks::camel_account_mock::CamelAccountMock; +use openzeppelin::tests::mocks::dual721_receiver_mocks::CamelERC721ReceiverMock; +use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; +use openzeppelin::tests::mocks::erc721_receiver::FAILURE; +use openzeppelin::tests::mocks::erc721_receiver::SUCCESS; +use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; +use openzeppelin::tests::utils; use openzeppelin::token::erc721::ERC721::ERC721CamelOnlyImpl; use openzeppelin::token::erc721::ERC721::ERC721Impl; use openzeppelin::token::erc721::ERC721::ERC721MetadataCamelOnlyImpl; @@ -19,16 +21,13 @@ use openzeppelin::token::erc721::ERC721::ERC721MetadataImpl; use openzeppelin::token::erc721::ERC721::InternalImpl; use openzeppelin::token::erc721::ERC721::SRC5CamelImpl; use openzeppelin::token::erc721::ERC721::SRC5Impl; -use openzeppelin::tests::mocks::camel_account_mock::CamelAccountMock; -use openzeppelin::tests::mocks::dual721_receiver_mocks::CamelERC721ReceiverMock; -use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; -use openzeppelin::tests::mocks::erc721_receiver::SUCCESS; -use openzeppelin::tests::mocks::erc721_receiver::FAILURE; -use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; -use openzeppelin::tests::utils; - -use ERC721::_owners::InternalContractStateTrait as OwnersTrait; -use ERC721::_token_approvals::InternalContractStateTrait as TokenApprovalsTrait; +use openzeppelin::token::erc721::ERC721; +use openzeppelin::token::erc721; +use starknet::ContractAddress; +use starknet::contract_address_const; +use starknet::testing::set_caller_address; +use traits::Into; +use zeroable::Zeroable; const NAME: felt252 = 111; const SYMBOL: felt252 = 222; diff --git a/src/tests/utils.cairo b/src/tests/utils.cairo index d3b720cce..5eb3d1001 100644 --- a/src/tests/utils.cairo +++ b/src/tests/utils.cairo @@ -1,8 +1,8 @@ use array::ArrayTrait; use core::result::ResultTrait; use option::OptionTrait; -use starknet::class_hash::Felt252TryIntoClassHash; use starknet::ContractAddress; +use starknet::class_hash::Felt252TryIntoClassHash; use traits::TryInto; fn deploy(contract_class_hash: felt252, calldata: Array) -> ContractAddress { diff --git a/src/token/erc20.cairo b/src/token/erc20.cairo index 6784045a0..736230aca 100644 --- a/src/token/erc20.cairo +++ b/src/token/erc20.cairo @@ -1,5 +1,5 @@ -mod erc20; mod dual20; +mod erc20; mod interface; use erc20::ERC20; diff --git a/src/token/erc20/dual20.cairo b/src/token/erc20/dual20.cairo index 1a867c0f7..28897c6f5 100644 --- a/src/token/erc20/dual20.cairo +++ b/src/token/erc20/dual20.cairo @@ -2,14 +2,13 @@ // OpenZeppelin Contracts for Cairo v0.7.0 (token/erc20/dual20.cairo) use array::ArrayTrait; -use starknet::call_contract_syscall; -use starknet::ContractAddress; -use starknet::SyscallResultTrait; - -use openzeppelin::utils::try_selector_with_fallback; +use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; -use openzeppelin::utils::UnwrapAndCast; +use openzeppelin::utils::try_selector_with_fallback; +use starknet::ContractAddress; +use starknet::SyscallResultTrait; +use starknet::call_contract_syscall; #[derive(Copy, Drop)] struct DualERC20 { diff --git a/src/token/erc20/erc20.cairo b/src/token/erc20/erc20.cairo index ebdc5af0f..9448b8ef3 100644 --- a/src/token/erc20/erc20.cairo +++ b/src/token/erc20/erc20.cairo @@ -4,13 +4,12 @@ #[starknet::contract] mod ERC20 { use integer::BoundedInt; + use openzeppelin::token::erc20::interface::IERC20; + use openzeppelin::token::erc20::interface::IERC20CamelOnly; use starknet::ContractAddress; use starknet::get_caller_address; use zeroable::Zeroable; - use openzeppelin::token::erc20::interface::IERC20; - use openzeppelin::token::erc20::interface::IERC20CamelOnly; - #[storage] struct Storage { _name: felt252, diff --git a/src/token/erc721/dual721.cairo b/src/token/erc721/dual721.cairo index 8d4ce8ffa..88cc35abd 100644 --- a/src/token/erc721/dual721.cairo +++ b/src/token/erc721/dual721.cairo @@ -2,13 +2,13 @@ // OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/dual721.cairo) use array::ArrayTrait; +use openzeppelin::utils::UnwrapAndCast; +use openzeppelin::utils::selectors; +use openzeppelin::utils::serde::SerializedAppend; +use openzeppelin::utils::try_selector_with_fallback; use starknet::ContractAddress; use starknet::SyscallResultTrait; use starknet::call_contract_syscall; -use openzeppelin::utils::try_selector_with_fallback; -use openzeppelin::utils::selectors; -use openzeppelin::utils::UnwrapAndCast; -use openzeppelin::utils::serde::SerializedAppend; #[derive(Copy, Drop)] struct DualCaseERC721 { diff --git a/src/token/erc721/dual721_receiver.cairo b/src/token/erc721/dual721_receiver.cairo index 55029bdd1..650285d6d 100644 --- a/src/token/erc721/dual721_receiver.cairo +++ b/src/token/erc721/dual721_receiver.cairo @@ -2,12 +2,12 @@ // OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/dual721_receiver.cairo) use array::ArrayTrait; -use starknet::ContractAddress; -use starknet::SyscallResultTrait; -use openzeppelin::utils::try_selector_with_fallback; +use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; -use openzeppelin::utils::UnwrapAndCast; +use openzeppelin::utils::try_selector_with_fallback; +use starknet::ContractAddress; +use starknet::SyscallResultTrait; #[derive(Copy, Drop)] struct DualCaseERC721Receiver { diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 0ea2598c7..5a5b56b51 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -6,11 +6,6 @@ use starknet::ContractAddress; #[starknet::contract] mod ERC721 { use array::SpanTrait; - use option::OptionTrait; - use starknet::ContractAddress; - use starknet::get_caller_address; - use zeroable::Zeroable; - use openzeppelin::account; use openzeppelin::introspection::dual_src5::DualCaseSRC5; use openzeppelin::introspection::dual_src5::DualCaseSRC5Trait; @@ -20,6 +15,10 @@ mod ERC721 { use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721Receiver; use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721ReceiverTrait; use openzeppelin::token::erc721::interface; + use option::OptionTrait; + use starknet::ContractAddress; + use starknet::get_caller_address; + use zeroable::Zeroable; #[storage] struct Storage { diff --git a/src/token/erc721/interface.cairo b/src/token/erc721/interface.cairo index 44ff80a5e..4aff670b8 100644 --- a/src/token/erc721/interface.cairo +++ b/src/token/erc721/interface.cairo @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/interface.cairo) -use starknet::ContractAddress; use array::SpanTrait; +use starknet::ContractAddress; const IERC721_ID: felt252 = 0x33eb2f84c309543403fd69f0d0f363781ef06ef6faeb0131ff16ea3175bd943; const IERC721_METADATA_ID: felt252 = diff --git a/src/utils.cairo b/src/utils.cairo index 721dfdfee..38b0b35dd 100644 --- a/src/utils.cairo +++ b/src/utils.cairo @@ -10,10 +10,10 @@ use array::ArrayTrait; use array::SpanTrait; use box::BoxTrait; use option::OptionTrait; -use starknet::call_contract_syscall; use starknet::ContractAddress; use starknet::SyscallResult; use starknet::SyscallResultTrait; +use starknet::call_contract_syscall; use unwrap_and_cast::UnwrapAndCast; fn try_selector_with_fallback( diff --git a/src/utils/unwrap_and_cast.cairo b/src/utils/unwrap_and_cast.cairo index 185242dc1..9e58009a2 100644 --- a/src/utils/unwrap_and_cast.cairo +++ b/src/utils/unwrap_and_cast.cairo @@ -2,6 +2,8 @@ // OpenZeppelin Contracts for Cairo v0.7.0 (utils/unwrap_and_cast.cairo) use array::SpanTrait; + +use openzeppelin::utils::Felt252TryIntoBool; use option::OptionTrait; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; @@ -9,8 +11,6 @@ use starknet::SyscallResult; use starknet::SyscallResultTrait; use traits::TryInto; -use openzeppelin::utils::Felt252TryIntoBool; - trait UnwrapAndCast { fn unwrap_and_cast(self: SyscallResult>) -> T; } From e8216d81800b77ce12443aeb95ed37520959f71d Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 9 Aug 2023 21:24:45 -0400 Subject: [PATCH 077/246] start erc721 comments --- src/token/erc721/erc721.cairo | 74 ++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 5a5b56b51..f217bb73b 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -1,8 +1,47 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/erc721.cairo) - -use starknet::ContractAddress; - +//! # License +//! +//! SPDX-License-Identifier: MIT +//! OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/erc721.cairo) +//! +//! # ERC721 Contract and Implementation +//! +//! # Example +//! +//! How to extend the ERC721 contract: +//! ``` +//! #[starknet::contract] +//! mod MyToken { +//! use starknet::ContractAddress; +//! use openzeppelin::token::erc721::ERC721; +//! +//! #[storage] +//! struct Storage {} +//! +//! #[constructor] +//! fn constructor( +//! ref self: ContractState, +//! recipient: ContractAddress, +//! token_id: u256 +//! ) { +//! let name = 'MyNFT'; +//! let symbol = 'NFT'; +//! +//! let mut unsafe_state = ERC721::unsafe_new_contract_state(); +//! ERC721::InternalImpl::initializer(ref unsafe_state, name, symbol); +//! ERC721::InternalImpl::_mint(ref unsafe_state, recipient, token_id); +//! } +//! +//! // Define methods that extend the ERC721 standard contract. +//! #[external(v0)] +//! fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { +//! let unsafe_state = ERC721::unsafe_new_contract_state(); +//! ERC721::ERC721Impl::balance_of(@unsafe_state, account) +//! } +//! +//! ... +//! +//! } +//! ``` #[starknet::contract] mod ERC721 { use array::SpanTrait; @@ -39,6 +78,12 @@ mod ERC721 { ApprovalForAll: ApprovalForAll } + /// Emitted when `token_id` token is transferred from `from` to `to`. + /// + /// # Arguments + /// * `from` - The current owner of the NFT. + /// * `to` - The new owner of the NFT. + /// * `token_id` - The NFT to transfer. #[derive(Drop, starknet::Event)] struct Transfer { from: ContractAddress, @@ -46,6 +91,12 @@ mod ERC721 { token_id: u256 } + /// Emitted when `owner` enables `approved` to manage the `token_id` token. + /// + /// # Arguments + /// * `owner` - The owner of the NFT. + /// * `approved` - The new approved NFT controller. + /// * `token_id` - The NFT to approve. #[derive(Drop, starknet::Event)] struct Approval { owner: ContractAddress, @@ -53,6 +104,13 @@ mod ERC721 { token_id: u256 } + /// Emitted when `owner` enables or disables (approved) `operator` to manage + /// all of its assets. + /// + /// # Arguments + /// * `owner` - The owner of the NFT. + /// * `operator` - Address to add to the set of authorized operators. + /// * `approved` - `true` if the operator is approved, `false` to revoke approval. #[derive(Drop, starknet::Event)] struct ApprovalForAll { owner: ContractAddress, @@ -60,6 +118,12 @@ mod ERC721 { approved: bool } + /// Initializes the state of the ERC721 contract. This includes setting the + /// NFT name and symbol. + /// + /// # Arguments + /// * `name` - The NFT name. + /// * `symbol` - The NFT symbol. #[constructor] fn constructor(ref self: ContractState, name: felt252, symbol: felt252) { self.initializer(name, symbol); From c28c75875a43fa85a6163b1c1b6f113c1182d517 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Thu, 10 Aug 2023 02:47:19 -0400 Subject: [PATCH 078/246] Migrate upgrades (#603) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add upgrades to lib * add proxy mod * update cairo * update Cargo * fix test command * add upgrades * fix spelling * add upgrade test * fix spelling * add upgrade_and_call * update cairo * update Cargo * add events * tidy up code * fix format * update cairo to alpha7 * update Cargo * fix expected syntax * fix conflict * update expected syntax * formatting * update cairo to rc0 * update Cargo * fix import * refactor mocks * start test refactor * simplify upgrades, add upgrade_and_call * refactor mocks * refactor upgrades * refactor tests * fix imports, add impl * fix imports * fix formatting * add abis to mocks * refactor tests with dispatchers * fix formatting * start migration * update syntax * update syntax in mocks * fix tests * add line * remove line * add upgradeable trait * replace interface impl with trait impl * fix formatting * add assertion * add tests for events * Apply suggestions from code review Co-authored-by: Eric Nordelo * move logic to internal impl * add interface * update paths * fix formatting * simplify imports * Apply suggestions from code review Co-authored-by: Eric Nordelo * Apply suggestions from code review Co-authored-by: Martín Triay * remove upgrade_and_call, test event in separate test * remove upgrade_and_call * remove unused import * reorder imports, tidy up code --------- Co-authored-by: Eric Nordelo Co-authored-by: Martín Triay --- src/lib.cairo | 1 + src/tests.cairo | 1 + src/tests/mocks.cairo | 2 + src/tests/mocks/upgrades_v1.cairo | 55 ++++++++++++ src/tests/mocks/upgrades_v2.cairo | 64 +++++++++++++ src/tests/upgrades.cairo | 1 + src/tests/upgrades/test_upgradeable.cairo | 104 ++++++++++++++++++++++ src/upgrades.cairo | 2 + src/upgrades/interface.cairo | 6 ++ src/upgrades/upgradeable.cairo | 29 ++++++ 10 files changed, 265 insertions(+) create mode 100644 src/tests/mocks/upgrades_v1.cairo create mode 100644 src/tests/mocks/upgrades_v2.cairo create mode 100644 src/tests/upgrades.cairo create mode 100644 src/tests/upgrades/test_upgradeable.cairo create mode 100644 src/upgrades.cairo create mode 100644 src/upgrades/interface.cairo create mode 100644 src/upgrades/upgradeable.cairo diff --git a/src/lib.cairo b/src/lib.cairo index 235e12392..ea35f72ca 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -3,6 +3,7 @@ mod account; mod introspection; mod security; mod token; +mod upgrades; mod utils; #[cfg(test)] diff --git a/src/tests.cairo b/src/tests.cairo index cc78a11ec..7145f5650 100644 --- a/src/tests.cairo +++ b/src/tests.cairo @@ -4,4 +4,5 @@ mod introspection; mod mocks; mod security; mod token; +mod upgrades; mod utils; diff --git a/src/tests/mocks.cairo b/src/tests/mocks.cairo index cb3112db1..11a30de47 100644 --- a/src/tests/mocks.cairo +++ b/src/tests/mocks.cairo @@ -17,3 +17,5 @@ mod snake721_mock; mod snake_accesscontrol_mock; mod snake_account_mock; mod src5_mocks; +mod upgrades_v1; +mod upgrades_v2; diff --git a/src/tests/mocks/upgrades_v1.cairo b/src/tests/mocks/upgrades_v1.cairo new file mode 100644 index 000000000..d851f59d9 --- /dev/null +++ b/src/tests/mocks/upgrades_v1.cairo @@ -0,0 +1,55 @@ +// This contract is a mock used to test the core functionality of the upgrade functions. +// The functions are NOT PROTECTED. +// DO NOT USE IN PRODUCTION. + +use array::ArrayTrait; +use starknet::ClassHash; + +#[starknet::interface] +trait IUpgradesV1 { + fn upgrade(ref self: TState, impl_hash: ClassHash); + fn set_value(ref self: TState, val: felt252); + fn get_value(self: @TState) -> felt252; + fn remove_selector(self: @TState); +} + +trait UpgradesV1Trait { + fn set_value(ref self: TState, val: felt252); + fn get_value(self: @TState) -> felt252; + fn remove_selector(self: @TState); +} + +#[starknet::contract] +mod UpgradesV1 { + use array::ArrayTrait; + use starknet::ClassHash; + use starknet::ContractAddress; + use openzeppelin::upgrades::interface::IUpgradeable; + use openzeppelin::upgrades::upgradeable::Upgradeable; + + #[storage] + struct Storage { + value: felt252 + } + + #[external(v0)] + impl UpgradeableImpl of IUpgradeable { + fn upgrade(ref self: ContractState, impl_hash: ClassHash) { + let mut unsafe_state = Upgradeable::unsafe_new_contract_state(); + Upgradeable::InternalImpl::_upgrade(ref unsafe_state, impl_hash); + } + } + + #[external(v0)] + impl UpgradesV1Impl of super::UpgradesV1Trait { + fn set_value(ref self: ContractState, val: felt252) { + self.value.write(val); + } + + fn get_value(self: @ContractState) -> felt252 { + self.value.read() + } + + fn remove_selector(self: @ContractState) {} + } +} diff --git a/src/tests/mocks/upgrades_v2.cairo b/src/tests/mocks/upgrades_v2.cairo new file mode 100644 index 000000000..f0ad60ee7 --- /dev/null +++ b/src/tests/mocks/upgrades_v2.cairo @@ -0,0 +1,64 @@ +// This contract is a mock used to test the core functionality of the upgrade functions. +// The functions are NOT PROTECTED. +// DO NOT USE IN PRODUCTION. + +use array::ArrayTrait; +use starknet::ClassHash; + +#[starknet::interface] +trait IUpgradesV2 { + fn upgrade(ref self: TState, impl_hash: ClassHash); + fn set_value(ref self: TState, val: felt252); + fn set_value2(ref self: TState, val: felt252); + fn get_value(self: @TState) -> felt252; + fn get_value2(self: @TState) -> felt252; +} + +trait UpgradesV2Trait { + fn set_value(ref self: TState, val: felt252); + fn set_value2(ref self: TState, val: felt252); + fn get_value(self: @TState) -> felt252; + fn get_value2(self: @TState) -> felt252; +} + +#[starknet::contract] +mod UpgradesV2 { + use array::ArrayTrait; + use starknet::ClassHash; + use starknet::ContractAddress; + use openzeppelin::upgrades::interface::IUpgradeable; + use openzeppelin::upgrades::upgradeable::Upgradeable; + + #[storage] + struct Storage { + value: felt252, + value2: felt252, + } + + #[external(v0)] + impl UpgradeableImpl of IUpgradeable { + fn upgrade(ref self: ContractState, impl_hash: ClassHash) { + let mut unsafe_state = Upgradeable::unsafe_new_contract_state(); + Upgradeable::InternalImpl::_upgrade(ref unsafe_state, impl_hash); + } + } + + #[external(v0)] + impl UpgradesV2Impl of super::UpgradesV2Trait { + fn set_value(ref self: ContractState, val: felt252) { + self.value.write(val); + } + + fn set_value2(ref self: ContractState, val: felt252) { + self.value2.write(val); + } + + fn get_value(self: @ContractState) -> felt252 { + self.value.read() + } + + fn get_value2(self: @ContractState) -> felt252 { + self.value2.read() + } + } +} diff --git a/src/tests/upgrades.cairo b/src/tests/upgrades.cairo new file mode 100644 index 000000000..fe66e01e3 --- /dev/null +++ b/src/tests/upgrades.cairo @@ -0,0 +1 @@ +mod test_upgradeable; diff --git a/src/tests/upgrades/test_upgradeable.cairo b/src/tests/upgrades/test_upgradeable.cairo new file mode 100644 index 000000000..66de7a2a7 --- /dev/null +++ b/src/tests/upgrades/test_upgradeable.cairo @@ -0,0 +1,104 @@ +use array::ArrayTrait; +use openzeppelin::tests::mocks::upgrades_v1::IUpgradesV1Dispatcher; +use openzeppelin::tests::mocks::upgrades_v1::IUpgradesV1DispatcherTrait; +use openzeppelin::tests::mocks::upgrades_v2::IUpgradesV2Dispatcher; +use openzeppelin::tests::mocks::upgrades_v2::IUpgradesV2DispatcherTrait; +use openzeppelin::tests::mocks::upgrades_v1::UpgradesV1; +use openzeppelin::tests::mocks::upgrades_v2::UpgradesV2; +use openzeppelin::tests::utils; +use openzeppelin::upgrades::upgradeable::Upgradeable::Upgraded; +use option::OptionTrait; +use starknet::ClassHash; +use starknet::ContractAddress; +use starknet::Felt252TryIntoClassHash; +use starknet::class_hash_const; +use starknet::contract_address_const; +use starknet::testing; +use traits::TryInto; + +const VALUE: felt252 = 123; + +fn V2_CLASS_HASH() -> ClassHash { + UpgradesV2::TEST_CLASS_HASH.try_into().unwrap() +} + +fn CLASS_HASH_ZERO() -> ClassHash { + class_hash_const::<0>() +} + +fn ZERO() -> ContractAddress { + contract_address_const::<0>() +} + +// +// Setup +// + +fn deploy_v1() -> IUpgradesV1Dispatcher { + let calldata = array![]; + let address = utils::deploy(UpgradesV1::TEST_CLASS_HASH, calldata); + IUpgradesV1Dispatcher { contract_address: address } +} + +// +// upgrade +// + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('Class hash cannot be zero', 'ENTRYPOINT_FAILED', ))] +fn test_upgrade_with_class_hash_zero() { + let v1 = deploy_v1(); + v1.upgrade(CLASS_HASH_ZERO()); +} + +#[test] +#[available_gas(2000000)] +fn test_upgraded_event() { + let v1 = deploy_v1(); + v1.upgrade(V2_CLASS_HASH()); + + let event = testing::pop_log::(v1.contract_address).unwrap(); + assert(event.class_hash == V2_CLASS_HASH(), 'Invalid class hash'); +} + +#[test] +#[available_gas(2000000)] +fn test_new_selector_after_upgrade() { + let v1 = deploy_v1(); + + v1.upgrade(V2_CLASS_HASH()); + let v2 = IUpgradesV2Dispatcher { contract_address: v1.contract_address }; + + v2.set_value2(VALUE); + assert(v2.get_value2() == VALUE, 'New selector should be callable'); +} + +#[test] +#[available_gas(2000000)] +fn test_state_persists_after_upgrade() { + let v1 = deploy_v1(); + v1.set_value(VALUE); + + v1.upgrade(V2_CLASS_HASH()); + let v2 = IUpgradesV2Dispatcher { contract_address: v1.contract_address }; + + assert(v2.get_value() == VALUE, 'Should keep state after upgrade'); +} + +#[test] +#[available_gas(2000000)] +fn test_remove_selector_passes_in_v1() { + let v1 = deploy_v1(); + v1.remove_selector(); +} + +#[test] +#[available_gas(2000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +fn test_remove_selector_fails_in_v2() { + let v1 = deploy_v1(); + v1.upgrade(V2_CLASS_HASH()); + // We use the v1 dispatcher because remove_selector is not in v2 interface + v1.remove_selector(); +} diff --git a/src/upgrades.cairo b/src/upgrades.cairo new file mode 100644 index 000000000..18d78fc73 --- /dev/null +++ b/src/upgrades.cairo @@ -0,0 +1,2 @@ +mod upgradeable; +mod interface; diff --git a/src/upgrades/interface.cairo b/src/upgrades/interface.cairo new file mode 100644 index 000000000..1857c003d --- /dev/null +++ b/src/upgrades/interface.cairo @@ -0,0 +1,6 @@ +use starknet::ClassHash; + +#[starknet::interface] +trait IUpgradeable { + fn upgrade(ref self: TState, impl_hash: ClassHash); +} diff --git a/src/upgrades/upgradeable.cairo b/src/upgrades/upgradeable.cairo new file mode 100644 index 000000000..444cf36b7 --- /dev/null +++ b/src/upgrades/upgradeable.cairo @@ -0,0 +1,29 @@ +#[starknet::contract] +mod Upgradeable { + use starknet::ClassHash; + use starknet::SyscallResult; + use zeroable::Zeroable; + + #[storage] + struct Storage {} + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + Upgraded: Upgraded + } + + #[derive(Drop, starknet::Event)] + struct Upgraded { + class_hash: ClassHash + } + + #[generate_trait] + impl InternalImpl of InternalState { + fn _upgrade(ref self: ContractState, new_class_hash: ClassHash) { + assert(!new_class_hash.is_zero(), 'Class hash cannot be zero'); + starknet::replace_class_syscall(new_class_hash).unwrap_syscall(); + self.emit(Upgraded { class_hash: new_class_hash }); + } + } +} From 7dbfca98a754c94b3cceea23798d1b3fc7730bda Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 10 Aug 2023 16:18:26 -0400 Subject: [PATCH 079/246] finish basic comments for interface --- src/token/erc721/erc721.cairo | 105 ++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index f217bb73b..befbebe0a 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -135,6 +135,14 @@ mod ERC721 { #[external(v0)] impl SRC5Impl of ISRC5 { + /// Checks if the contract supports a specific interface as defined by + /// `interface_id`. + /// See: https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md + /// + /// # Arguments + /// * `interface_id` - The calculated interface ID to query. + /// # Returns + /// `true` if the interface is supported. fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { let unsafe_state = src5::SRC5::unsafe_new_contract_state(); src5::SRC5::SRC5Impl::supports_interface(@unsafe_state, interface_id) @@ -143,6 +151,8 @@ mod ERC721 { #[external(v0)] impl SRC5CamelImpl of ISRC5Camel { + /// Camel case support. + /// See [supports_interface](supports_interface). fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { let unsafe_state = src5::SRC5::unsafe_new_contract_state(); src5::SRC5::SRC5CamelImpl::supportsInterface(@unsafe_state, interfaceId) @@ -151,14 +161,29 @@ mod ERC721 { #[external(v0)] impl ERC721MetadataImpl of interface::IERC721Metadata { + /// Returns the NFT name. + /// + /// # Returns + /// NFT name. fn name(self: @ContractState) -> felt252 { self._name.read() } + /// Returns the NFT symbol. + /// + /// # Returns + /// NFT symbol. fn symbol(self: @ContractState) -> felt252 { self._symbol.read() } + /// Returns the Uniform Resource Identifier (URI) for `token_id` token. + /// If the URI is not set for the `token_id`, the return value will be `0`. + /// + /// # Arguments + /// `token_id` - The token to query. + /// # Returns + /// URI of `token_id`. fn token_uri(self: @ContractState, token_id: u256) -> felt252 { assert(self._exists(token_id), 'ERC721: invalid token ID'); self._token_uri.read(token_id) @@ -167,6 +192,8 @@ mod ERC721 { #[external(v0)] impl ERC721MetadataCamelOnlyImpl of interface::IERC721MetadataCamelOnly { + /// Camel case support. + /// See [token_uri](token_uri). fn tokenUri(self: @ContractState, tokenId: u256) -> felt252 { assert(self._exists(tokenId), 'ERC721: invalid token ID'); self._token_uri.read(tokenId) @@ -175,26 +202,56 @@ mod ERC721 { #[external(v0)] impl ERC721Impl of interface::IERC721 { + /// Returns the number of NFTs owned by `account`. + /// + /// # Arguments + /// `account` - The address to query. + /// # Returns + /// Number of NFTs owned by `account`. fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { assert(!account.is_zero(), 'ERC721: invalid account'); self._balances.read(account) } + /// Returns the owner address of `token_id`. + /// + /// # Arguments + /// `token_id` - The token to query. + /// # Returns + /// Owner address of `token_id`. fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress { self._owner_of(token_id) } + /// Returns the address approved for `token_id`. + /// + /// # Arguments + /// `token_id` - The token ID to query. + /// # Returns + /// Approved address for the `token_id` NFT, or `0` if there is none. fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { assert(self._exists(token_id), 'ERC721: invalid token ID'); self._token_approvals.read(token_id) } + /// Query if `operator` is an authorized operator for `owner`. + /// + /// # Arguments + /// `owner` - The address that owns the NFT. + /// `operator` - The address that acts on behalf of the `owner`. + /// # Returns + /// `true` if `operator` is an authorized operator for `owner`. fn is_approved_for_all( self: @ContractState, owner: ContractAddress, operator: ContractAddress ) -> bool { self._operator_approvals.read((owner, operator)) } + /// Change or reaffirm the approved address for an NFT. + /// + /// # Arguments + /// `to` - The new approved NFT controller. + /// `token_id` - The NFT to approve. fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) { let owner = self._owner_of(token_id); @@ -206,12 +263,34 @@ mod ERC721 { self._approve(to, token_id); } + /// Enable or disable approval for `operator` to manage all of the + /// caller's assets. + /// + /// Emits an [Approval](Approval) event. + /// + /// # Arguments + /// `operator` - Address to add to the set of authorized operators. + /// `approved` - `true` if operator is approved, `false` to revoke approval. fn set_approval_for_all( ref self: ContractState, operator: ContractAddress, approved: bool ) { self._set_approval_for_all(get_caller_address(), operator, approved) } + /// Transfer ownership of `token_id` from `from` to `to`. + /// + /// Note that the caller is responsible to confirm that the recipient is + /// capable of receiving ERC721 transfers or else they may be permanently lost. + /// Usage of [safe_transfer_from](safe_transfer_from) prevents loss, though + /// the caller must understand this adds an external call which potentially + /// creates a reentrancy vulnerability. + /// + /// Emits a [Transfer](Transfer) event. + /// + /// # Arguments + /// `from` - The current owner of the NFT. + /// `to` - The new owner. + /// `token_id` - The NFT to transfer. fn transfer_from( ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 ) { @@ -222,6 +301,18 @@ mod ERC721 { self._transfer(from, to, token_id); } + /// Safely transfer ownership of `token_id` from `from` to `to`, checking first + /// that `to` is aware of the ERC721 protocol to prevent tokens being locked + /// forever. For information regarding how contracts communicate their + /// awareness of the ERC721 protocol, see [ERC721Received](TODO!). + /// + /// Emits a [Transfer](Transfer) event. + /// + /// # Arguments + /// `from` - The current owner of the NFT. + /// `to` - The new owner. + /// `token_id` - The NFT to transfer. + /// `data` - Additional data with no specified format, sent in call to `to` fn safe_transfer_from( ref self: ContractState, from: ContractAddress, @@ -239,34 +330,48 @@ mod ERC721 { #[external(v0)] impl ERC721CamelOnlyImpl of interface::IERC721CamelOnly { + /// Camel case support. + /// See [balance_of](balance_of). fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 { ERC721Impl::balance_of(self, account) } + /// Camel case support. + /// See [owner_of](owner_of). fn ownerOf(self: @ContractState, tokenId: u256) -> ContractAddress { ERC721Impl::owner_of(self, tokenId) } + /// Camel case support. + /// See [get_approved](get_approved). fn getApproved(self: @ContractState, tokenId: u256) -> ContractAddress { ERC721Impl::get_approved(self, tokenId) } + /// Camel case support. + /// See [is_approved_for_all](is_approved_for_all). fn isApprovedForAll( self: @ContractState, owner: ContractAddress, operator: ContractAddress ) -> bool { ERC721Impl::is_approved_for_all(self, owner, operator) } + /// Camel case support. + /// See [set_approval_for_all](set_approval_for_all). fn setApprovalForAll(ref self: ContractState, operator: ContractAddress, approved: bool) { ERC721Impl::set_approval_for_all(ref self, operator, approved) } + /// Camel case support. + /// See [transfer_from](transfer_from). fn transferFrom( ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256 ) { ERC721Impl::transfer_from(ref self, from, to, tokenId) } + /// Camel case support. + /// See [safe_transfer_from](safe_transfer_from). fn safeTransferFrom( ref self: ContractState, from: ContractAddress, From f9c30da2d70762660e33088965006b43f07ccfcb Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 11 Aug 2023 20:11:48 +0200 Subject: [PATCH 080/246] Add testing for Ownable events (#675) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add tests for ownable events * feat: improve tests * Update src/tests/utils.cairo Co-authored-by: Martín Triay --------- Co-authored-by: Martín Triay --- src/tests/access/test_accesscontrol.cairo | 26 ++-------- .../access/test_dual_accesscontrol.cairo | 29 ++++------- src/tests/access/test_ownable.cairo | 48 ++++++++++++++----- src/tests/utils.cairo | 22 ++++++++- src/tests/utils/constants.cairo | 33 +++++++++++++ 5 files changed, 100 insertions(+), 58 deletions(-) create mode 100644 src/tests/utils/constants.cairo diff --git a/src/tests/access/test_accesscontrol.cairo b/src/tests/access/test_accesscontrol.cairo index 86ef607e0..6099c551f 100644 --- a/src/tests/access/test_accesscontrol.cairo +++ b/src/tests/access/test_accesscontrol.cairo @@ -4,33 +4,13 @@ use openzeppelin::access::accesscontrol::AccessControl::InternalImpl; use openzeppelin::access::accesscontrol::AccessControl; use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; use openzeppelin::access::accesscontrol::interface::IACCESSCONTROL_ID; +use openzeppelin::tests::utils::constants::{ + ADMIN, AUTHORIZED, OTHER, OTHER_ADMIN, ROLE, OTHER_ROLE, ZERO +}; use starknet::ContractAddress; use starknet::contract_address_const; use starknet::testing; -const ROLE: felt252 = 41; -const OTHER_ROLE: felt252 = 42; - -fn ZERO() -> ContractAddress { - contract_address_const::<0>() -} - -fn ADMIN() -> ContractAddress { - contract_address_const::<1>() -} - -fn AUTHORIZED() -> ContractAddress { - contract_address_const::<2>() -} - -fn OTHER() -> ContractAddress { - contract_address_const::<3>() -} - -fn OTHER_ADMIN() -> ContractAddress { - contract_address_const::<4>() -} - // // Setup // diff --git a/src/tests/access/test_dual_accesscontrol.cairo b/src/tests/access/test_dual_accesscontrol.cairo index a20657902..2d6a17be4 100644 --- a/src/tests/access/test_dual_accesscontrol.cairo +++ b/src/tests/access/test_dual_accesscontrol.cairo @@ -1,37 +1,24 @@ use array::ArrayTrait; use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; -use openzeppelin::access::accesscontrol::dual_accesscontrol::DualCaseAccessControl; -use openzeppelin::access::accesscontrol::dual_accesscontrol::DualCaseAccessControlTrait; use openzeppelin::access::accesscontrol::interface::IACCESSCONTROL_ID; -use openzeppelin::access::accesscontrol::interface::IAccessControlCamelDispatcher; -use openzeppelin::access::accesscontrol::interface::IAccessControlCamelDispatcherTrait; use openzeppelin::access::accesscontrol::interface::IAccessControlDispatcher; +use openzeppelin::access::accesscontrol::interface::IAccessControlCamelDispatcher; use openzeppelin::access::accesscontrol::interface::IAccessControlDispatcherTrait; -use openzeppelin::tests::mocks::accesscontrol_panic_mock::CamelAccessControlPanicMock; -use openzeppelin::tests::mocks::accesscontrol_panic_mock::SnakeAccessControlPanicMock; +use openzeppelin::access::accesscontrol::interface::IAccessControlCamelDispatcherTrait; +use openzeppelin::access::accesscontrol::dual_accesscontrol::DualCaseAccessControlTrait; +use openzeppelin::access::accesscontrol::dual_accesscontrol::DualCaseAccessControl; +use openzeppelin::tests::mocks::snake_accesscontrol_mock::SnakeAccessControlMock; use openzeppelin::tests::mocks::camel_accesscontrol_mock::CamelAccessControlMock; +use openzeppelin::tests::mocks::accesscontrol_panic_mock::SnakeAccessControlPanicMock; +use openzeppelin::tests::mocks::accesscontrol_panic_mock::CamelAccessControlPanicMock; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; -use openzeppelin::tests::mocks::snake_accesscontrol_mock::SnakeAccessControlMock; +use openzeppelin::tests::utils::constants::{ADMIN, AUTHORIZED, ROLE}; use openzeppelin::tests::utils; use openzeppelin::utils::serde::SerializedAppend; use starknet::ContractAddress; use starknet::contract_address_const; use starknet::testing::set_contract_address; -// -// Constants -// - -const ROLE: felt252 = 41; - -fn ADMIN() -> ContractAddress { - contract_address_const::<10>() -} - -fn AUTHORIZED() -> ContractAddress { - contract_address_const::<20>() -} - // // Setup // diff --git a/src/tests/access/test_ownable.cairo b/src/tests/access/test_ownable.cairo index d88d0f786..c2e6b8682 100644 --- a/src/tests/access/test_ownable.cairo +++ b/src/tests/access/test_ownable.cairo @@ -1,25 +1,17 @@ +use openzeppelin::access::ownable::Ownable; use openzeppelin::access::ownable::Ownable::InternalImpl; use openzeppelin::access::ownable::Ownable::OwnableCamelOnlyImpl; use openzeppelin::access::ownable::Ownable::OwnableImpl; +use openzeppelin::access::ownable::Ownable::OwnershipTransferred; use openzeppelin::access::ownable::Ownable::_owner::InternalContractStateTrait; -use openzeppelin::access::ownable::Ownable; +use openzeppelin::tests::utils; +use openzeppelin::tests::utils::constants::{ZERO, OTHER, OWNER}; +use option::OptionTrait; use starknet::ContractAddress; use starknet::contract_address_const; use starknet::testing; use zeroable::Zeroable; -fn ZERO() -> ContractAddress { - contract_address_const::<0>() -} - -fn OWNER() -> ContractAddress { - contract_address_const::<10>() -} - -fn OTHER() -> ContractAddress { - contract_address_const::<20>() -} - // // Setup // @@ -31,6 +23,7 @@ fn STATE() -> Ownable::ContractState { fn setup() -> Ownable::ContractState { let mut state = STATE(); InternalImpl::initializer(ref state, OWNER()); + testing::pop_log_raw(ZERO()); state } @@ -44,6 +37,9 @@ fn test_initializer() { let mut state = STATE(); assert(state._owner.read().is_zero(), 'Should be zero'); InternalImpl::initializer(ref state, OWNER()); + + assert_event_ownership_transferred(ZERO(), OWNER()); + assert(state._owner.read() == OWNER(), 'Owner should be set'); } @@ -85,6 +81,9 @@ fn test_assert_only_owner_when_caller_zero() { fn test__transfer_ownership() { let mut state = setup(); InternalImpl::_transfer_ownership(ref state, OTHER()); + + assert_event_ownership_transferred(OWNER(), OTHER()); + assert(state._owner.read() == OTHER(), 'Owner should be OTHER'); } @@ -98,6 +97,9 @@ fn test_transfer_ownership() { let mut state = setup(); testing::set_caller_address(OWNER()); OwnableImpl::transfer_ownership(ref state, OTHER()); + + assert_event_ownership_transferred(OWNER(), OTHER()); + assert(OwnableImpl::owner(@state) == OTHER(), 'Should transfer ownership'); } @@ -133,6 +135,9 @@ fn test_transferOwnership() { let mut state = setup(); testing::set_caller_address(OWNER()); OwnableCamelOnlyImpl::transferOwnership(ref state, OTHER()); + + assert_event_ownership_transferred(OWNER(), OTHER()); + assert(OwnableImpl::owner(@state) == OTHER(), 'Should transfer ownership'); } @@ -172,6 +177,9 @@ fn test_renounce_ownership() { let mut state = setup(); testing::set_caller_address(OWNER()); OwnableImpl::renounce_ownership(ref state); + + assert_event_ownership_transferred(OWNER(), ZERO()); + assert(OwnableImpl::owner(@state) == ZERO(), 'Should renounce ownership'); } @@ -198,6 +206,9 @@ fn test_renounceOwnership() { let mut state = setup(); testing::set_caller_address(OWNER()); OwnableCamelOnlyImpl::renounceOwnership(ref state); + + assert_event_ownership_transferred(OWNER(), ZERO()); + assert(OwnableImpl::owner(@state) == ZERO(), 'Should renounce ownership'); } @@ -217,3 +228,14 @@ fn test_renounceOwnership_from_nonowner() { testing::set_caller_address(OTHER()); OwnableCamelOnlyImpl::renounceOwnership(ref state); } + +// +// Helpers +// + +fn assert_event_ownership_transferred(previous_owner: ContractAddress, new_owner: ContractAddress) { + let event = utils::pop_log::(ZERO()).unwrap(); + assert(event.previous_owner == previous_owner, 'Invalid previous_owner'); + assert(event.new_owner == new_owner, 'Invalid new_owner'); + utils::assert_no_events_left(ZERO()); +} diff --git a/src/tests/utils.cairo b/src/tests/utils.cairo index 5eb3d1001..084e685e7 100644 --- a/src/tests/utils.cairo +++ b/src/tests/utils.cairo @@ -1,8 +1,12 @@ +mod constants; + use array::ArrayTrait; +use array::SpanTrait; use core::result::ResultTrait; use option::OptionTrait; -use starknet::ContractAddress; use starknet::class_hash::Felt252TryIntoClassHash; +use starknet::ContractAddress; +use starknet::testing; use traits::TryInto; fn deploy(contract_class_hash: felt252, calldata: Array) -> ContractAddress { @@ -12,3 +16,19 @@ fn deploy(contract_class_hash: felt252, calldata: Array) -> ContractAdd .unwrap(); address } + +/// Pop the earliest unpopped logged event for the contract as the requested type +/// and checks there's no more data left on the event, preventing missing extra params. +/// Indexed event members are currently not supported, so they are ignored. +fn pop_log, impl TEvent: starknet::Event>( + address: ContractAddress +) -> Option { + let (mut keys, mut data) = testing::pop_log_raw(address)?; + let ret = starknet::Event::deserialize(ref keys, ref data); + assert(data.is_empty(), 'Event has extra data'); + ret +} + +fn assert_no_events_left(address: ContractAddress) { + assert(testing::pop_log_raw(address).is_none(), 'Events remaining on queue'); +} diff --git a/src/tests/utils/constants.cairo b/src/tests/utils/constants.cairo new file mode 100644 index 000000000..84cc4eae4 --- /dev/null +++ b/src/tests/utils/constants.cairo @@ -0,0 +1,33 @@ +use starknet::ContractAddress; +use starknet::contract_address_const; + +const ROLE: felt252 = 'ROLE'; +const OTHER_ROLE: felt252 = 'OTHER_ROLE'; + +fn ADMIN() -> ContractAddress { + contract_address_const::<'ADMIN'>() +} + +fn AUTHORIZED() -> ContractAddress { + contract_address_const::<'AUTHORIZED'>() +} + +fn ZERO() -> ContractAddress { + contract_address_const::<0>() +} + +fn OWNER() -> ContractAddress { + contract_address_const::<'OWNER'>() +} + +fn NEW_OWNER() -> ContractAddress { + contract_address_const::<'NEW_OWNER'>() +} + +fn OTHER() -> ContractAddress { + contract_address_const::<'OTHER'>() +} + +fn OTHER_ADMIN() -> ContractAddress { + contract_address_const::<'OTHER_ADMIN'>() +} From 457b98b10080820ad041980d71b24e7856766336 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Fri, 11 Aug 2023 14:20:47 -0400 Subject: [PATCH 081/246] Add account events (#687) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add tests for ownable events * define account events * add event to set_public_key * add event test * change param name * fix event emit param * fix formatting * add tests for events * add default checks in tests * feat: improve tests * Apply suggestions from code review Co-authored-by: Eric Nordelo * reorder imports, remove Account prefix * fix events * fix formatting * Update src/tests/utils.cairo Co-authored-by: Martín Triay * add event checks, use ZERO from constants * fix formatting --------- Co-authored-by: Eric Nordelo Co-authored-by: Martín Triay --- src/account/account.cairo | 30 +++++++++-- src/tests/account/test_account.cairo | 79 ++++++++++++++++++++++------ 2 files changed, 90 insertions(+), 19 deletions(-) diff --git a/src/account/account.cairo b/src/account/account.cairo index 8d8105521..8fffb55ea 100644 --- a/src/account/account.cairo +++ b/src/account/account.cairo @@ -49,6 +49,23 @@ mod Account { public_key: felt252 } + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + OwnerAdded: OwnerAdded, + OwnerRemoved: OwnerRemoved, + } + + #[derive(Drop, starknet::Event)] + struct OwnerAdded { + new_owner_guid: felt252 + } + + #[derive(Drop, starknet::Event)] + struct OwnerRemoved { + removed_owner_guid: felt252 + } + #[constructor] fn constructor(ref self: ContractState, _public_key: felt252) { self.initializer(_public_key); @@ -131,7 +148,8 @@ mod Account { fn set_public_key(ref self: ContractState, new_public_key: felt252) { assert_only_self(); - self.public_key.write(new_public_key); + self.emit(OwnerRemoved { removed_owner_guid: self.public_key.read() }); + self._set_public_key(new_public_key); } } @@ -142,8 +160,7 @@ mod Account { } fn setPublicKey(ref self: ContractState, newPublicKey: felt252) { - assert_only_self(); - self.public_key.write(newPublicKey); + PublicKeyImpl::set_public_key(ref self, newPublicKey); } } @@ -166,7 +183,7 @@ mod Account { fn initializer(ref self: ContractState, _public_key: felt252) { let mut unsafe_state = SRC5::unsafe_new_contract_state(); SRC5::InternalImpl::register_interface(ref unsafe_state, interface::ISRC6_ID); - self.public_key.write(_public_key); + self._set_public_key(_public_key); } fn validate_transaction(self: @ContractState) -> felt252 { @@ -177,6 +194,11 @@ mod Account { starknet::VALIDATED } + fn _set_public_key(ref self: ContractState, new_public_key: felt252) { + self.public_key.write(new_public_key); + self.emit(OwnerAdded { new_owner_guid: new_public_key }); + } + fn _is_valid_signature( self: @ContractState, hash: felt252, signature: Span ) -> bool { diff --git a/src/tests/account/test_account.cairo b/src/tests/account/test_account.cairo index b2fe45ceb..d66e9fbfe 100644 --- a/src/tests/account/test_account.cairo +++ b/src/tests/account/test_account.cairo @@ -1,5 +1,9 @@ use array::ArrayTrait; use core::traits::Into; +use openzeppelin::account::Account::OwnerAdded; +use openzeppelin::account::Account::OwnerRemoved; +use openzeppelin::account::Account::PublicKeyCamelImpl; +use openzeppelin::account::Account::PublicKeyImpl; use openzeppelin::account::Account; use openzeppelin::account::AccountABIDispatcher; use openzeppelin::account::AccountABIDispatcherTrait; @@ -8,6 +12,7 @@ use openzeppelin::account::TRANSACTION_VERSION; use openzeppelin::account::interface::ISRC6_ID; use openzeppelin::introspection::interface::ISRC5_ID; use openzeppelin::tests::utils; +use openzeppelin::tests::utils::constants::ZERO; use openzeppelin::token::erc20::ERC20; use openzeppelin::token::erc20::interface::IERC20Dispatcher; use openzeppelin::token::erc20::interface::IERC20DispatcherTrait; @@ -100,9 +105,12 @@ fn deploy_erc20(recipient: ContractAddress, initial_supply: u256) -> IERC20Dispa fn test_constructor() { let mut state = STATE(); Account::constructor(ref state, PUBLIC_KEY); - assert( - Account::PublicKeyImpl::get_public_key(@state) == PUBLIC_KEY, 'Should return public key' - ); + + let event = testing::pop_log::(ZERO()).unwrap(); + assert(event.new_owner_guid == PUBLIC_KEY, 'Invalid owner key'); + utils::assert_no_events_left(ZERO()); + + assert(PublicKeyImpl::get_public_key(@state) == PUBLIC_KEY, 'Should return public key'); } // @@ -149,7 +157,7 @@ fn test_is_valid_signature() { let mut good_signature = array![data.r, data.s]; let mut bad_signature = array![0x987, 0x564]; - Account::PublicKeyImpl::set_public_key(ref state, data.public_key); + PublicKeyImpl::set_public_key(ref state, data.public_key); let is_valid = Account::SRC6Impl::is_valid_signature(@state, hash, good_signature); assert(is_valid == starknet::VALIDATED, 'Should accept valid signature'); @@ -168,7 +176,7 @@ fn test_isValidSignature() { let mut good_signature = array![data.r, data.s]; let mut bad_signature = array![0x987, 0x564]; - Account::PublicKeyImpl::set_public_key(ref state, data.public_key); + PublicKeyImpl::set_public_key(ref state, data.public_key); let is_valid = Account::SRC6CamelOnlyImpl::isValidSignature(@state, hash, good_signature); assert(is_valid == starknet::VALIDATED, 'Should accept valid signature'); @@ -437,9 +445,21 @@ fn test_public_key_setter_and_getter() { testing::set_contract_address(ACCOUNT_ADDRESS()); testing::set_caller_address(ACCOUNT_ADDRESS()); - Account::PublicKeyImpl::set_public_key(ref state, NEW_PUBKEY); + // Check default + let public_key = PublicKeyImpl::get_public_key(@state); + assert(public_key == 0, 'Should be zero'); + + // Set key + PublicKeyImpl::set_public_key(ref state, NEW_PUBKEY); + + let event = testing::pop_log::(ACCOUNT_ADDRESS()).unwrap(); + assert(event.removed_owner_guid == 0, 'Invalid old owner key'); - let public_key = Account::PublicKeyImpl::get_public_key(@state); + let event = testing::pop_log::(ACCOUNT_ADDRESS()).unwrap(); + assert(event.new_owner_guid == NEW_PUBKEY, 'Invalid new owner key'); + utils::assert_no_events_left(ACCOUNT_ADDRESS()); + + let public_key = PublicKeyImpl::get_public_key(@state); assert(public_key == NEW_PUBKEY, 'Should update key'); } @@ -452,7 +472,7 @@ fn test_public_key_setter_different_account() { testing::set_contract_address(ACCOUNT_ADDRESS()); testing::set_caller_address(caller); - Account::PublicKeyImpl::set_public_key(ref state, NEW_PUBKEY); + PublicKeyImpl::set_public_key(ref state, NEW_PUBKEY); } // @@ -466,9 +486,21 @@ fn test_public_key_setter_and_getter_camel() { testing::set_contract_address(ACCOUNT_ADDRESS()); testing::set_caller_address(ACCOUNT_ADDRESS()); - Account::PublicKeyCamelImpl::setPublicKey(ref state, NEW_PUBKEY); + // Check default + let public_key = PublicKeyCamelImpl::getPublicKey(@state); + assert(public_key == 0, 'Should be zero'); + + // Set key + PublicKeyCamelImpl::setPublicKey(ref state, NEW_PUBKEY); + + let event = testing::pop_log::(ACCOUNT_ADDRESS()).unwrap(); + assert(event.removed_owner_guid == 0, 'Invalid old owner key'); - let public_key = Account::PublicKeyCamelImpl::getPublicKey(@state); + let event = testing::pop_log::(ACCOUNT_ADDRESS()).unwrap(); + assert(event.new_owner_guid == NEW_PUBKEY, 'Invalid new owner key'); + utils::assert_no_events_left(ACCOUNT_ADDRESS()); + + let public_key = PublicKeyCamelImpl::getPublicKey(@state); assert(public_key == NEW_PUBKEY, 'Should update key'); } @@ -481,7 +513,7 @@ fn test_public_key_setter_different_account_camel() { testing::set_contract_address(ACCOUNT_ADDRESS()); testing::set_caller_address(caller); - Account::PublicKeyCamelImpl::setPublicKey(ref state, NEW_PUBKEY); + PublicKeyCamelImpl::setPublicKey(ref state, NEW_PUBKEY); } // @@ -493,9 +525,12 @@ fn test_public_key_setter_different_account_camel() { fn test_initializer() { let mut state = STATE(); Account::InternalImpl::initializer(ref state, PUBLIC_KEY); - assert( - Account::PublicKeyImpl::get_public_key(@state) == PUBLIC_KEY, 'Should return public key' - ); + + let event = testing::pop_log::(ZERO()).unwrap(); + assert(event.new_owner_guid == PUBLIC_KEY, 'Invalid owner key'); + utils::assert_no_events_left(ZERO()); + + assert(PublicKeyImpl::get_public_key(@state) == PUBLIC_KEY, 'Should return public key'); } #[test] @@ -527,7 +562,7 @@ fn test__is_valid_signature() { let mut bad_signature = array![0x987, 0x564]; let mut invalid_length_signature = array![0x987]; - Account::PublicKeyImpl::set_public_key(ref state, data.public_key); + PublicKeyImpl::set_public_key(ref state, data.public_key); let is_valid = Account::InternalImpl::_is_valid_signature(@state, hash, good_signature.span()); assert(is_valid, 'Should accept valid signature'); @@ -540,3 +575,17 @@ fn test__is_valid_signature() { ); assert(!is_valid, 'Should reject invalid length'); } + +#[test] +#[available_gas(2000000)] +fn test__set_public_key() { + let mut state = STATE(); + Account::InternalImpl::_set_public_key(ref state, PUBLIC_KEY); + + let event = testing::pop_log::(ZERO()).unwrap(); + assert(event.new_owner_guid == PUBLIC_KEY, 'Invalid owner key'); + utils::assert_no_events_left(ZERO()); + + let public_key = PublicKeyImpl::get_public_key(@state); + assert(public_key == PUBLIC_KEY, 'Should update key'); +} From 5b766fbc829da95c48e875da9ae89111f57f69ff Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 13 Aug 2023 03:45:33 -0400 Subject: [PATCH 082/246] fix comments --- src/token/erc721/erc721.cairo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index befbebe0a..cd78c59c4 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -177,7 +177,7 @@ mod ERC721 { self._symbol.read() } - /// Returns the Uniform Resource Identifier (URI) for `token_id` token. + /// Returns the Uniform Resource Identifier (URI) for the `token_id` token. /// If the URI is not set for the `token_id`, the return value will be `0`. /// /// # Arguments @@ -312,7 +312,7 @@ mod ERC721 { /// `from` - The current owner of the NFT. /// `to` - The new owner. /// `token_id` - The NFT to transfer. - /// `data` - Additional data with no specified format, sent in call to `to` + /// `data` - Additional data with no specified format, sent in call to `to`. fn safe_transfer_from( ref self: ContractState, from: ContractAddress, From 258d62dcbcc2bfa7ecc30df3512182eae310753a Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 13 Aug 2023 03:46:13 -0400 Subject: [PATCH 083/246] finish ierc721 api --- docs/modules/ROOT/pages/erc721.adoc | 870 ++++++++++++---------------- 1 file changed, 357 insertions(+), 513 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 53d06d313..eb2f6ea91 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -1,7 +1,7 @@ = ERC721 The ERC721 token standard is a specification for https://docs.openzeppelin.com/contracts/4.x/tokens#different-kinds-of-tokens[non-fungible tokens], or more colloquially: NFTs. -The `ERC721.cairo` contract implements an approximation of https://eips.ethereum.org/EIPS/eip-721[EIP-721] in Cairo for StarkNet. +The `erc721.cairo` contract implements an approximation of https://eips.ethereum.org/EIPS/eip-721[EIP-721] in Cairo for StarkNet. == Table of Contents @@ -51,38 +51,40 @@ The `ERC721.cairo` contract implements an approximation of https://eips.ethereum == IERC721 -[,cairo] ----- -@contract_interface -namespace IERC721 { - func balanceOf(owner: felt) -> (balance: Uint256) { - } - - func ownerOf(tokenId: Uint256) -> (owner: felt) { - } - - func safeTransferFrom(from_: felt, to: felt, tokenId: Uint256, data_len: felt, data: felt*) { - } - - func transferFrom(from_: felt, to: felt, tokenId: Uint256) { - } - - func approve(approved: felt, tokenId: Uint256) { - } - - func setApprovalForAll(operator: felt, approved: felt) { - } - - func getApproved(tokenId: Uint256) -> (approved: felt) { - } - - func isApprovedForAll(owner: felt, operator: felt) -> (approved: felt) { - } - - --------------- IERC165 --------------- +[,rust] +---- +#[starknet::interface] +trait IERC721 { + fn balance_of(self: @TState, account: ContractAddress) -> u256; + fn owner_of(self: @TState, token_id: u256) -> ContractAddress; + fn transfer_from( + ref self: TState, + from: ContractAddress, + to: ContractAddress, + token_id: u256 + ); + fn safe_transfer_from( + ref self: TState, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span + ); + fn approve(ref self: TState, to: ContractAddress, token_id: u256); + fn set_approval_for_all( + ref self: TState, + operator: ContractAddress, + approved: bool + ); + fn get_approved(self: @TState, token_id: u256) -> ContractAddress; + fn is_approved_for_all( + self: @TState, owner: ContractAddress, operator: ContractAddress + ) -> bool; +} - func supportsInterface(interfaceId: felt) -> (success: felt) { - } +#[starknet::interface] +trait ISRC5 { + fn supports_interface(self: @TState, interface_id: felt252) -> bool; } ---- @@ -90,16 +92,16 @@ namespace IERC721 { Although StarkNet is not EVM compatible, this implementation aims to be as close as possible to the ERC721 standard in the following ways: -* It uses Cairo's `uint256` instead of `felt`. -* It returns `TRUE` as success. +* It uses Cairo's `u256` instead of `felt252`. +* It returns `true` as success. * It makes use of Cairo's short strings to simulate `name` and `symbol`. But some differences can still be found, such as: -* `tokenURI` returns a felt representation of the queried token's URI. +* `token_uri` returns a felt252 representation of the queried token's URI. The EIP721 standard, however, states that the return value should be of type string. If a token's URI is not set, the returned value is `0`. -Note that URIs cannot exceed 31 characters. +Note that URIs cannot exceed 31 characters at this time. See <>. * ``interface_id``s are hardcoded and initialized by the constructor. The hardcoded values derive from Solidity's selector calculations. @@ -124,117 +126,46 @@ In doing so, recipient contracts (both accounts and non-accounts) can be verifie == Usage -Use cases go from artwork, digital collectibles, physical property, and many more. - -To show a standard use case, we'll use the `ERC721Mintable` preset which allows for only the owner to `mint` and `burn` tokens. -To create a token you need to first deploy both Account and ERC721 contracts respectively. -As most StarkNet contracts, ERC721 expects to be called by another contract and it identifies it through `get_caller_address` (analogous to Solidity's `this.address`). -This is why we need an Account contract to interact with it. - -Considering that the ERC721 constructor method looks like this: - -[,cairo] ----- -func constructor( - name: felt, // Token name as Cairo short string - symbol: felt, // Token symbol as Cairo short string - owner: felt // Address designated as the contract owner -) { -} ----- - -Deployment of both contracts looks like this: - -[,python] ----- -account = await starknet.deploy( - "contracts/Account.cairo", - constructor_calldata=[signer.public_key] -) - -erc721 = await starknet.deploy( - "contracts/token/erc721/presets/ERC721Mintable.cairo", - constructor_calldata=[ - str_to_felt("Token"), # name - str_to_felt("TKN"), # symbol - account.contract_address # owner - ] -) ----- - -To mint a non-fungible token, send a transaction like this: - -[,python] ----- -signer = MockSigner(PRIVATE_KEY) -tokenId = uint(1) - -await signer.send_transaction( - account, erc721.contract_address, 'mint', [ - recipient_address, - *tokenId - ] -) ----- +TODO === Token Transfers -This library includes `transferFrom` and `safeTransferFrom` to transfer NFTs. -If using `transferFrom`, *the caller is responsible to confirm that the recipient is capable of receiving NFTs or else they may be permanently lost.* +This library includes `transfer_from` and `safe_transfer_from` to transfer NFTs. +If using `transfer_from`, *the caller is responsible to confirm that the recipient is capable of receiving NFTs or else they may be permanently lost.* -The `safeTransferFrom` method incorporates the following conditional logic: +The `safe_transfer_from` method incorporates the following conditional logic: -. if the calling address is an account contract, the token transfer will behave as if `transferFrom` was called -. if the calling address is not an account contract, the safe function will check that the contract supports ERC721 tokens +. If the calling address is an account contract, the token transfer will behave as if `transfer_from` was called. +. If the calling address is not an account contract, the safe function will check that the contract supports ERC721 tokens. -The current implementation of `safeTansferFrom` checks for `onERC721Received` and requires that the recipient contract supports ERC165 and exposes the `supportsInterface` method. +The current implementation of `safe_transfer_from` checks for `on_erc721_received` and requires that the recipient contract supports SRC5 and exposes the `supports_interface` method. See <>. +## Fix me^ + === Interpreting ERC721 URIs Token URIs in Cairo are stored as single field elements. -Each field element equates to 252-bits (or 31.5 bytes) which means that a token's URI can be no longer than 31 characters. +Each field element equates to 252-bits (or 31.5 bytes) which means that a token's URI can be no longer than 31 characters. NOTE: Storing the URI as an array of felts was considered to accommodate larger strings. While this approach is more flexible regarding URIs, a returned array further deviates from the standard set in https://eips.ethereum.org/EIPS/eip-721[EIP721]. Therefore, this library's ERC721 implementation sets URIs as a single field element. -The `utils.py` module includes utility methods for converting to/from Cairo field elements. -To properly interpret a URI from ERC721, simply trim the null bytes and decode the remaining bits as an ASCII string. -For example: - -[,python] ----- -# HELPER METHODS -def str_to_felt(text): - b_text = bytes(text, 'ascii') - return int.from_bytes(b_text, "big") - -def felt_to_str(felt): - b_felt = felt.to_bytes(31, "big") - return b_felt.decode() - -token_id = uint(1) -sample_uri = str_to_felt('mock://mytoken') - -await signer.send_transaction( - account, erc721.contract_address, 'setTokenURI', [ - *token_id, sample_uri] -) - -felt_uri = await erc721.tokenURI(first_token_id).call() -string_uri = felt_to_str(felt_uri) ----- - === ERC721Received -In order to be sure a contract can safely accept ERC721 tokens, said contract must implement the `ERC721_Receiver` interface (as expressed in the EIP721 specification). -Methods such as `safeTransferFrom` and `safeMint` call the recipient contract's `onERC721Received` method. +In order to be sure a contract can safely accept ERC721 tokens, said contract must implement the `IERC721Receiver` interface (as expressed in the EIP721 specification). +Methods such as `safe_transfer_from` and `safe_mint` call the recipient contract's `on_erc721_received` method. If the contract fails to return the correct magic value, the transaction fails. -StarkNet contracts that support safe transfers, however, must also support xref:introspection.adoc#erc165[ERC165] and include `supportsInterface` as proposed in https://github.com/OpenZeppelin/cairo-contracts/discussions/100[#100]. -`safeTransferFrom` requires a means of differentiating between account and non-account contracts. -Currently, StarkNet does not support error handling from the contract level; +Starknet contracts that support safe transfers, however, must also support xref:introspection.adoc#src5[SRC5] and include `supports_interface` as proposed (originally as ERC165) in https://github.com/OpenZeppelin/cairo-contracts/discussions/100[#100]. +`safe_transfer_from` requires a means of differentiating between account and non-account contracts. +`on_erc721_received` will call `supports_interface` with the SRC6 magic value (INSERT ME) on the recipient address. +SRC6-compliant account contracts will return `true` thus communicating that the recipient is an account contract. +Non-account contracts, however, _must_ register support for ERC721 safe transfers. +Otherwise, the safe transfer will fail. + +Currently, Starknet does not support error handling from the contract level; therefore, the current ERC721 implementation requires that all contracts that support safe ERC721 transfers (both accounts and non-accounts) include the `supportsInterface` method. Further, `supportsInterface` should return `TRUE` if the recipient contract supports the `IERC721Receiver` magic value `0x150b7a02` (which invokes `onERC721Received`). If the recipient contract supports the `IAccount` magic value `0x50b70dcb`, `supportsInterface` should return `TRUE`. @@ -242,118 +173,38 @@ Otherwise, `safeTransferFrom` should fail. ==== IERC721Receiver -Interface for any contract that wants to support safeTransfers from ERC721 asset contracts. +Interface for any contract that wants to support safe transfers from ERC721 asset contracts. -[,cairo] +[,rust] ---- -@contract_interface -namespace IERC721Receiver { - func onERC721Received( - operator: felt, from_: felt, tokenId: Uint256, data_len: felt data: felt*) -> (selector: felt) { - } +#[starknet::interface] +trait IERC721Receiver { + fn on_erc721_received( + self: @TState, + operator: ContractAddress, + from: ContractAddress, + token_id: u256, + data: Span + ) -> felt252; } ---- === Supporting Interfaces -In order to ensure EVM/StarkNet compatibility, this ERC721 implementation does not calculate interface identifiers. -Instead, the interface IDs are hardcoded from their EVM calculations. -On the EVM, the interface ID is calculated from the selector's first four bytes of the hash of the function's signature while Cairo selectors are 252 bytes long. -Due to this difference, hardcoding EVM's already-calculated interface IDs is the most consistent approach to both follow the EIP165 standard and EVM compatibility. - -Further, this implementation stores supported interfaces in a mapping (similar to OpenZeppelin's https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v0.6.1/contracts/utils/introspection/ERC165Storage.sol[ERC165Storage]). +TODO === Ready-to-Use Presets -ERC721 presets have been created to allow for quick deployments as-is. -To be as explicit as possible, each preset includes the additional features they offer in the contract name. -For example: - -* `ERC721MintableBurnable` includes `mint` and `burn`. -* `ERC721MintablePausable` includes `mint`, `pause`, and `unpause`. -* `ERC721EnumerableMintableBurnable` includes `mint`, `burn`, and <> methods. - -Ready-to-use presets are a great option for testing and prototyping. +ERC721 presets have been created to allow for quick deployments as-is whic are a great option for testing and prototyping. See <>. == Extensibility -Following the xref:extensibility.adoc[contracts extensibility pattern], this implementation is set up to include all ERC721 related storage and business logic under a namespace. -Developers should be mindful of manually exposing the required methods from the namespace to comply with the standard interface. -This is already done in the <>; -however, additional functionality can be added. -For instance, you could: - -* Implement a pausing mechanism. -* Add roles such as _owner_ or _minter_. -* Modify the `transferFrom` function to mimic the https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol#L335[`_beforeTokenTransfer` and `_afterTokenTransfer` hooks]. - -Just be sure that the exposed `external` methods invoke their imported function logic a la `approve` invokes `ERC721.approve`. -As an example, see below. - -[,python] ----- -from openzeppelin.token.erc721.library import ERC721 - -@external -func approve{pedersen_ptr: HashBuiltin*, syscall_ptr: felt*, range_check_ptr}( - to: felt, tokenId: Uint256 -) { - ERC721.approve(to, tokenId) - return() -} ----- +TODO == Presets -The following contract presets are ready to deploy and can be used as-is for quick prototyping and testing. -Each preset includes a contract owner, which is set in the `constructor`, to offer simple access control on sensitive methods such as `mint` and `burn`. - -=== ERC721MintableBurnable - -The `ERC721MintableBurnable` preset offers a quick and easy setup for creating NFTs. -The contract owner can create tokens with `mint`, whereas token owners can destroy their tokens with `burn`. - -=== ERC721MintablePausable - -The `ERC721MintablePausable` preset creates a contract with pausable token transfers and minting capabilities. -This preset proves useful for scenarios such as preventing trades until the end of an evaluation period and having an emergency switch for freezing all token transfers in the event of a large bug. -In this preset, only the contract owner can `mint`, `pause`, and `unpause`. - -=== ERC721EnumerableMintableBurnable - -The `ERC721EnumerableMintableBurnable` preset adds enumerability of all the token ids in the contract as well as all token ids owned by each account. -This allows contracts to publish its full list of NFTs and make them discoverable. - -In regard to implementation, contracts should expose the following view methods: - -* `ERC721Enumerable.total_supply` -* `ERC721Enumerable.token_by_index` -* `ERC721Enumerable.token_of_owner_by_index` - -In order for the tokens to be correctly indexed, the contract should also use the following methods (which supersede some of the base `ERC721` methods): - -* `ERC721Enumerable.transfer_from` -* `ERC721Enumerable.safe_transfer_from` -* `ERC721Enumerable._mint` -* `ERC721Enumerable._burn` - -==== IERC721Enumerable - -[,cairo] ----- -@contract_interface -namespace IERC721Enumerable { - func totalSupply() -> (totalSupply: Uint256) { - } - - func tokenByIndex(index: Uint256) -> (tokenId: Uint256) { - } - - func tokenOfOwnerByIndex(owner: felt, index: Uint256) -> (tokenId: Uint256) { - } -} ----- +TODO === ERC721Metadata @@ -365,386 +216,379 @@ Note that the `IERC721Metadata` interface id should be removed from the construc ==== IERC721Metadata -[,cairo] +[,rust] ---- -@contract_interface -namespace IERC721Metadata { - func name() -> (name: felt) { - } - - func symbol() -> (symbol: felt) { - } - - func tokenURI(tokenId: Uint256) -> (tokenURI: felt) { - } +#[starknet::interface] +trait IERC721Metadata { + fn name(self: @TState) -> felt252; + fn symbol(self: @TState) -> felt252; + fn token_uri(self: @TState, token_id: u256) -> felt252; } ---- -== Utilities - -=== ERC721Holder - -Implementation of the `IERC721Receiver` interface. - -Accepts all token transfers. -Make sure the contract is able to use its token with `IERC721.safeTransferFrom`, `IERC721.approve` or `IERC721.setApprovalForAll`. - -Also utilizes the ERC165 method `supportsInterface` to determine if the contract is an account. -See <> - == API Specification === IERC721 API -[,cairo] ----- -func balanceOf(owner: felt) -> (balance: Uint256) { -} - -func ownerOf(tokenId: Uint256) -> (owner: felt) { -} - -func safeTransferFrom(from_: felt, to: felt, tokenId: Uint256, data_len: felt, data: felt*) { -} - -func transferFrom(from_: felt, to: felt, tokenId: Uint256) { -} - -func approve(approved: felt, tokenId: Uint256) { -} - -func setApprovalForAll(operator: felt, approved: felt) { -} - -func getApproved(tokenId: Uint256) -> (approved: felt) { -} - -func isApprovedForAll(owner: felt, operator: felt) -> (approved: felt) { -} ----- - -==== `balanceOf` - -Returns the number of tokens in ``owner``'s account. - -Parameters: - -[,cairo] ----- -owner: felt ----- - -Returns: - -[,cairo] ----- -balance: Uint256 ----- - -==== `ownerOf` - -Returns the owner of the `tokenId` token. - -Parameters: - -[,cairo] ----- -tokenId: Uint256 ----- - -Returns: - -[,cairo] +[,rust] ---- -owner: felt ----- - -==== `safeTransferFrom` - -Safely transfers `tokenId` token from `from_` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. -For information regarding how contracts communicate their awareness of the ERC721 protocol, see <>. - -Emits a <> event. - -Parameters: - -[,cairo] ----- -from_: felt -to: felt -tokenId: Uint256 -data_len: felt -data: felt* ----- - -Returns: None. - -==== `transferFrom` - -Transfers `tokenId` token from `from_` to `to`. -*The caller is responsible to confirm that `to` is capable of receiving NFTs or else they may be permanently lost*. - -Emits a <> event. - -Parameters: - -[,cairo] ----- -from_: felt -to: felt -tokenId: Uint256 ----- - -Returns: None. +fn balance_of(self: @ContractState, account: ContractAddress) -> u256; +fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress; +fn transfer_from(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256); +fn safe_transfer_from( + ref self: ContractState, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span +); +fn approve(ref self: ContractState, to: ContractAddress, token_id: u256); +fn set_approval_for_all(ref self: ContractState, operator: ContractAddress, approved: bool); +fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress; +fn is_approved_for_all( + self: @ContractState, owner: ContractAddress, operator: ContractAddress +) -> bool; +---- + +==== `balance_of` + +[.contract-item] +[[balance_of]] +==== `[.contract-item-name]#++balance_of++#++(self: @ContractState, account: ContractAddress) → u256++` + +Returns the number of NFTs owned by `account`. + +===== Arguments + +- `*account*` ++ +The account balance to query. + +===== Returns + +- `*u256*` ++ +Token balance of `account`. + +==== `owner_of` + +[.contract-item] +[[owner_of]] +==== `[.contract-item-name]#++owner_of++#++(self: @ContractState, token_id: u256) → ContractAddress++` + +Returns the owner address of `token_id`. + +===== Arguments + +- `*token_id*` ++ +The token to query. + +===== Returns + +- `*ContractAddress*` ++ +Owner address of `token_id`. + +==== `transfer_from` + +[.contract-item] +[[transfer_from]] +==== `[.contract-item-name]#++transfer_from++#++(self: @ContractState, from: ContractAddress, to: ContractAddress, token_id: u256)++` + +Transfer ownership of `token_id` from `from` to `to`. + +Note that the caller is responsible to confirm that the recipient is +capable of receiving ERC721 transfers or else they may be permanently lost. +Usage of <> prevents loss, though +the caller must understand this adds an external call which potentially +creates a reentrancy vulnerability. + +Emits a <> event. + +===== Arguments + +- `*from*` ++ +The current owner of the NFT. +- `*to*` ++ +The new owner. +- `*token_id*` ++ +The NFT to transfer. + +==== `safe_transfer_from` + +[.contract-item] +[[safe_transfer_from]] +==== `[.contract-item-name]#++safe_transfer_from++#++(self: @ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` + +Safely transfer ownership of `token_id` from `from` to `to`, checking first +that `to` is aware of the ERC721 protocol to prevent tokens being locked +forever. For information regarding how contracts communicate their +awareness of the ERC721 protocol, see <>(TODO!). + +Emits a <> event. + +===== Arguments + +- `*from*` ++ +The current owner of the NFT. +- `*to*` ++ +The new owner. +- `*token_id*` ++ +The NFT to transfer. +- `*data*` ++ +Additional data with no specified format, sent in call to `to`. ==== `approve` -Gives permission to `to` to transfer `tokenId` token to another account. -The approval is cleared when the token is transferred. - -Emits an <> event. - -Parameters: - -[,cairo] ----- -to: felt -tokenId: Uint256 ----- - -Returns: None. - -==== `getApproved` - -Returns the account approved for `tokenId` token. - -Parameters: - -[,cairo] ----- -tokenId: Uint256 ----- - -Returns: +[.contract-item] +[[approve]] +==== `[.contract-item-name]#++approve++#++(self: @ContractState, to: ContractAddress, token_id: u256)++` -[,cairo] ----- -operator: felt ----- +Change or reaffirm the approved address for an NFT. -==== `setApprovalForAll` +===== Arguments -Approve or remove `operator` as an operator for the caller. -Operators can call `transferFrom` or `safeTransferFrom` for any token owned by the caller. +- `*to*` ++ +The new approved NFT controller. +- `*token_id*` ++ +The NFT to approve. -Emits an <> event. + fn set_approval_for_all(ref self: TState, operator: ContractAddress, approved: bool); -Parameters: +==== `set_approval_for_all` -[,cairo] ----- -operator: felt ----- +[.contract-item] +[[set_approval_for_all]] +==== `[.contract-item-name]#++set_approval_for_all++#++(self: @ContractState, operator: ContractAddress, approved: bool)++` -Returns: None. +Enable or disable approval for `operator` to manage all of the +caller's assets. -==== `isApprovedForAll` +Emits an <> event. -Returns if the `operator` is allowed to manage all of the assets of `owner`. +===== Arguments -Parameters: +- `*operator*` ++ +Address to add to the set of authorized operators. +- `*approved*` ++ +`true` if operator is approved, `false` to revoke approval. -[,cairo] ----- -owner: felt -operator: felt ----- +==== `get_approved` -Returns: +[.contract-item] +[[get_approved]] +==== `[.contract-item-name]#++get_approved++#++(self: @ContractState, token_id: u256) -> ContractAddress++` -[,cairo] ----- -approved: felt ----- +Returns the address approved for `token_id`. -=== Events +===== Arguments -==== `Approval (Event)` +- `*token_id*` ++ +The token ID to query. -Emitted when `owner` enables `approved` to manage the `tokenId` token. +===== Returns -Parameters: +- `*ContractAddress*` ++ +Approved address for the `token_id` NFT, or `0` if there is none. -[,cairo] ----- -owner: felt -approved: felt -tokenId: Uint256 ----- +==== `is_approved_for_all` -==== `ApprovalForAll (Event)` +[.contract-item] +[[is_approved_for_all]] +==== `[.contract-item-name]#++is_approved_for_all++#++(self: @ContractState, owner: ContractAddress, operator: ContractAddress) -> bool++` -Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. +Query if `operator` is an authorized operator for `owner`. -Parameters: +===== Arguments -[,cairo] ----- -owner: felt -operator: felt -approved: felt ----- +- `*owner*` ++ +The address that owns the NFT. +- `*operator*` ++ +The address that acts on behalf of the `owner`. -==== `Transfer (Event)` +===== Returns -Emitted when `tokenId` token is transferred from `from_` to `to`. +- `*bool*` ++ +`true` if `operator` is an authorized operator for `owner`. -Parameters: +=== Events -[,cairo] ----- -from_: felt -to: felt -tokenId: Uint256 +[,rust] ---- +#[event] +#[derive(Drop, starknet::Event)] +enum Event { + Transfer: Transfer, + Approval: Approval, + ApprovalForAll: ApprovalForAll +} -''' - -=== IERC721Metadata API - -[,cairo] ----- -func name() -> (name: felt) { +#[derive(Drop, starknet::Event)] +struct Approval { + owner: ContractAddress, + approved: ContractAddress, + token_id: u256 } -func symbol() -> (symbol: felt) { +#[derive(Drop, starknet::Event)] +struct ApprovalForAll { + owner: ContractAddress, + operator: ContractAddress, + approved: bool } -func tokenURI(tokenId: Uint256) -> (tokenURI: felt) { +#[derive(Drop, starknet::Event)] +struct Transfer { + from: ContractAddress, + to: ContractAddress, + token_id: u256 } ---- -==== `name` - -Returns the token collection name. +==== `Approval` -Parameters: None. - -Returns: +[.contract-item] +[[Approval]] +==== `[.contract-item-name]#++Approval++#++(owner: ContractAddress, approved: ContractAddress, token_id: u256)++` -[,cairo] ----- -name: felt ----- +Emitted when `owner` enables `approved` to manage the `token_id` token. -==== `symbol` +===== Arguments -Returns the token collection symbol. +- `*owner*` ++ +The owner of the NFT. +- `*approved*` ++ +The new approved NFT controller. +- `*token_id*` ++ +The NFT to approve. -Parameters: None. +==== `ApprovalForAll` -Returns: +[.contract-item] +[[ApprovalForAll]] +==== `[.contract-item-name]#++ApprovalForAll++#++(owner: ContractAddress, operator: ContractAddress, approved: bool)++` -[,cairo] ----- -symbol: felt ----- +Emitted when `owner` enables or disables (approved) `operator` to manage all of its assets. -==== `tokenURI` +===== Arguments -Returns the Uniform Resource Identifier (URI) for `tokenID` token. -If the URI is not set for the `tokenId`, the return value will be `0`. +- `*owner*` ++ +The owner of the NFT. +- `*operator*` ++ +Address to add to the set of authorized operators. +- `*approved*` ++ +`true` if the operator is approved, `false` to revoke approval. -Parameters: +==== `Transfer` -[,cairo] ----- -tokenId: Uint256 ----- +[.contract-item] +[[Transfer]] +==== `[.contract-item-name]#++Transfer++#++(from: ContractAddress, to: ContractAddress, token_id: u256)++` -Returns: +Emitted when `token_id` token is transferred from `from` to `to`. -[,cairo] ----- -tokenURI: felt ----- +===== Arguments -''' +- `*from*` ++ +The current owner of the NFT. +- `*to*` ++ +The new owner of the NFT. +- `*token_id*` ++ +The NFT to transfer. -=== IERC721Enumerable API +=== IERC721Metadata API -[,cairo] +[,rust] ---- -func totalSupply() -> (totalSupply: Uint256) { -} - -func tokenByIndex(index: Uint256) -> (tokenId: Uint256) { -} - -func tokenOfOwnerByIndex(owner: felt, index: Uint256) -> (tokenId: Uint256) { +#[starknet::interface] +trait IERC721Metadata { + fn name(self: @TState) -> felt252; + fn symbol(self: @TState) -> felt252; + fn token_uri(self: @TState, token_id: u256) -> felt252; } ---- -==== `totalSupply` - -Returns the total amount of tokens stored by the contract. +==== `name` -Parameters: None +[.contract-item] +[[name]] +==== `[.contract-item-name]#++name++#++(self: @ContractState)++` -Returns: +Returns the NFT name. -[,cairo] ----- -totalSupply: Uint256 ----- +===== Returns -==== `tokenByIndex` +- `*felt252*` ++ +The NFT name. -Returns a token ID owned by `owner` at a given `index` of its token list. -Use along with <> to enumerate all of ``owner``'s tokens. +==== `symbol` -Parameters: +[.contract-item] +[[symbol]] +==== `[.contract-item-name]#++symbol++#++(self: @ContractState)++` -[,cairo] ----- -index: Uint256 ----- +Returns the NFT ticker symbol. -Returns: +===== Returns -[,cairo] ----- -tokenId: Uint256 ----- +- `*felt252*` ++ +The NFT symbol. -==== `tokenOfOwnerByIndex` +==== `token_uri` -Returns a token ID at a given `index` of all the tokens stored by the contract. -Use along with <> to enumerate all tokens. +[.contract-item] +[[token_uri]] +==== `[.contract-item-name]#++token_uri++#++(self: @ContractState, token_id: u256)++` -Parameters: +Returns the Uniform Resource Identifier (URI) for the `token_id` token. +If the URI is not set for the `token_id`, the return value will be `0`. -[,cairo] ----- -owner: felt -index: Uint256 ----- +===== Arguments -Returns: +- `*token_id*` ++ +The NFT symbol. -[,cairo] ----- -tokenId: Uint256 ----- +===== Returns -''' +- `*felt252*` ++ +The URI of `token_id`. === IERC721Receiver API [,cairo] ---- func onERC721Received( - operator: felt, from_: felt, tokenId: Uint256, data_len: felt data: felt* + operator: felt, from_: felt, tokenId: Uint256, data_len: felt252data: felt* ) -> (selector: felt) { } ---- From bd443768e18b818ba5fde5f6dc15c1783cebf5a4 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 14 Aug 2023 10:04:54 -0400 Subject: [PATCH 084/246] update erc721receiver --- docs/modules/ROOT/pages/erc721.adoc | 92 +++++++++++++++++------------ 1 file changed, 55 insertions(+), 37 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index eb2f6ea91..8c54fcdc7 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -26,18 +26,18 @@ The `erc721.cairo` contract implements an approximation of https://eips.ethereum ** <> * <> ** <> - *** <> - *** <> - *** <> - *** <> + *** <> + *** <> + *** <> + *** <> *** <> - *** <> - *** <> - *** <> + *** <> + *** <> + *** <> ** <> - *** <> - *** <> - *** <> + *** <> + *** <> + *** <> ** <> *** <> *** <> @@ -141,7 +141,7 @@ The `safe_transfer_from` method incorporates the following conditional logic: The current implementation of `safe_transfer_from` checks for `on_erc721_received` and requires that the recipient contract supports SRC5 and exposes the `supports_interface` method. See <>. -## Fix me^ +Fix me^ === Interpreting ERC721 URIs @@ -210,7 +210,7 @@ TODO The `ERC721Metadata` extension allows your smart contract to be interrogated for its name and for details about the assets which your NFTs represent. -We follow OpenZeppelin's Solidity approach of integrating the Metadata methods `name`, `symbol`, and `tokenURI` into all ERC721 implementations. +We follow OpenZeppelin's Solidity approach of integrating the Metadata methods `name`, `symbol`, and `token_uri` (`tokenURI` in Solidity) into all ERC721 implementations. If preferred, a contract can be created that does not import the Metadata methods from the `ERC721` library. Note that the `IERC721Metadata` interface id should be removed from the constructor as well. @@ -232,6 +232,8 @@ trait IERC721Metadata { [,rust] ---- +// SRC5 id: 0x33eb2f84c309543403fd69f0d0f363781ef06ef6faeb0131ff16ea3175bd943 + fn balance_of(self: @ContractState, account: ContractAddress) -> u256; fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress; fn transfer_from(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256); @@ -462,10 +464,9 @@ struct Transfer { } ---- -==== `Approval` +==== `Approval` [[Approval]] [.contract-item] -[[Approval]] ==== `[.contract-item-name]#++Approval++#++(owner: ContractAddress, approved: ContractAddress, token_id: u256)++` Emitted when `owner` enables `approved` to manage the `token_id` token. @@ -482,10 +483,9 @@ The new approved NFT controller. + The NFT to approve. -==== `ApprovalForAll` +==== `ApprovalForAll` [[ApprovalForAll]] [.contract-item] -[[ApprovalForAll]] ==== `[.contract-item-name]#++ApprovalForAll++#++(owner: ContractAddress, operator: ContractAddress, approved: bool)++` Emitted when `owner` enables or disables (approved) `operator` to manage all of its assets. @@ -502,10 +502,9 @@ Address to add to the set of authorized operators. + `true` if the operator is approved, `false` to revoke approval. -==== `Transfer` +==== `Transfer` [[Transfer]] [.contract-item] -[[Transfer]] ==== `[.contract-item-name]#++Transfer++#++(from: ContractAddress, to: ContractAddress, token_id: u256)++` Emitted when `token_id` token is transferred from `from` to `to`. @@ -526,6 +525,8 @@ The NFT to transfer. [,rust] ---- +// SRC5 id: 0x6069a70848f907fa57668ba1875164eb4dcee693952468581406d131081bbd + #[starknet::interface] trait IERC721Metadata { fn name(self: @TState) -> felt252; @@ -585,32 +586,49 @@ The URI of `token_id`. === IERC721Receiver API -[,cairo] +[,rust] ---- -func onERC721Received( - operator: felt, from_: felt, tokenId: Uint256, data_len: felt252data: felt* -) -> (selector: felt) { +// SRC5 id: 0x3a0dff5f70d80458ad14ae37bb182a728e3c8cdda0402a5daa86620bdf910bc + +#[starknet::interface] +trait IERC721Receiver { + fn on_erc721_received( + self: @TState, + operator: ContractAddress, + from: ContractAddress, + token_id: u256, + data: Span + ) -> felt252; } ---- -==== `onERC721Received` +==== `on_erc721_received` -Whenever an IERC721 `tokenId` token is transferred to this non-account contract via `safeTransferFrom` by `operator` from `from_`, this function is called. +[.contract-item] +[[on_erc721_received]] +==== `[.contract-item-name]#++on_erc721_received++#++(self: @TState, operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span) -> felt252++` -Parameters: +Whenever an IERC721 `token_id` token is transferred to this non-account contract through `safe_transfer_from`, this function is called. +This function may reject the transfer. +If this function returns anything other than the IERC721_RECEIVER_ID, the transaction must be reverted. -[,cairo] ----- -operator: felt -from_: felt -tokenId: Uint256 -data_len: felt -data: felt* ----- +===== Arguments + +- `*operator*` ++ +The address which called `safe_transfer_from` function. +- `*from*` ++ +The address which previously owned the token. +- `*token_id*` ++ +The NFT identifier which is being transferred. +- `*data*` ++ +Additional data with no specified format. Returns: -[,cairo] ----- -selector: felt ----- +- `*felt252*` ++ +The IERC721Receiver magic value _0x3a0dff5f70d80458ad14ae37bb182a728e3c8cdda0402a5daa86620bdf910bc_. From 544ddacd26736533651f0430bf15ccb5ff70332e Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Wed, 16 Aug 2023 00:25:07 +0200 Subject: [PATCH 085/246] Add testing for AccessControl events (#674) * feat: add testing for events * feat: add tests for ownable events * feat: improve tests * feat: apply review updates --- src/tests/access/test_accesscontrol.cairo | 60 ++++++++++++++++++++++- src/tests/access/test_ownable.cairo | 6 +-- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/tests/access/test_accesscontrol.cairo b/src/tests/access/test_accesscontrol.cairo index 6099c551f..cea50e7d4 100644 --- a/src/tests/access/test_accesscontrol.cairo +++ b/src/tests/access/test_accesscontrol.cairo @@ -1,12 +1,17 @@ use openzeppelin::access::accesscontrol::AccessControl::AccessControlCamelImpl; use openzeppelin::access::accesscontrol::AccessControl::AccessControlImpl; use openzeppelin::access::accesscontrol::AccessControl::InternalImpl; +use openzeppelin::access::accesscontrol::AccessControl::RoleAdminChanged; +use openzeppelin::access::accesscontrol::AccessControl::RoleGranted; +use openzeppelin::access::accesscontrol::AccessControl::RoleRevoked; use openzeppelin::access::accesscontrol::AccessControl; use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; use openzeppelin::access::accesscontrol::interface::IACCESSCONTROL_ID; use openzeppelin::tests::utils::constants::{ ADMIN, AUTHORIZED, OTHER, OTHER_ADMIN, ROLE, OTHER_ROLE, ZERO }; +use openzeppelin::tests::utils; +use option::OptionTrait; use starknet::ContractAddress; use starknet::contract_address_const; use starknet::testing; @@ -22,6 +27,7 @@ fn STATE() -> AccessControl::ContractState { fn setup() -> AccessControl::ContractState { let mut state = STATE(); InternalImpl::_grant_role(ref state, DEFAULT_ADMIN_ROLE, ADMIN()); + testing::pop_log_raw(ZERO()); state } @@ -133,6 +139,8 @@ fn test_grant_role() { let mut state = setup(); testing::set_caller_address(ADMIN()); AccessControlImpl::grant_role(ref state, ROLE, AUTHORIZED()); + + assert_event_role_granted(ROLE, AUTHORIZED(), ADMIN()); assert(AccessControlImpl::has_role(@state, ROLE, AUTHORIZED()), 'Role should be granted'); } @@ -142,6 +150,8 @@ fn test_grantRole() { let mut state = setup(); testing::set_caller_address(ADMIN()); AccessControlCamelImpl::grantRole(ref state, ROLE, AUTHORIZED()); + + assert_event_role_granted(ROLE, AUTHORIZED(), ADMIN()); assert(AccessControlCamelImpl::hasRole(@state, ROLE, AUTHORIZED()), 'Role should be granted'); } @@ -214,7 +224,11 @@ fn test_revoke_role_for_granted_role() { testing::set_caller_address(ADMIN()); AccessControlImpl::grant_role(ref state, ROLE, AUTHORIZED()); + testing::pop_log_raw(ZERO()); + AccessControlImpl::revoke_role(ref state, ROLE, AUTHORIZED()); + + assert_event_role_revoked(ROLE, AUTHORIZED(), ADMIN()); assert(!AccessControlImpl::has_role(@state, ROLE, AUTHORIZED()), 'Role should be revoked'); } @@ -225,7 +239,11 @@ fn test_revokeRole_for_granted_role() { testing::set_caller_address(ADMIN()); AccessControlCamelImpl::grantRole(ref state, ROLE, AUTHORIZED()); + testing::pop_log_raw(ZERO()); + AccessControlCamelImpl::revokeRole(ref state, ROLE, AUTHORIZED()); + + assert_event_role_revoked(ROLE, AUTHORIZED(), ADMIN()); assert(!AccessControlCamelImpl::hasRole(@state, ROLE, AUTHORIZED()), 'Role should be revoked'); } @@ -300,10 +318,14 @@ fn test_renounceRole_for_role_not_granted() { fn test_renounce_role_for_granted_role() { let mut state = setup(); testing::set_caller_address(ADMIN()); + AccessControlImpl::grant_role(ref state, ROLE, AUTHORIZED()); + testing::pop_log_raw(ZERO()); testing::set_caller_address(AUTHORIZED()); AccessControlImpl::renounce_role(ref state, ROLE, AUTHORIZED()); + + assert_event_role_revoked(ROLE, AUTHORIZED(), AUTHORIZED()); assert(!AccessControlImpl::has_role(@state, ROLE, AUTHORIZED()), 'Role should be renounced'); } @@ -312,10 +334,14 @@ fn test_renounce_role_for_granted_role() { fn test_renounceRole_for_granted_role() { let mut state = setup(); testing::set_caller_address(ADMIN()); + AccessControlCamelImpl::grantRole(ref state, ROLE, AUTHORIZED()); + testing::pop_log_raw(ZERO()); testing::set_caller_address(AUTHORIZED()); AccessControlCamelImpl::renounceRole(ref state, ROLE, AUTHORIZED()); + + assert_event_role_revoked(ROLE, AUTHORIZED(), AUTHORIZED()); assert( !AccessControlCamelImpl::hasRole(@state, ROLE, AUTHORIZED()), 'Role should be renounced' ); @@ -389,6 +415,8 @@ fn test__set_role_admin() { 'ROLE admin default should be 0' ); InternalImpl::_set_role_admin(ref state, ROLE, OTHER_ROLE); + + assert_event_role_admin_changed(ROLE, DEFAULT_ADMIN_ROLE, OTHER_ROLE); assert( AccessControlImpl::get_role_admin(@state, ROLE) == OTHER_ROLE, 'ROLE admin should be OTHER_ROLE' @@ -447,7 +475,7 @@ fn test_previous_admin_cannot_revoke_roles() { } // -// default admin +// Default admin // #[test] @@ -469,3 +497,33 @@ fn test_default_admin_role_is_its_own_admin() { 'Should be DEFAULT_ADMIN_ROLE' ); } + +// +// Helpers +// + +fn assert_event_role_revoked(role: felt252, account: ContractAddress, sender: ContractAddress) { + let event = utils::pop_log::(ZERO()).unwrap(); + assert(event.role == role, 'Invalid `role`'); + assert(event.account == account, 'Invalid `account`'); + assert(event.sender == sender, 'Invalid `sender`'); + utils::assert_no_events_left(ZERO()); +} + +fn assert_event_role_granted(role: felt252, account: ContractAddress, sender: ContractAddress) { + let event = utils::pop_log::(ZERO()).unwrap(); + assert(event.role == role, 'Invalid `role`'); + assert(event.account == account, 'Invalid `account`'); + assert(event.sender == sender, 'Invalid `sender`'); + utils::assert_no_events_left(ZERO()); +} + +fn assert_event_role_admin_changed( + role: felt252, previous_admin_role: felt252, new_admin_role: felt252 +) { + let event = utils::pop_log::(ZERO()).unwrap(); + assert(event.role == role, 'Invalid `role`'); + assert(event.previous_admin_role == previous_admin_role, 'Invalid `previous_admin_role`'); + assert(event.new_admin_role == new_admin_role, 'Invalid `new_admin_role`'); + utils::assert_no_events_left(ZERO()); +} diff --git a/src/tests/access/test_ownable.cairo b/src/tests/access/test_ownable.cairo index c2e6b8682..c27af64d6 100644 --- a/src/tests/access/test_ownable.cairo +++ b/src/tests/access/test_ownable.cairo @@ -4,8 +4,8 @@ use openzeppelin::access::ownable::Ownable::OwnableCamelOnlyImpl; use openzeppelin::access::ownable::Ownable::OwnableImpl; use openzeppelin::access::ownable::Ownable::OwnershipTransferred; use openzeppelin::access::ownable::Ownable::_owner::InternalContractStateTrait; -use openzeppelin::tests::utils; use openzeppelin::tests::utils::constants::{ZERO, OTHER, OWNER}; +use openzeppelin::tests::utils; use option::OptionTrait; use starknet::ContractAddress; use starknet::contract_address_const; @@ -235,7 +235,7 @@ fn test_renounceOwnership_from_nonowner() { fn assert_event_ownership_transferred(previous_owner: ContractAddress, new_owner: ContractAddress) { let event = utils::pop_log::(ZERO()).unwrap(); - assert(event.previous_owner == previous_owner, 'Invalid previous_owner'); - assert(event.new_owner == new_owner, 'Invalid new_owner'); + assert(event.previous_owner == previous_owner, 'Invalid `previous_owner`'); + assert(event.new_owner == new_owner, 'Invalid `new_owner`'); utils::assert_no_events_left(ZERO()); } From 4473243bdb513f9e57868700a46d5c48b1fe12cf Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Wed, 16 Aug 2023 00:28:57 +0200 Subject: [PATCH 086/246] Add testing for Pausable events (#676) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add testing for events * feat: add tests for ownable events * feat: add tests for pausable events * Update src/openzeppelin/tests/security/test_pausable.cairo Co-authored-by: Andrew Fleming * feat: improve tests * feat: apply review updates * feat: apply review updates --------- Co-authored-by: Andrew Fleming Co-authored-by: Martín Triay --- src/tests/security/test_pausable.cairo | 38 ++++++++++++++++++++++++++ src/tests/utils/constants.cairo | 4 +++ 2 files changed, 42 insertions(+) diff --git a/src/tests/security/test_pausable.cairo b/src/tests/security/test_pausable.cairo index ac84fedc2..7f3feb8e7 100644 --- a/src/tests/security/test_pausable.cairo +++ b/src/tests/security/test_pausable.cairo @@ -1,6 +1,18 @@ use openzeppelin::security::pausable::Pausable::InternalImpl; use openzeppelin::security::pausable::Pausable::PausableImpl; +use openzeppelin::security::pausable::Pausable::Paused; +use openzeppelin::security::pausable::Pausable::Unpaused; use openzeppelin::security::pausable::Pausable; +use openzeppelin::tests::utils::constants::{CALLER, ZERO}; +use openzeppelin::tests::utils; +use option::OptionTrait; +use starknet::contract_address_const; +use starknet::ContractAddress; +use starknet::testing; + +// +// Setup +// fn STATE() -> Pausable::ContractState { Pausable::contract_state_for_testing() @@ -71,7 +83,11 @@ fn test_assert_not_paused_when_not_paused() { #[available_gas(2000000)] fn test_pause_when_unpaused() { let mut state = STATE(); + testing::set_caller_address(CALLER()); + InternalImpl::_pause(ref state); + + assert_event_paused(CALLER()); assert(PausableImpl::is_paused(@state), 'Should be paused'); } @@ -92,8 +108,14 @@ fn test_pause_when_paused() { #[available_gas(2000000)] fn test_unpause_when_paused() { let mut state = STATE(); + testing::set_caller_address(CALLER()); + InternalImpl::_pause(ref state); + testing::pop_log_raw(ZERO()); + InternalImpl::_unpause(ref state); + + assert_event_unpaused(CALLER()); assert(!PausableImpl::is_paused(@state), 'Should not be paused'); } @@ -105,3 +127,19 @@ fn test_unpause_when_unpaused() { assert(!PausableImpl::is_paused(@state), 'Should be paused'); InternalImpl::_unpause(ref state); } + +// +// Helpers +// + +fn assert_event_paused(account: ContractAddress) { + let event = testing::pop_log::(ZERO()).unwrap(); + assert(event.account == account, 'Invalid `account`'); + utils::assert_no_events_left(ZERO()); +} + +fn assert_event_unpaused(account: ContractAddress) { + let event = testing::pop_log::(ZERO()).unwrap(); + assert(event.account == account, 'Invalid `account`'); + utils::assert_no_events_left(ZERO()); +} diff --git a/src/tests/utils/constants.cairo b/src/tests/utils/constants.cairo index 84cc4eae4..f6fc94a3f 100644 --- a/src/tests/utils/constants.cairo +++ b/src/tests/utils/constants.cairo @@ -16,6 +16,10 @@ fn ZERO() -> ContractAddress { contract_address_const::<0>() } +fn CALLER() -> ContractAddress { + contract_address_const::<'CALLER'>() +} + fn OWNER() -> ContractAddress { contract_address_const::<'OWNER'>() } From e95def8d88cdfcea9e54ba5270ec63b5b0085e66 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Wed, 16 Aug 2023 00:39:44 +0200 Subject: [PATCH 087/246] Add testing for ERC20 events (#677) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add testing for events * feat: add tests for ownable events * feat: add tests for pausable events * feat: add tests for erc20 events * Update src/openzeppelin/tests/security/test_pausable.cairo Co-authored-by: Andrew Fleming * feat: apply review updates * feat: improve tests * feat: apply review updates * feat: apply review updates * feat: apply review updates --------- Co-authored-by: Andrew Fleming Co-authored-by: Martín Triay --- src/tests/token/test_erc20.cairo | 157 ++++++++++++++++++++----------- src/tests/utils/constants.cairo | 13 +++ 2 files changed, 117 insertions(+), 53 deletions(-) diff --git a/src/tests/token/test_erc20.cairo b/src/tests/token/test_erc20.cairo index 1303c3e1c..dd31d8e4d 100644 --- a/src/tests/token/test_erc20.cairo +++ b/src/tests/token/test_erc20.cairo @@ -1,46 +1,35 @@ use integer::BoundedInt; use integer::u256; use integer::u256_from_felt252; +use openzeppelin::tests::utils::constants::{ + ZERO, OWNER, SPENDER, RECIPIENT, NAME, SYMBOL, DECIMALS, SUPPLY, VALUE +}; +use openzeppelin::tests::utils; +use openzeppelin::token::erc20::ERC20::Approval; use openzeppelin::token::erc20::ERC20::ERC20CamelOnlyImpl; use openzeppelin::token::erc20::ERC20::ERC20Impl; use openzeppelin::token::erc20::ERC20::InternalImpl; +use openzeppelin::token::erc20::ERC20::Transfer; use openzeppelin::token::erc20::ERC20; +use option::OptionTrait; use starknet::ContractAddress; use starknet::contract_address_const; -use starknet::testing::set_caller_address; +use starknet::testing; use traits::Into; use zeroable::Zeroable; // -// Constants +// Setup // -const NAME: felt252 = 111; -const SYMBOL: felt252 = 222; -const DECIMALS: u8 = 18_u8; -const SUPPLY: u256 = 2000; -const VALUE: u256 = 300; - fn STATE() -> ERC20::ContractState { ERC20::contract_state_for_testing() } -fn OWNER() -> ContractAddress { - contract_address_const::<1>() -} -fn SPENDER() -> ContractAddress { - contract_address_const::<2>() -} -fn RECIPIENT() -> ContractAddress { - contract_address_const::<3>() -} - -// -// Setup -// fn setup() -> ERC20::ContractState { let mut state = STATE(); ERC20::constructor(ref state, NAME, SYMBOL, SUPPLY, OWNER()); + testing::pop_log_raw(ZERO()); state } @@ -67,6 +56,8 @@ fn test_constructor() { let mut state = STATE(); ERC20::constructor(ref state, NAME, SYMBOL, SUPPLY, OWNER()); + assert_only_event_transfer(ZERO(), OWNER(), SUPPLY); + assert(ERC20Impl::balance_of(@state, OWNER()) == SUPPLY, 'Should eq inital_supply'); assert(ERC20Impl::total_supply(@state) == SUPPLY, 'Should eq inital_supply'); assert(ERC20Impl::name(@state) == NAME, 'Name should be NAME'); @@ -114,7 +105,7 @@ fn test_balanceOf() { #[available_gas(2000000)] fn test_allowance() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC20Impl::approve(ref state, SPENDER(), VALUE); assert(ERC20Impl::allowance(@state, OWNER(), SPENDER()) == VALUE, 'Should eq VALUE'); @@ -128,8 +119,10 @@ fn test_allowance() { #[available_gas(2000000)] fn test_approve() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); assert(ERC20Impl::approve(ref state, SPENDER(), VALUE), 'Should return true'); + + assert_only_event_approval(OWNER(), SPENDER(), VALUE); assert( ERC20Impl::allowance(@state, OWNER(), SPENDER()) == VALUE, 'Spender not approved correctly' ); @@ -148,7 +141,7 @@ fn test_approve_from_zero() { #[should_panic(expected: ('ERC20: approve to 0', ))] fn test_approve_to_zero() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC20Impl::approve(ref state, Zeroable::zero(), VALUE); } @@ -156,9 +149,10 @@ fn test_approve_to_zero() { #[available_gas(2000000)] fn test__approve() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); InternalImpl::_approve(ref state, OWNER(), SPENDER(), VALUE); + assert_only_event_approval(OWNER(), SPENDER(), VALUE); assert( ERC20Impl::allowance(@state, OWNER(), SPENDER()) == VALUE, 'Spender not approved correctly' ); @@ -177,7 +171,7 @@ fn test__approve_from_zero() { #[should_panic(expected: ('ERC20: approve to 0', ))] fn test__approve_to_zero() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); InternalImpl::_approve(ref state, OWNER(), Zeroable::zero(), VALUE); } @@ -189,9 +183,10 @@ fn test__approve_to_zero() { #[available_gas(2000000)] fn test_transfer() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); assert(ERC20Impl::transfer(ref state, RECIPIENT(), VALUE), 'Should return true'); + assert_only_event_transfer(OWNER(), RECIPIENT(), VALUE); assert(ERC20Impl::balance_of(@state, RECIPIENT()) == VALUE, 'Balance should eq VALUE'); assert(ERC20Impl::balance_of(@state, OWNER()) == SUPPLY - VALUE, 'Should eq supply - VALUE'); assert(ERC20Impl::total_supply(@state) == SUPPLY, 'Total supply should not change'); @@ -203,6 +198,8 @@ fn test__transfer() { let mut state = setup(); InternalImpl::_transfer(ref state, OWNER(), RECIPIENT(), VALUE); + + assert_only_event_transfer(OWNER(), RECIPIENT(), VALUE); assert(ERC20Impl::balance_of(@state, RECIPIENT()) == VALUE, 'Balance should eq amount'); assert(ERC20Impl::balance_of(@state, OWNER()) == SUPPLY - VALUE, 'Should eq supply - amount'); assert(ERC20Impl::total_supply(@state) == SUPPLY, 'Total supply should not change'); @@ -213,7 +210,7 @@ fn test__transfer() { #[should_panic(expected: ('u256_sub Overflow', ))] fn test__transfer_not_enough_balance() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); let balance_plus_one = SUPPLY + 1; InternalImpl::_transfer(ref state, OWNER(), RECIPIENT(), balance_plus_one); @@ -243,12 +240,16 @@ fn test__transfer_to_zero() { #[available_gas(2000000)] fn test_transfer_from() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC20Impl::approve(ref state, SPENDER(), VALUE); + testing::pop_log_raw(ZERO()); - set_caller_address(SPENDER()); + testing::set_caller_address(SPENDER()); assert(ERC20Impl::transfer_from(ref state, OWNER(), RECIPIENT(), VALUE), 'Should return true'); + assert_event_approval(OWNER(), SPENDER(), 0); + assert_only_event_transfer(OWNER(), RECIPIENT(), VALUE); + assert(ERC20Impl::balance_of(@state, RECIPIENT()) == VALUE, 'Should eq amount'); assert(ERC20Impl::balance_of(@state, OWNER()) == SUPPLY - VALUE, 'Should eq suppy - amount'); assert(ERC20Impl::allowance(@state, OWNER(), SPENDER()) == 0, 'Should eq 0'); @@ -259,10 +260,10 @@ fn test_transfer_from() { #[available_gas(2000000)] fn test_transfer_from_doesnt_consume_infinite_allowance() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC20Impl::approve(ref state, SPENDER(), BoundedInt::max()); - set_caller_address(SPENDER()); + testing::set_caller_address(SPENDER()); ERC20Impl::transfer_from(ref state, OWNER(), RECIPIENT(), VALUE); assert( @@ -276,10 +277,10 @@ fn test_transfer_from_doesnt_consume_infinite_allowance() { #[should_panic(expected: ('u256_sub Overflow', ))] fn test_transfer_from_greater_than_allowance() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC20Impl::approve(ref state, SPENDER(), VALUE); - set_caller_address(SPENDER()); + testing::set_caller_address(SPENDER()); let allowance_plus_one = VALUE + 1; ERC20Impl::transfer_from(ref state, OWNER(), RECIPIENT(), allowance_plus_one); } @@ -289,10 +290,10 @@ fn test_transfer_from_greater_than_allowance() { #[should_panic(expected: ('ERC20: transfer to 0', ))] fn test_transfer_from_to_zero_address() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC20Impl::approve(ref state, SPENDER(), VALUE); - set_caller_address(SPENDER()); + testing::set_caller_address(SPENDER()); ERC20Impl::transfer_from(ref state, OWNER(), Zeroable::zero(), VALUE); } @@ -308,15 +309,19 @@ fn test_transfer_from_from_zero_address() { #[available_gas(2000000)] fn test_transferFrom() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC20Impl::approve(ref state, SPENDER(), VALUE); + testing::pop_log_raw(ZERO()); - set_caller_address(SPENDER()); - + testing::set_caller_address(SPENDER()); assert( ERC20CamelOnlyImpl::transferFrom(ref state, OWNER(), RECIPIENT(), VALUE), 'Should return true' ); + + assert_event_approval(OWNER(), SPENDER(), 0); + assert_only_event_transfer(OWNER(), RECIPIENT(), VALUE); + assert(ERC20CamelOnlyImpl::balanceOf(@state, RECIPIENT()) == VALUE, 'Should eq amount'); assert( ERC20CamelOnlyImpl::balanceOf(@state, OWNER()) == SUPPLY - VALUE, 'Should eq suppy - amount' @@ -329,10 +334,10 @@ fn test_transferFrom() { #[available_gas(2000000)] fn test_transferFrom_doesnt_consume_infinite_allowance() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC20Impl::approve(ref state, SPENDER(), BoundedInt::max()); - set_caller_address(SPENDER()); + testing::set_caller_address(SPENDER()); ERC20CamelOnlyImpl::transferFrom(ref state, OWNER(), RECIPIENT(), VALUE); assert( @@ -346,10 +351,10 @@ fn test_transferFrom_doesnt_consume_infinite_allowance() { #[should_panic(expected: ('u256_sub Overflow', ))] fn test_transferFrom_greater_than_allowance() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC20Impl::approve(ref state, SPENDER(), VALUE); - set_caller_address(SPENDER()); + testing::set_caller_address(SPENDER()); let allowance_plus_one = VALUE + 1; ERC20CamelOnlyImpl::transferFrom(ref state, OWNER(), RECIPIENT(), allowance_plus_one); } @@ -359,10 +364,10 @@ fn test_transferFrom_greater_than_allowance() { #[should_panic(expected: ('ERC20: transfer to 0', ))] fn test_transferFrom_to_zero_address() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC20Impl::approve(ref state, SPENDER(), VALUE); - set_caller_address(SPENDER()); + testing::set_caller_address(SPENDER()); ERC20CamelOnlyImpl::transferFrom(ref state, OWNER(), Zeroable::zero(), VALUE); } @@ -382,10 +387,13 @@ fn test_transferFrom_from_zero_address() { #[available_gas(2000000)] fn test_increase_allowance() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC20Impl::approve(ref state, SPENDER(), VALUE); + testing::pop_log_raw(ZERO()); assert(ERC20::increase_allowance(ref state, SPENDER(), VALUE), 'Should return true'); + + assert_only_event_approval(OWNER(), SPENDER(), VALUE * 2); assert(ERC20Impl::allowance(@state, OWNER(), SPENDER()) == VALUE * 2, 'Should be amount * 2'); } @@ -394,7 +402,7 @@ fn test_increase_allowance() { #[should_panic(expected: ('ERC20: approve to 0', ))] fn test_increase_allowance_to_zero_address() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC20::increase_allowance(ref state, Zeroable::zero(), VALUE); } @@ -410,10 +418,13 @@ fn test_increase_allowance_from_zero_address() { #[available_gas(2000000)] fn test_increaseAllowance() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC20Impl::approve(ref state, SPENDER(), VALUE); + testing::pop_log_raw(ZERO()); assert(ERC20::increaseAllowance(ref state, SPENDER(), VALUE), 'Should return true'); + + assert_only_event_approval(OWNER(), SPENDER(), 2 * VALUE); assert(ERC20Impl::allowance(@state, OWNER(), SPENDER()) == VALUE * 2, 'Should be amount * 2'); } @@ -422,7 +433,7 @@ fn test_increaseAllowance() { #[should_panic(expected: ('ERC20: approve to 0', ))] fn test_increaseAllowance_to_zero_address() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC20::increaseAllowance(ref state, Zeroable::zero(), VALUE); } @@ -442,10 +453,13 @@ fn test_increaseAllowance_from_zero_address() { #[available_gas(2000000)] fn test_decrease_allowance() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC20Impl::approve(ref state, SPENDER(), VALUE); + testing::pop_log_raw(ZERO()); assert(ERC20::decrease_allowance(ref state, SPENDER(), VALUE), 'Should return true'); + + assert_only_event_approval(OWNER(), SPENDER(), 0); assert(ERC20Impl::allowance(@state, OWNER(), SPENDER()) == VALUE - VALUE, 'Should be 0'); } @@ -454,7 +468,7 @@ fn test_decrease_allowance() { #[should_panic(expected: ('u256_sub Overflow', ))] fn test_decrease_allowance_to_zero_address() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC20::decrease_allowance(ref state, Zeroable::zero(), VALUE); } @@ -470,10 +484,13 @@ fn test_decrease_allowance_from_zero_address() { #[available_gas(2000000)] fn test_decreaseAllowance() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC20Impl::approve(ref state, SPENDER(), VALUE); + testing::pop_log_raw(ZERO()); assert(ERC20::decreaseAllowance(ref state, SPENDER(), VALUE), 'Should return true'); + + assert_only_event_approval(OWNER(), SPENDER(), 0); assert(ERC20Impl::allowance(@state, OWNER(), SPENDER()) == VALUE - VALUE, 'Should be 0'); } @@ -482,7 +499,7 @@ fn test_decreaseAllowance() { #[should_panic(expected: ('u256_sub Overflow', ))] fn test_decreaseAllowance_to_zero_address() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC20::decreaseAllowance(ref state, Zeroable::zero(), VALUE); } @@ -504,7 +521,11 @@ fn test__spend_allowance_not_unlimited() { let mut state = setup(); InternalImpl::_approve(ref state, OWNER(), SPENDER(), SUPPLY); + testing::pop_log_raw(ZERO()); + InternalImpl::_spend_allowance(ref state, OWNER(), SPENDER(), VALUE); + + assert_only_event_approval(OWNER(), SPENDER(), SUPPLY - VALUE); assert( ERC20Impl::allowance(@state, OWNER(), SPENDER()) == SUPPLY - VALUE, 'Should eq supply - amount' @@ -536,6 +557,7 @@ fn test__mint() { let mut state = STATE(); InternalImpl::_mint(ref state, OWNER(), VALUE); + assert_only_event_transfer(ZERO(), OWNER(), VALUE); assert(ERC20Impl::balance_of(@state, OWNER()) == VALUE, 'Should eq amount'); assert(ERC20Impl::total_supply(@state) == VALUE, 'Should eq total supply'); } @@ -558,6 +580,7 @@ fn test__burn() { let mut state = setup(); InternalImpl::_burn(ref state, OWNER(), VALUE); + assert_only_event_transfer(OWNER(), ZERO(), VALUE); assert(ERC20Impl::total_supply(@state) == SUPPLY - VALUE, 'Should eq supply - amount'); assert(ERC20Impl::balance_of(@state, OWNER()) == SUPPLY - VALUE, 'Should eq supply - amount'); } @@ -569,3 +592,31 @@ fn test__burn_from_zero() { let mut state = setup(); InternalImpl::_burn(ref state, Zeroable::zero(), VALUE); } + +// +// Helpers +// + +fn assert_event_approval(owner: ContractAddress, spender: ContractAddress, value: u256) { + let event = testing::pop_log::(ZERO()).unwrap(); + assert(event.owner == owner, 'Invalid `owner`'); + assert(event.spender == spender, 'Invalid `spender`'); + assert(event.value == value, 'Invalid `value`'); +} + +fn assert_only_event_approval(owner: ContractAddress, spender: ContractAddress, value: u256) { + assert_event_approval(owner, spender, value); + utils::assert_no_events_left(ZERO()); +} + +fn assert_event_transfer(from: ContractAddress, to: ContractAddress, value: u256) { + let event = testing::pop_log::(ZERO()).unwrap(); + assert(event.from == from, 'Invalid `from`'); + assert(event.to == to, 'Invalid `to`'); + assert(event.value == value, 'Invalid `value`'); +} + +fn assert_only_event_transfer(from: ContractAddress, to: ContractAddress, value: u256) { + assert_event_transfer(from, to, value); + utils::assert_no_events_left(ZERO()); +} diff --git a/src/tests/utils/constants.cairo b/src/tests/utils/constants.cairo index f6fc94a3f..c27c2f1ce 100644 --- a/src/tests/utils/constants.cairo +++ b/src/tests/utils/constants.cairo @@ -1,6 +1,11 @@ use starknet::ContractAddress; use starknet::contract_address_const; +const NAME: felt252 = 'NAME'; +const SYMBOL: felt252 = 'SYMBOL'; +const DECIMALS: u8 = 18_u8; +const SUPPLY: u256 = 2000; +const VALUE: u256 = 300; const ROLE: felt252 = 'ROLE'; const OTHER_ROLE: felt252 = 'OTHER_ROLE'; @@ -35,3 +40,11 @@ fn OTHER() -> ContractAddress { fn OTHER_ADMIN() -> ContractAddress { contract_address_const::<'OTHER_ADMIN'>() } + +fn SPENDER() -> ContractAddress { + contract_address_const::<'SPENDER'>() +} + +fn RECIPIENT() -> ContractAddress { + contract_address_const::<'RECIPIENT'>() +} From 77089c151cc99e94ed5f34bd8372fe6af59893cd Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Tue, 15 Aug 2023 18:45:56 -0400 Subject: [PATCH 088/246] Add mint to erc721 preset constructor (#700) * add _mint to constructor * add constructor assertions * fix formatting --- src/tests/token/test_erc721.cairo | 5 +++-- src/token/erc721/erc721.cairo | 9 ++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/tests/token/test_erc721.cairo b/src/tests/token/test_erc721.cairo index 45bd6c39e..51731a13b 100644 --- a/src/tests/token/test_erc721.cairo +++ b/src/tests/token/test_erc721.cairo @@ -104,11 +104,12 @@ fn setup_camel_account() -> ContractAddress { #[available_gas(20000000)] fn test_constructor() { let mut state = STATE(); - ERC721::constructor(ref state, NAME, SYMBOL); + ERC721::constructor(ref state, NAME, SYMBOL, OWNER(), TOKEN_ID); assert(ERC721MetadataImpl::name(@state) == NAME, 'Name should be NAME'); assert(ERC721MetadataImpl::symbol(@state) == SYMBOL, 'Symbol should be SYMBOL'); - assert(ERC721Impl::balance_of(@state, OWNER()) == 0, 'Balance should be zero'); + assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance should be one'); + assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'OWNER should be owner'); assert( SRC5Impl::supports_interface(@state, erc721::interface::IERC721_ID), 'Missing interface ID' diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 5a5b56b51..32a265c06 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -61,8 +61,15 @@ mod ERC721 { } #[constructor] - fn constructor(ref self: ContractState, name: felt252, symbol: felt252) { + fn constructor( + ref self: ContractState, + name: felt252, + symbol: felt252, + recipient: ContractAddress, + token_id: u256 + ) { self.initializer(name, symbol); + self._mint(recipient, token_id); } // From 3e08fa6d262d7c50422b760b6b945aa2bfaa6cbb Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 17 Aug 2023 00:50:04 +0200 Subject: [PATCH 089/246] Add testing for ERC721 events (#678) * feat: add testing for events * feat: add tests for ownable events * feat: add tests for pausable events * feat: add tests for erc20 events * feat: add tests for erc721 events * Update src/openzeppelin/tests/security/test_pausable.cairo Co-authored-by: Andrew Fleming * feat: apply review updates * feat: add test helper for events * feat: improve tests * feat: apply review updates * feat: apply review updates * feat: apply review updates * feat: apply review updates * feat: apply review updates --------- Co-authored-by: Andrew Fleming --- src/tests/access/test_accesscontrol.cairo | 10 +- .../access/test_dual_accesscontrol.cairo | 4 +- src/tests/access/test_ownable.cairo | 4 +- src/tests/security/test_pausable.cairo | 6 +- src/tests/token/test_erc20.cairo | 20 +- src/tests/token/test_erc721.cairo | 396 +++++++++++------- src/tests/utils.cairo | 6 +- src/tests/utils/constants.cairo | 7 + 8 files changed, 272 insertions(+), 181 deletions(-) diff --git a/src/tests/access/test_accesscontrol.cairo b/src/tests/access/test_accesscontrol.cairo index cea50e7d4..8dd4af473 100644 --- a/src/tests/access/test_accesscontrol.cairo +++ b/src/tests/access/test_accesscontrol.cairo @@ -27,7 +27,7 @@ fn STATE() -> AccessControl::ContractState { fn setup() -> AccessControl::ContractState { let mut state = STATE(); InternalImpl::_grant_role(ref state, DEFAULT_ADMIN_ROLE, ADMIN()); - testing::pop_log_raw(ZERO()); + utils::drop_event(ZERO()); state } @@ -224,7 +224,7 @@ fn test_revoke_role_for_granted_role() { testing::set_caller_address(ADMIN()); AccessControlImpl::grant_role(ref state, ROLE, AUTHORIZED()); - testing::pop_log_raw(ZERO()); + utils::drop_event(ZERO()); AccessControlImpl::revoke_role(ref state, ROLE, AUTHORIZED()); @@ -239,7 +239,7 @@ fn test_revokeRole_for_granted_role() { testing::set_caller_address(ADMIN()); AccessControlCamelImpl::grantRole(ref state, ROLE, AUTHORIZED()); - testing::pop_log_raw(ZERO()); + utils::drop_event(ZERO()); AccessControlCamelImpl::revokeRole(ref state, ROLE, AUTHORIZED()); @@ -320,7 +320,7 @@ fn test_renounce_role_for_granted_role() { testing::set_caller_address(ADMIN()); AccessControlImpl::grant_role(ref state, ROLE, AUTHORIZED()); - testing::pop_log_raw(ZERO()); + utils::drop_event(ZERO()); testing::set_caller_address(AUTHORIZED()); AccessControlImpl::renounce_role(ref state, ROLE, AUTHORIZED()); @@ -336,7 +336,7 @@ fn test_renounceRole_for_granted_role() { testing::set_caller_address(ADMIN()); AccessControlCamelImpl::grantRole(ref state, ROLE, AUTHORIZED()); - testing::pop_log_raw(ZERO()); + utils::drop_event(ZERO()); testing::set_caller_address(AUTHORIZED()); AccessControlCamelImpl::renounceRole(ref state, ROLE, AUTHORIZED()); diff --git a/src/tests/access/test_dual_accesscontrol.cairo b/src/tests/access/test_dual_accesscontrol.cairo index 2d6a17be4..094f44a21 100644 --- a/src/tests/access/test_dual_accesscontrol.cairo +++ b/src/tests/access/test_dual_accesscontrol.cairo @@ -1,10 +1,10 @@ use array::ArrayTrait; use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; use openzeppelin::access::accesscontrol::interface::IACCESSCONTROL_ID; -use openzeppelin::access::accesscontrol::interface::IAccessControlDispatcher; -use openzeppelin::access::accesscontrol::interface::IAccessControlCamelDispatcher; use openzeppelin::access::accesscontrol::interface::IAccessControlDispatcherTrait; +use openzeppelin::access::accesscontrol::interface::IAccessControlDispatcher; use openzeppelin::access::accesscontrol::interface::IAccessControlCamelDispatcherTrait; +use openzeppelin::access::accesscontrol::interface::IAccessControlCamelDispatcher; use openzeppelin::access::accesscontrol::dual_accesscontrol::DualCaseAccessControlTrait; use openzeppelin::access::accesscontrol::dual_accesscontrol::DualCaseAccessControl; use openzeppelin::tests::mocks::snake_accesscontrol_mock::SnakeAccessControlMock; diff --git a/src/tests/access/test_ownable.cairo b/src/tests/access/test_ownable.cairo index c27af64d6..214329b9b 100644 --- a/src/tests/access/test_ownable.cairo +++ b/src/tests/access/test_ownable.cairo @@ -1,9 +1,9 @@ -use openzeppelin::access::ownable::Ownable; use openzeppelin::access::ownable::Ownable::InternalImpl; use openzeppelin::access::ownable::Ownable::OwnableCamelOnlyImpl; use openzeppelin::access::ownable::Ownable::OwnableImpl; use openzeppelin::access::ownable::Ownable::OwnershipTransferred; use openzeppelin::access::ownable::Ownable::_owner::InternalContractStateTrait; +use openzeppelin::access::ownable::Ownable; use openzeppelin::tests::utils::constants::{ZERO, OTHER, OWNER}; use openzeppelin::tests::utils; use option::OptionTrait; @@ -23,7 +23,7 @@ fn STATE() -> Ownable::ContractState { fn setup() -> Ownable::ContractState { let mut state = STATE(); InternalImpl::initializer(ref state, OWNER()); - testing::pop_log_raw(ZERO()); + utils::drop_event(ZERO()); state } diff --git a/src/tests/security/test_pausable.cairo b/src/tests/security/test_pausable.cairo index 7f3feb8e7..f48a2904b 100644 --- a/src/tests/security/test_pausable.cairo +++ b/src/tests/security/test_pausable.cairo @@ -111,7 +111,7 @@ fn test_unpause_when_paused() { testing::set_caller_address(CALLER()); InternalImpl::_pause(ref state); - testing::pop_log_raw(ZERO()); + utils::drop_event(ZERO()); InternalImpl::_unpause(ref state); @@ -133,13 +133,13 @@ fn test_unpause_when_unpaused() { // fn assert_event_paused(account: ContractAddress) { - let event = testing::pop_log::(ZERO()).unwrap(); + let event = utils::pop_log::(ZERO()).unwrap(); assert(event.account == account, 'Invalid `account`'); utils::assert_no_events_left(ZERO()); } fn assert_event_unpaused(account: ContractAddress) { - let event = testing::pop_log::(ZERO()).unwrap(); + let event = utils::pop_log::(ZERO()).unwrap(); assert(event.account == account, 'Invalid `account`'); utils::assert_no_events_left(ZERO()); } diff --git a/src/tests/token/test_erc20.cairo b/src/tests/token/test_erc20.cairo index dd31d8e4d..a2e609a1b 100644 --- a/src/tests/token/test_erc20.cairo +++ b/src/tests/token/test_erc20.cairo @@ -29,7 +29,7 @@ fn STATE() -> ERC20::ContractState { fn setup() -> ERC20::ContractState { let mut state = STATE(); ERC20::constructor(ref state, NAME, SYMBOL, SUPPLY, OWNER()); - testing::pop_log_raw(ZERO()); + utils::drop_event(ZERO()); state } @@ -242,7 +242,7 @@ fn test_transfer_from() { let mut state = setup(); testing::set_caller_address(OWNER()); ERC20Impl::approve(ref state, SPENDER(), VALUE); - testing::pop_log_raw(ZERO()); + utils::drop_event(ZERO()); testing::set_caller_address(SPENDER()); assert(ERC20Impl::transfer_from(ref state, OWNER(), RECIPIENT(), VALUE), 'Should return true'); @@ -311,7 +311,7 @@ fn test_transferFrom() { let mut state = setup(); testing::set_caller_address(OWNER()); ERC20Impl::approve(ref state, SPENDER(), VALUE); - testing::pop_log_raw(ZERO()); + utils::drop_event(ZERO()); testing::set_caller_address(SPENDER()); assert( @@ -389,7 +389,7 @@ fn test_increase_allowance() { let mut state = setup(); testing::set_caller_address(OWNER()); ERC20Impl::approve(ref state, SPENDER(), VALUE); - testing::pop_log_raw(ZERO()); + utils::drop_event(ZERO()); assert(ERC20::increase_allowance(ref state, SPENDER(), VALUE), 'Should return true'); @@ -420,7 +420,7 @@ fn test_increaseAllowance() { let mut state = setup(); testing::set_caller_address(OWNER()); ERC20Impl::approve(ref state, SPENDER(), VALUE); - testing::pop_log_raw(ZERO()); + utils::drop_event(ZERO()); assert(ERC20::increaseAllowance(ref state, SPENDER(), VALUE), 'Should return true'); @@ -455,7 +455,7 @@ fn test_decrease_allowance() { let mut state = setup(); testing::set_caller_address(OWNER()); ERC20Impl::approve(ref state, SPENDER(), VALUE); - testing::pop_log_raw(ZERO()); + utils::drop_event(ZERO()); assert(ERC20::decrease_allowance(ref state, SPENDER(), VALUE), 'Should return true'); @@ -486,7 +486,7 @@ fn test_decreaseAllowance() { let mut state = setup(); testing::set_caller_address(OWNER()); ERC20Impl::approve(ref state, SPENDER(), VALUE); - testing::pop_log_raw(ZERO()); + utils::drop_event(ZERO()); assert(ERC20::decreaseAllowance(ref state, SPENDER(), VALUE), 'Should return true'); @@ -521,7 +521,7 @@ fn test__spend_allowance_not_unlimited() { let mut state = setup(); InternalImpl::_approve(ref state, OWNER(), SPENDER(), SUPPLY); - testing::pop_log_raw(ZERO()); + utils::drop_event(ZERO()); InternalImpl::_spend_allowance(ref state, OWNER(), SPENDER(), VALUE); @@ -598,7 +598,7 @@ fn test__burn_from_zero() { // fn assert_event_approval(owner: ContractAddress, spender: ContractAddress, value: u256) { - let event = testing::pop_log::(ZERO()).unwrap(); + let event = utils::pop_log::(ZERO()).unwrap(); assert(event.owner == owner, 'Invalid `owner`'); assert(event.spender == spender, 'Invalid `spender`'); assert(event.value == value, 'Invalid `value`'); @@ -610,7 +610,7 @@ fn assert_only_event_approval(owner: ContractAddress, spender: ContractAddress, } fn assert_event_transfer(from: ContractAddress, to: ContractAddress, value: u256) { - let event = testing::pop_log::(ZERO()).unwrap(); + let event = utils::pop_log::(ZERO()).unwrap(); assert(event.from == from, 'Invalid `from`'); assert(event.to == to, 'Invalid `to`'); assert(event.value == value, 'Invalid `value`'); diff --git a/src/tests/token/test_erc721.cairo b/src/tests/token/test_erc721.cairo index 51731a13b..b39b24a01 100644 --- a/src/tests/token/test_erc721.cairo +++ b/src/tests/token/test_erc721.cairo @@ -1,9 +1,9 @@ use ERC721::_owners::InternalContractStateTrait as OwnersTrait; use ERC721::_token_approvals::InternalContractStateTrait as TokenApprovalsTrait; + use array::ArrayTrait; use integer::u256; use integer::u256_from_felt252; - use openzeppelin::account::Account; use openzeppelin::introspection::src5; use openzeppelin::introspection; @@ -13,50 +13,23 @@ use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; use openzeppelin::tests::mocks::erc721_receiver::FAILURE; use openzeppelin::tests::mocks::erc721_receiver::SUCCESS; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; +use openzeppelin::tests::utils::constants::{ + ZERO, OWNER, RECIPIENT, SPENDER, OPERATOR, OTHER, NAME, SYMBOL, URI, TOKEN_ID, PUBKEY, +}; use openzeppelin::tests::utils; -use openzeppelin::token::erc721::ERC721::ERC721CamelOnlyImpl; -use openzeppelin::token::erc721::ERC721::ERC721Impl; -use openzeppelin::token::erc721::ERC721::ERC721MetadataCamelOnlyImpl; -use openzeppelin::token::erc721::ERC721::ERC721MetadataImpl; -use openzeppelin::token::erc721::ERC721::InternalImpl; -use openzeppelin::token::erc721::ERC721::SRC5CamelImpl; -use openzeppelin::token::erc721::ERC721::SRC5Impl; +use openzeppelin::token::erc721::ERC721::{ + Approval, ApprovalForAll, ERC721CamelOnlyImpl, ERC721Impl, ERC721MetadataCamelOnlyImpl, + ERC721MetadataImpl, InternalImpl, SRC5CamelImpl, SRC5Impl, Transfer, +}; use openzeppelin::token::erc721::ERC721; use openzeppelin::token::erc721; -use starknet::ContractAddress; +use option::OptionTrait; use starknet::contract_address_const; -use starknet::testing::set_caller_address; +use starknet::ContractAddress; +use starknet::testing; use traits::Into; use zeroable::Zeroable; -const NAME: felt252 = 111; -const SYMBOL: felt252 = 222; -const URI: felt252 = 333; -const TOKEN_ID: u256 = 7; -const PUBKEY: felt252 = 444; - -fn STATE() -> ERC721::ContractState { - ERC721::contract_state_for_testing() -} -fn ZERO() -> ContractAddress { - Zeroable::zero() -} -fn OWNER() -> ContractAddress { - contract_address_const::<10>() -} -fn RECIPIENT() -> ContractAddress { - contract_address_const::<20>() -} -fn SPENDER() -> ContractAddress { - contract_address_const::<30>() -} -fn OPERATOR() -> ContractAddress { - contract_address_const::<40>() -} -fn OTHER() -> ContractAddress { - contract_address_const::<50>() -} - fn DATA(success: bool) -> Span { let mut data = array![]; if success { @@ -71,10 +44,15 @@ fn DATA(success: bool) -> Span { // Setup // +fn STATE() -> ERC721::ContractState { + ERC721::contract_state_for_testing() +} + fn setup() -> ERC721::ContractState { let mut state = STATE(); InternalImpl::initializer(ref state, NAME, SYMBOL); InternalImpl::_mint(ref state, OWNER(), TOKEN_ID); + utils::drop_event(ZERO()); state } @@ -235,8 +213,10 @@ fn test__exists() { fn test_approve_from_owner() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC721Impl::approve(ref state, SPENDER(), TOKEN_ID); + assert_event_approval(OWNER(), SPENDER(), TOKEN_ID); + assert( ERC721Impl::get_approved(@state, TOKEN_ID) == SPENDER(), 'Spender not approved correctly' ); @@ -247,11 +227,14 @@ fn test_approve_from_owner() { fn test_approve_from_operator() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); + utils::drop_event(ZERO()); - set_caller_address(OPERATOR()); + testing::set_caller_address(OPERATOR()); ERC721Impl::approve(ref state, SPENDER(), TOKEN_ID); + assert_event_approval(OWNER(), SPENDER(), TOKEN_ID); + assert( ERC721Impl::get_approved(@state, TOKEN_ID) == SPENDER(), 'Spender not approved correctly' ); @@ -263,7 +246,7 @@ fn test_approve_from_operator() { fn test_approve_from_unauthorized() { let mut state = setup(); - set_caller_address(OTHER()); + testing::set_caller_address(OTHER()); ERC721Impl::approve(ref state, SPENDER(), TOKEN_ID); } @@ -273,7 +256,7 @@ fn test_approve_from_unauthorized() { fn test_approve_to_owner() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC721Impl::approve(ref state, OWNER(), TOKEN_ID); } @@ -290,6 +273,8 @@ fn test_approve_nonexistent() { fn test__approve() { let mut state = setup(); InternalImpl::_approve(ref state, SPENDER(), TOKEN_ID); + assert_event_approval(OWNER(), SPENDER(), TOKEN_ID); + assert( ERC721Impl::get_approved(@state, TOKEN_ID) == SPENDER(), 'Spender not approved correctly' ); @@ -319,17 +304,21 @@ fn test__approve_nonexistent() { #[available_gas(20000000)] fn test_set_approval_for_all() { let mut state = STATE(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); assert(!ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), 'Invalid default value'); ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); + assert_event_approval_for_all(OWNER(), OPERATOR(), true); + assert( ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), 'Operator not approved correctly' ); ERC721Impl::set_approval_for_all(ref state, OPERATOR(), false); + assert_event_approval_for_all(OWNER(), OPERATOR(), false); + assert( !ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), 'Approval not revoked correctly' @@ -341,7 +330,7 @@ fn test_set_approval_for_all() { #[should_panic(expected: ('ERC721: self approval', ))] fn test_set_approval_for_all_owner_equal_operator_true() { let mut state = STATE(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC721Impl::set_approval_for_all(ref state, OWNER(), true); } @@ -350,7 +339,7 @@ fn test_set_approval_for_all_owner_equal_operator_true() { #[should_panic(expected: ('ERC721: self approval', ))] fn test_set_approval_for_all_owner_equal_operator_false() { let mut state = STATE(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC721Impl::set_approval_for_all(ref state, OWNER(), false); } @@ -361,12 +350,16 @@ fn test__set_approval_for_all() { assert(!ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), 'Invalid default value'); InternalImpl::_set_approval_for_all(ref state, OWNER(), OPERATOR(), true); + assert_event_approval_for_all(OWNER(), OPERATOR(), true); + assert( ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), 'Operator not approved correctly' ); InternalImpl::_set_approval_for_all(ref state, OWNER(), OPERATOR(), false); + assert_event_approval_for_all(OWNER(), OPERATOR(), false); + assert( !ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), 'Operator not approved correctly' @@ -402,14 +395,16 @@ fn test_transfer_from_owner() { let recipient = RECIPIENT(); // set approval to check reset InternalImpl::_approve(ref state, OTHER(), token_id); + utils::drop_event(ZERO()); - assert_state_before_transfer(token_id, owner, recipient); + assert_state_before_transfer(owner, recipient, token_id); assert(ERC721Impl::get_approved(@state, token_id) == OTHER(), 'Approval not implicitly reset'); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::transfer_from(ref state, owner, recipient, token_id); + assert_event_transfer(owner, recipient, token_id); - assert_state_after_transfer(token_id, owner, recipient); + assert_state_after_transfer(owner, recipient, token_id); } #[test] @@ -421,14 +416,16 @@ fn test_transferFrom_owner() { let recipient = RECIPIENT(); // set approval to check reset InternalImpl::_approve(ref state, OTHER(), token_id); + utils::drop_event(ZERO()); - assert_state_before_transfer(token_id, owner, recipient); + assert_state_before_transfer(owner, recipient, token_id); assert(ERC721Impl::get_approved(@state, token_id) == OTHER(), 'Approval not implicitly reset'); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721CamelOnlyImpl::transferFrom(ref state, owner, recipient, token_id); + assert_event_transfer(owner, recipient, token_id); - assert_state_after_transfer(token_id, owner, recipient); + assert_state_after_transfer(owner, recipient, token_id); } #[test] @@ -452,7 +449,7 @@ fn test_transferFrom_nonexistent() { #[should_panic(expected: ('ERC721: invalid receiver', ))] fn test_transfer_from_to_zero() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC721Impl::transfer_from(ref state, OWNER(), ZERO(), TOKEN_ID); } @@ -462,7 +459,7 @@ fn test_transfer_from_to_zero() { fn test_transferFrom_to_zero() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC721CamelOnlyImpl::transferFrom(ref state, OWNER(), ZERO(), TOKEN_ID); } @@ -474,8 +471,9 @@ fn test_transfer_from_to_owner() { assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership before'); assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner before'); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC721Impl::transfer_from(ref state, OWNER(), OWNER(), TOKEN_ID); + assert_event_transfer(OWNER(), OWNER(), TOKEN_ID); assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership after'); assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner after'); @@ -489,8 +487,9 @@ fn test_transferFrom_to_owner() { assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership before'); assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner before'); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC721CamelOnlyImpl::transferFrom(ref state, OWNER(), OWNER(), TOKEN_ID); + assert_event_transfer(OWNER(), OWNER(), TOKEN_ID); assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership after'); assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner after'); @@ -503,15 +502,17 @@ fn test_transfer_from_approved() { let token_id = TOKEN_ID; let owner = OWNER(); let recipient = RECIPIENT(); - assert_state_before_transfer(token_id, owner, recipient); + assert_state_before_transfer(owner, recipient, token_id); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::approve(ref state, OPERATOR(), token_id); + utils::drop_event(ZERO()); - set_caller_address(OPERATOR()); + testing::set_caller_address(OPERATOR()); ERC721Impl::transfer_from(ref state, owner, recipient, token_id); + assert_event_transfer(owner, recipient, token_id); - assert_state_after_transfer(token_id, owner, recipient); + assert_state_after_transfer(owner, recipient, token_id); } #[test] @@ -521,15 +522,17 @@ fn test_transferFrom_approved() { let token_id = TOKEN_ID; let owner = OWNER(); let recipient = RECIPIENT(); - assert_state_before_transfer(token_id, owner, recipient); + assert_state_before_transfer(owner, recipient, token_id); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::approve(ref state, OPERATOR(), token_id); + utils::drop_event(ZERO()); - set_caller_address(OPERATOR()); + testing::set_caller_address(OPERATOR()); ERC721CamelOnlyImpl::transferFrom(ref state, owner, recipient, token_id); + assert_event_transfer(owner, recipient, token_id); - assert_state_after_transfer(token_id, owner, recipient); + assert_state_after_transfer(owner, recipient, token_id); } #[test] @@ -540,15 +543,17 @@ fn test_transfer_from_approved_for_all() { let owner = OWNER(); let recipient = RECIPIENT(); - assert_state_before_transfer(token_id, owner, recipient); + assert_state_before_transfer(owner, recipient, token_id); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); + utils::drop_event(ZERO()); - set_caller_address(OPERATOR()); + testing::set_caller_address(OPERATOR()); ERC721Impl::transfer_from(ref state, owner, recipient, token_id); + assert_event_transfer(owner, recipient, token_id); - assert_state_after_transfer(token_id, owner, recipient); + assert_state_after_transfer(owner, recipient, token_id); } #[test] @@ -559,15 +564,17 @@ fn test_transferFrom_approved_for_all() { let owner = OWNER(); let recipient = RECIPIENT(); - assert_state_before_transfer(token_id, owner, recipient); + assert_state_before_transfer(owner, recipient, token_id); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); + utils::drop_event(ZERO()); - set_caller_address(OPERATOR()); + testing::set_caller_address(OPERATOR()); ERC721CamelOnlyImpl::transferFrom(ref state, owner, recipient, token_id); + assert_event_transfer(owner, recipient, token_id); - assert_state_after_transfer(token_id, owner, recipient); + assert_state_after_transfer(owner, recipient, token_id); } #[test] @@ -575,7 +582,7 @@ fn test_transferFrom_approved_for_all() { #[should_panic(expected: ('ERC721: unauthorized caller', ))] fn test_transfer_from_unauthorized() { let mut state = setup(); - set_caller_address(OTHER()); + testing::set_caller_address(OTHER()); ERC721Impl::transfer_from(ref state, OWNER(), RECIPIENT(), TOKEN_ID); } @@ -584,7 +591,7 @@ fn test_transfer_from_unauthorized() { #[should_panic(expected: ('ERC721: unauthorized caller', ))] fn test_transferFrom_unauthorized() { let mut state = setup(); - set_caller_address(OTHER()); + testing::set_caller_address(OTHER()); ERC721CamelOnlyImpl::transferFrom(ref state, OWNER(), RECIPIENT(), TOKEN_ID); } @@ -600,12 +607,13 @@ fn test_safe_transfer_from_to_account() { let token_id = TOKEN_ID; let owner = OWNER(); - assert_state_before_transfer(token_id, owner, account); + assert_state_before_transfer(owner, account, token_id); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::safe_transfer_from(ref state, owner, account, token_id, DATA(true)); + assert_event_transfer(owner, account, token_id); - assert_state_after_transfer(token_id, owner, account); + assert_state_after_transfer(owner, account, token_id); } #[test] @@ -616,12 +624,13 @@ fn test_safeTransferFrom_to_account() { let token_id = TOKEN_ID; let owner = OWNER(); - assert_state_before_transfer(token_id, owner, account); + assert_state_before_transfer(owner, account, token_id); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, account, token_id, DATA(true)); + assert_event_transfer(owner, account, token_id); - assert_state_after_transfer(token_id, owner, account); + assert_state_after_transfer(owner, account, token_id); } #[test] @@ -632,12 +641,13 @@ fn test_safe_transfer_from_to_account_camel() { let token_id = TOKEN_ID; let owner = OWNER(); - assert_state_before_transfer(token_id, owner, account); + assert_state_before_transfer(owner, account, token_id); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::safe_transfer_from(ref state, owner, account, token_id, DATA(true)); + assert_event_transfer(owner, account, token_id); - assert_state_after_transfer(token_id, owner, account); + assert_state_after_transfer(owner, account, token_id); } #[test] @@ -648,12 +658,13 @@ fn test_safeTransferFrom_to_account_camel() { let token_id = TOKEN_ID; let owner = OWNER(); - assert_state_before_transfer(token_id, owner, account); + assert_state_before_transfer(owner, account, token_id); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, account, token_id, DATA(true)); + assert_event_transfer(owner, account, token_id); - assert_state_after_transfer(token_id, owner, account); + assert_state_after_transfer(owner, account, token_id); } #[test] @@ -664,12 +675,13 @@ fn test_safe_transfer_from_to_receiver() { let token_id = TOKEN_ID; let owner = OWNER(); - assert_state_before_transfer(token_id, owner, receiver); + assert_state_before_transfer(owner, receiver, token_id); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); - assert_state_after_transfer(token_id, owner, receiver); + assert_state_after_transfer(owner, receiver, token_id); } #[test] @@ -680,12 +692,13 @@ fn test_safeTransferFrom_to_receiver() { let token_id = TOKEN_ID; let owner = OWNER(); - assert_state_before_transfer(token_id, owner, receiver); + assert_state_before_transfer(owner, receiver, token_id); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); - assert_state_after_transfer(token_id, owner, receiver); + assert_state_after_transfer(owner, receiver, token_id); } #[test] @@ -696,12 +709,13 @@ fn test_safe_transfer_from_to_receiver_camel() { let token_id = TOKEN_ID; let owner = OWNER(); - assert_state_before_transfer(token_id, owner, receiver); + assert_state_before_transfer(owner, receiver, token_id); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); - assert_state_after_transfer(token_id, owner, receiver); + assert_state_after_transfer(owner, receiver, token_id); } #[test] @@ -712,12 +726,13 @@ fn test_safeTransferFrom_to_receiver_camel() { let token_id = TOKEN_ID; let owner = OWNER(); - assert_state_before_transfer(token_id, owner, receiver); + assert_state_before_transfer(owner, receiver, token_id); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); - assert_state_after_transfer(token_id, owner, receiver); + assert_state_after_transfer(owner, receiver, token_id); } #[test] @@ -729,7 +744,7 @@ fn test_safe_transfer_from_to_receiver_failure() { let token_id = TOKEN_ID; let owner = OWNER(); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(false)); } @@ -742,7 +757,7 @@ fn test_safeTransferFrom_to_receiver_failure() { let token_id = TOKEN_ID; let owner = OWNER(); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(false)); } @@ -755,7 +770,7 @@ fn test_safe_transfer_from_to_receiver_failure_camel() { let token_id = TOKEN_ID; let owner = OWNER(); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(false)); } @@ -768,7 +783,7 @@ fn test_safeTransferFrom_to_receiver_failure_camel() { let token_id = TOKEN_ID; let owner = OWNER(); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(false)); } @@ -781,7 +796,7 @@ fn test_safe_transfer_from_to_non_receiver() { let token_id = TOKEN_ID; let owner = OWNER(); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::safe_transfer_from(ref state, owner, recipient, token_id, DATA(true)); } @@ -794,7 +809,7 @@ fn test_safeTransferFrom_to_non_receiver() { let token_id = TOKEN_ID; let owner = OWNER(); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, recipient, token_id, DATA(true)); } @@ -819,7 +834,7 @@ fn test_safeTransferFrom_nonexistent() { #[should_panic(expected: ('ERC721: invalid receiver', ))] fn test_safe_transfer_from_to_zero() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC721Impl::safe_transfer_from(ref state, OWNER(), ZERO(), TOKEN_ID, DATA(true)); } @@ -828,7 +843,7 @@ fn test_safe_transfer_from_to_zero() { #[should_panic(expected: ('ERC721: invalid receiver', ))] fn test_safeTransferFrom_to_zero() { let mut state = setup(); - set_caller_address(OWNER()); + testing::set_caller_address(OWNER()); ERC721CamelOnlyImpl::safeTransferFrom(ref state, OWNER(), ZERO(), TOKEN_ID, DATA(true)); } @@ -840,12 +855,14 @@ fn test_safe_transfer_from_to_owner() { let owner = setup_receiver(); InternalImpl::initializer(ref state, NAME, SYMBOL); InternalImpl::_mint(ref state, owner, token_id); + utils::drop_event(ZERO()); assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::safe_transfer_from(ref state, owner, owner, token_id, DATA(true)); + assert_event_transfer(owner, owner, token_id); assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership after'); assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner after'); @@ -859,12 +876,14 @@ fn test_safeTransferFrom_to_owner() { let owner = setup_receiver(); InternalImpl::initializer(ref state, NAME, SYMBOL); InternalImpl::_mint(ref state, owner, token_id); + utils::drop_event(ZERO()); assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, owner, token_id, DATA(true)); + assert_event_transfer(owner, owner, token_id); assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership after'); assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner after'); @@ -878,12 +897,14 @@ fn test_safe_transfer_from_to_owner_camel() { let owner = setup_camel_receiver(); InternalImpl::initializer(ref state, NAME, SYMBOL); InternalImpl::_mint(ref state, owner, token_id); + utils::drop_event(ZERO()); assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::safe_transfer_from(ref state, owner, owner, token_id, DATA(true)); + assert_event_transfer(owner, owner, token_id); assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership after'); assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner after'); @@ -897,12 +918,14 @@ fn test_safeTransferFrom_to_owner_camel() { let owner = setup_camel_receiver(); InternalImpl::initializer(ref state, NAME, SYMBOL); InternalImpl::_mint(ref state, owner, token_id); + utils::drop_event(ZERO()); assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, owner, token_id, DATA(true)); + assert_event_transfer(owner, owner, token_id); assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership after'); assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner after'); @@ -916,15 +939,17 @@ fn test_safe_transfer_from_approved() { let token_id = TOKEN_ID; let owner = OWNER(); - assert_state_before_transfer(token_id, owner, receiver); + assert_state_before_transfer(owner, receiver, token_id); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::approve(ref state, OPERATOR(), token_id); + utils::drop_event(ZERO()); - set_caller_address(OPERATOR()); + testing::set_caller_address(OPERATOR()); ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); - assert_state_after_transfer(token_id, owner, receiver); + assert_state_after_transfer(owner, receiver, token_id); } #[test] @@ -935,15 +960,17 @@ fn test_safeTransferFrom_approved() { let token_id = TOKEN_ID; let owner = OWNER(); - assert_state_before_transfer(token_id, owner, receiver); + assert_state_before_transfer(owner, receiver, token_id); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::approve(ref state, OPERATOR(), token_id); + utils::drop_event(ZERO()); - set_caller_address(OPERATOR()); + testing::set_caller_address(OPERATOR()); ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); - assert_state_after_transfer(token_id, owner, receiver); + assert_state_after_transfer(owner, receiver, token_id); } #[test] @@ -954,15 +981,17 @@ fn test_safe_transfer_from_approved_camel() { let token_id = TOKEN_ID; let owner = OWNER(); - assert_state_before_transfer(token_id, owner, receiver); + assert_state_before_transfer(owner, receiver, token_id); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::approve(ref state, OPERATOR(), token_id); + utils::drop_event(ZERO()); - set_caller_address(OPERATOR()); + testing::set_caller_address(OPERATOR()); ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); - assert_state_after_transfer(token_id, owner, receiver); + assert_state_after_transfer(owner, receiver, token_id); } #[test] @@ -973,15 +1002,17 @@ fn test_safeTransferFrom_approved_camel() { let token_id = TOKEN_ID; let owner = OWNER(); - assert_state_before_transfer(token_id, owner, receiver); + assert_state_before_transfer(owner, receiver, token_id); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::approve(ref state, OPERATOR(), token_id); + utils::drop_event(ZERO()); - set_caller_address(OPERATOR()); + testing::set_caller_address(OPERATOR()); ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); - assert_state_after_transfer(token_id, owner, receiver); + assert_state_after_transfer(owner, receiver, token_id); } #[test] @@ -992,15 +1023,17 @@ fn test_safe_transfer_from_approved_for_all() { let token_id = TOKEN_ID; let owner = OWNER(); - assert_state_before_transfer(token_id, owner, receiver); + assert_state_before_transfer(owner, receiver, token_id); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); + utils::drop_event(ZERO()); - set_caller_address(OPERATOR()); + testing::set_caller_address(OPERATOR()); ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); - assert_state_after_transfer(token_id, owner, receiver); + assert_state_after_transfer(owner, receiver, token_id); } #[test] @@ -1011,15 +1044,17 @@ fn test_safeTransferFrom_approved_for_all() { let token_id = TOKEN_ID; let owner = OWNER(); - assert_state_before_transfer(token_id, owner, receiver); + assert_state_before_transfer(owner, receiver, token_id); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); + utils::drop_event(ZERO()); - set_caller_address(OPERATOR()); + testing::set_caller_address(OPERATOR()); ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); - assert_state_after_transfer(token_id, owner, receiver); + assert_state_after_transfer(owner, receiver, token_id); } #[test] @@ -1030,15 +1065,17 @@ fn test_safe_transfer_from_approved_for_all_camel() { let token_id = TOKEN_ID; let owner = OWNER(); - assert_state_before_transfer(token_id, owner, receiver); + assert_state_before_transfer(owner, receiver, token_id); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); + utils::drop_event(ZERO()); - set_caller_address(OPERATOR()); + testing::set_caller_address(OPERATOR()); ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); - assert_state_after_transfer(token_id, owner, receiver); + assert_state_after_transfer(owner, receiver, token_id); } #[test] @@ -1049,15 +1086,17 @@ fn test_safeTransferFrom_approved_for_all_camel() { let token_id = TOKEN_ID; let owner = OWNER(); - assert_state_before_transfer(token_id, owner, receiver); + assert_state_before_transfer(owner, receiver, token_id); - set_caller_address(owner); + testing::set_caller_address(owner); ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); + utils::drop_event(ZERO()); - set_caller_address(OPERATOR()); + testing::set_caller_address(OPERATOR()); ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); - assert_state_after_transfer(token_id, owner, receiver); + assert_state_after_transfer(owner, receiver, token_id); } #[test] @@ -1065,7 +1104,7 @@ fn test_safeTransferFrom_approved_for_all_camel() { #[should_panic(expected: ('ERC721: unauthorized caller', ))] fn test_safe_transfer_from_unauthorized() { let mut state = setup(); - set_caller_address(OTHER()); + testing::set_caller_address(OTHER()); ERC721Impl::safe_transfer_from(ref state, OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); } @@ -1074,7 +1113,7 @@ fn test_safe_transfer_from_unauthorized() { #[should_panic(expected: ('ERC721: unauthorized caller', ))] fn test_safeTransferFrom_unauthorized() { let mut state = setup(); - set_caller_address(OTHER()); + testing::set_caller_address(OTHER()); ERC721CamelOnlyImpl::safeTransferFrom(ref state, OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); } @@ -1090,9 +1129,12 @@ fn test__transfer() { let owner = OWNER(); let recipient = RECIPIENT(); - assert_state_before_transfer(token_id, owner, recipient); + assert_state_before_transfer(owner, recipient, token_id); + InternalImpl::_transfer(ref state, owner, recipient, token_id); - assert_state_after_transfer(token_id, owner, recipient); + assert_event_transfer(owner, recipient, token_id); + + assert_state_after_transfer(owner, recipient, token_id); } #[test] @@ -1131,8 +1173,10 @@ fn test__mint() { let token_id = TOKEN_ID; assert_state_before_mint(recipient); - InternalImpl::_mint(ref state, RECIPIENT(), TOKEN_ID); - assert_state_after_mint(token_id, recipient); + InternalImpl::_mint(ref state, recipient, TOKEN_ID); + assert_event_transfer(ZERO(), recipient, token_id); + + assert_state_after_mint(recipient, token_id); } #[test] @@ -1164,7 +1208,9 @@ fn test__safe_mint_to_receiver() { assert_state_before_mint(recipient); InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(true)); - assert_state_after_mint(token_id, recipient); + assert_event_transfer(ZERO(), recipient, token_id); + + assert_state_after_mint(recipient, token_id); } #[test] @@ -1176,7 +1222,9 @@ fn test__safe_mint_to_receiver_camel() { assert_state_before_mint(recipient); InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(true)); - assert_state_after_mint(token_id, recipient); + assert_event_transfer(ZERO(), recipient, token_id); + + assert_state_after_mint(recipient, token_id); } #[test] @@ -1188,7 +1236,9 @@ fn test__safe_mint_to_account() { assert_state_before_mint(account); InternalImpl::_safe_mint(ref state, account, token_id, DATA(true)); - assert_state_after_mint(token_id, account); + assert_event_transfer(ZERO(), account, token_id); + + assert_state_after_mint(account, token_id); } #[test] @@ -1200,7 +1250,9 @@ fn test__safe_mint_to_account_camel() { assert_state_before_mint(account); InternalImpl::_safe_mint(ref state, account, token_id, DATA(true)); - assert_state_after_mint(token_id, account); + assert_event_transfer(ZERO(), account, token_id); + + assert_state_after_mint(account, token_id); } #[test] @@ -1213,7 +1265,7 @@ fn test__safe_mint_to_non_receiver() { assert_state_before_mint(recipient); InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(true)); - assert_state_after_mint(token_id, recipient); + assert_state_after_mint(recipient, token_id); } #[test] @@ -1226,7 +1278,7 @@ fn test__safe_mint_to_receiver_failure() { assert_state_before_mint(recipient); InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(false)); - assert_state_after_mint(token_id, recipient); + assert_state_after_mint(recipient, token_id); } #[test] @@ -1239,7 +1291,7 @@ fn test__safe_mint_to_receiver_failure_camel() { assert_state_before_mint(recipient); InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(false)); - assert_state_after_mint(token_id, recipient); + assert_state_after_mint(recipient, token_id); } #[test] @@ -1268,12 +1320,14 @@ fn test__burn() { let mut state = setup(); InternalImpl::_approve(ref state, OTHER(), TOKEN_ID); + utils::drop_event(ZERO()); assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership before'); assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner before'); assert(ERC721Impl::get_approved(@state, TOKEN_ID) == OTHER(), 'Approval before'); InternalImpl::_burn(ref state, TOKEN_ID); + assert_event_transfer(OWNER(), ZERO(), TOKEN_ID); assert(state._owners.read(TOKEN_ID) == ZERO(), 'Ownership after'); assert(ERC721Impl::balance_of(@state, OWNER()) == 0, 'Balance of owner after'); @@ -1315,7 +1369,7 @@ fn test__set_token_uri_nonexistent() { // fn assert_state_before_transfer( - token_id: u256, owner: ContractAddress, recipient: ContractAddress + owner: ContractAddress, recipient: ContractAddress, token_id: u256 ) { let state = STATE(); assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); @@ -1323,7 +1377,7 @@ fn assert_state_before_transfer( assert(ERC721Impl::balance_of(@state, recipient) == 0, 'Balance of recipient before'); } -fn assert_state_after_transfer(token_id: u256, owner: ContractAddress, recipient: ContractAddress) { +fn assert_state_after_transfer(owner: ContractAddress, recipient: ContractAddress, token_id: u256) { let state = STATE(); assert(ERC721Impl::owner_of(@state, token_id) == recipient, 'Ownership after'); assert(ERC721Impl::balance_of(@state, owner) == 0, 'Balance of owner after'); @@ -1336,9 +1390,35 @@ fn assert_state_before_mint(recipient: ContractAddress) { assert(ERC721Impl::balance_of(@state, recipient) == 0, 'Balance of recipient before'); } -fn assert_state_after_mint(token_id: u256, recipient: ContractAddress) { +fn assert_state_after_mint(recipient: ContractAddress, token_id: u256) { let state = STATE(); assert(ERC721Impl::owner_of(@state, token_id) == recipient, 'Ownership after'); assert(ERC721Impl::balance_of(@state, recipient) == 1, 'Balance of recipient after'); assert(ERC721Impl::get_approved(@state, token_id) == ZERO(), 'Approval implicitly set'); } + +fn assert_event_approval_for_all( + owner: ContractAddress, operator: ContractAddress, approved: bool +) { + let event = utils::pop_log::(ZERO()).unwrap(); + assert(event.owner == owner, 'Invalid `owner`'); + assert(event.operator == operator, 'Invalid `operator`'); + assert(event.approved == approved, 'Invalid `approved`'); + utils::assert_no_events_left(ZERO()); +} + +fn assert_event_approval(owner: ContractAddress, approved: ContractAddress, token_id: u256) { + let event = utils::pop_log::(ZERO()).unwrap(); + assert(event.owner == owner, 'Invalid `owner`'); + assert(event.approved == approved, 'Invalid `approved`'); + assert(event.token_id == token_id, 'Invalid `token_id`'); + utils::assert_no_events_left(ZERO()); +} + +fn assert_event_transfer(from: ContractAddress, to: ContractAddress, token_id: u256) { + let event = utils::pop_log::(ZERO()).unwrap(); + assert(event.from == from, 'Invalid `from`'); + assert(event.to == to, 'Invalid `to`'); + assert(event.token_id == token_id, 'Invalid `token_id`'); + utils::assert_no_events_left(ZERO()); +} diff --git a/src/tests/utils.cairo b/src/tests/utils.cairo index 084e685e7..77d711c53 100644 --- a/src/tests/utils.cairo +++ b/src/tests/utils.cairo @@ -18,7 +18,7 @@ fn deploy(contract_class_hash: felt252, calldata: Array) -> ContractAdd } /// Pop the earliest unpopped logged event for the contract as the requested type -/// and checks there's no more data left on the event, preventing missing extra params. +/// and checks there's no more data left on the event, preventing unaccounted params. /// Indexed event members are currently not supported, so they are ignored. fn pop_log, impl TEvent: starknet::Event>( address: ContractAddress @@ -32,3 +32,7 @@ fn pop_log, impl TEvent: starknet::Event>( fn assert_no_events_left(address: ContractAddress) { assert(testing::pop_log_raw(address).is_none(), 'Events remaining on queue'); } + +fn drop_event(address: ContractAddress) { + testing::pop_log_raw(address); +} diff --git a/src/tests/utils/constants.cairo b/src/tests/utils/constants.cairo index c27c2f1ce..a66b6e721 100644 --- a/src/tests/utils/constants.cairo +++ b/src/tests/utils/constants.cairo @@ -8,6 +8,9 @@ const SUPPLY: u256 = 2000; const VALUE: u256 = 300; const ROLE: felt252 = 'ROLE'; const OTHER_ROLE: felt252 = 'OTHER_ROLE'; +const URI: felt252 = 'URI'; +const TOKEN_ID: u256 = 21; +const PUBKEY: felt252 = 'PUBKEY'; fn ADMIN() -> ContractAddress { contract_address_const::<'ADMIN'>() @@ -48,3 +51,7 @@ fn SPENDER() -> ContractAddress { fn RECIPIENT() -> ContractAddress { contract_address_const::<'RECIPIENT'>() } + +fn OPERATOR() -> ContractAddress { + contract_address_const::<'OPERATOR'>() +} From da96f33e2d422dba33ba8c6258cd82a655a55c2f Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 17 Aug 2023 16:57:04 -0400 Subject: [PATCH 090/246] work on internalImpl --- docs/modules/ROOT/pages/erc721.adoc | 285 +++++++++++++++++++++++++++- 1 file changed, 281 insertions(+), 4 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 8c54fcdc7..3eb242e85 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -365,8 +365,6 @@ The new approved NFT controller. + The NFT to approve. - fn set_approval_for_all(ref self: TState, operator: ContractAddress, approved: bool); - ==== `set_approval_for_all` [.contract-item] @@ -385,7 +383,7 @@ Emits an <> event. Address to add to the set of authorized operators. - `*approved*` + -`true` if operator is approved, `false` to revoke approval. +`true` if `operator` is approved, `false` to revoke approval. ==== `get_approved` @@ -500,7 +498,7 @@ The owner of the NFT. Address to add to the set of authorized operators. - `*approved*` + -`true` if the operator is approved, `false` to revoke approval. +`true` if the `operator` is approved, `false` to revoke approval. ==== `Transfer` [[Transfer]] @@ -632,3 +630,282 @@ Returns: - `*felt252*` + The IERC721Receiver magic value _0x3a0dff5f70d80458ad14ae37bb182a728e3c8cdda0402a5daa86620bdf910bc_. + +=== InternalImpl + +[,rust] +---- +fn initializer(ref self: ContractState, name_: felt252, symbol_: felt252); +fn _owner_of(self: @ContractState, token_id: u256) -> ContractAddress; +fn _exists(self: @ContractState, token_id: u256) -> bool; +fn _is_approved_or_owner( + self: @ContractState, + spender: ContractAddress, + token_id: u256 +) -> bool; +fn _approve(ref self: ContractState, to: ContractAddress, token_id: u256); +fn _set_approval_for_all( + ref self: ContractState, + owner: ContractAddress, + operator: ContractAddress, + approved: bool +); +fn _mint(ref self: ContractState, to: ContractAddress, token_id: u256); +fn _transfer( + ref self: ContractState, + from: ContractAddress, + to: ContractAddress, + token_id: u256 +); +fn _burn(ref self: ContractState, token_id: u256); +fn _safe_mint( + ref self: ContractState, + to: ContractAddress, + token_id: u256, + data: Span +); +fn _safe_transfer( + ref self: ContractState, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span +); +fn _set_token_uri(ref self: ContractState, token_id: u256, token_uri: felt252); +---- + +==== `initializer` + +[.contract-item] +[[initializer]] +==== `[.contract-item-name]#++initializer++#++(self: @ContractState, name_: felt252, symbol_: felt252)++` + +Initializes the contract by setting the token name and symbol. +This should be used inside the contract's constructor. + +===== Arguments + +- `*name_*` ++ +The token name. +- `*symbol_*` ++ +The token symbol. + +==== `_owner_of` + +[.contract-item] +[[_owner_of]] +==== `[.contract-item-name]#++_owner_of++#++(self: @ContractState, token_id: u256) -> ContractAddress++` + +Internal function that returns the owner address of `token_id`. +This function will panic if the token does not exist. + +===== Arguments + +- `*token_id*` ++ +The token to query. + +===== Returns + +- `*ContractAddress*` ++ +The owner address of `token_id`. + +==== `_exists` + +[.contract-item] +[[_exists]] +==== `[.contract-item-name]#++_exists++#++(self: @ContractState, token_id: u256) -> bool++` + +Internal function that returns whether `token_id` exists. + +Tokens start existing when they are minted (<<_mint,_mint>>), and stop existing when they are burned (<<_burn,_burn>>). + +===== Arguments + +- `*token_id*` ++ +The token to query. + +===== Returns + +- `*bool*` ++ +`true` if the token exists, `false` otherwise. + +==== `_is_approved_or_owner` + +[.contract-item] +[[_is_approved_or_owner]] +==== `[.contract-item-name]#++_is_approved_or_owner++#++(self: @ContractState, spender: ContractAddress, token_id: u256) -> bool++` + +Internal function that returns whether `spender` is allowed to manage `token_id`. + +This function panics if `token_id` does not exist. + +===== Arguments + +- `*spender*` ++ +The target address to query. +- `*token_id*` ++ +The token to query. + +===== Returns + +- `*bool*` ++ +`true` if the `spender` is either the owner or approved, `false` otherwise. + +fn _approve(ref self: ContractState, to: ContractAddress, token_id: u256); + +==== `_approve` + +[.contract-item] +[[_approve]] +==== `[.contract-item-name]#++_approve++#++(self: @ContractState, to: ContractAddress, token_id: u256)++` + +Internal function that changes or reaffirms the approved address for an NFT. + +This function panics if `token_id` does not exist or if `to` is the current token owner. + +Emits an <> event. + +===== Arguments + +- `*to*` ++ +The new approved NFT controller. +- `*token_id*` ++ +The NFT to approve. + +==== `_set_approval_for_all` + +[.contract-item] +[[_set_approval_for_all]] +==== `[.contract-item-name]#++_set_approval_for_all++#++(self: @ContractState, owner: ContractAddress, operator: ContractAddress, approved: bool)++` + +Internal function that enables or disables approval for `operator` to manage all of the +`owner` assets. + +Emits an <> event. + +This function panics if `owner` is also `operator`. + +===== Arguments + +- `*owner*` ++ +The current owner of the NFT. +- `*operator*` ++ +Address to add to the set of authorized operators. +- `*approved*` ++ +`true` if `operator` is approved, `false` to revoke approval. + +==== `_mint` + +[.contract-item] +[[_mint]] +==== `[.contract-item-name]#++_mint++#++(self: @ContractState, to: ContractAddress, token_id: u256)++` + +Internal function that mints `token_id` and transfer it to `to`. + +WARNING: Usage of this method is discouraged, use <<_safe_mint,_safe_mint>> whenever possible. + +Emits an <> event. + +This function panics if `to` is the zero address or if the `token_id` already exists. + +===== Arguments + +- `*to*` ++ +The new owner of the NFT. +- `*token_id*` ++ +The newly created NFT to transfer. + +==== `_transfer` + +[.contract-item] +[[_transfer]] +==== `[.contract-item-name]#++_transfer++#++(self: @ContractState, from: ContractAddress, to: ContractAddress, token_id: u256)++` + +Internal function that transfers `token_id` from `from` to `to`. + +Emits an <> event. + +This function panics if: + +* `to` is the zero address. +* `from` is not the token owner. +* `token_id` does not exist. + +===== Arguments + +- `*from*` ++ +The current NFT owner. +- `*to*` ++ +The new owner. +- `*token_id*` ++ +The NFT to transfer. + +==== `_burn` + +[.contract-item] +[[_burn]] +==== `[.contract-item-name]#++_burn++#++(self: @ContractState, token_id: u256)++` + +Internal function that destroys `token_id`. +The approval is cleared when the token is burned. +This internal function does not check if the sender is authorized to operate on the token. + +Emits an <> event. + +This function panics if: + +* `token_id` does not exist. + +===== Arguments + +- `*token_id*` ++ +The NFT to burn. + +==== `_safe_mint` + +[.contract-item] +[[_safe_mint]] +==== `[.contract-item-name]#++_safe_mint++#++(self: @ContractState, to: ContractAddress, token_id: u256, data: Span)++` + +Internal function that safely mints `token_id` and transfers it to `to`. +If `to` is not an account contract, `to` must support IERC721Receiver; otherwise, the transaction will fail. + +Emits an <> event. + +This function panics if: + +* `token_id` does not exist. +* `to` neither is an account contract nor supports the IERC721Receiver interface. + +===== Arguments + +- `*to*` ++ +The new owner. +- `*token_id*` ++ +The newly created NFT to transfer. +- `*data*` ++ +Additional data with no specified format, sent in call to `to`. + From ae543503bac9b9ba809969b84d475bafdeae339a Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 18 Aug 2023 14:50:14 -0400 Subject: [PATCH 091/246] finish api --- docs/modules/ROOT/pages/erc721.adoc | 175 ++++++++++++++++++++-------- 1 file changed, 126 insertions(+), 49 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 3eb242e85..348be5fcb 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -268,9 +268,7 @@ The account balance to query. ===== Returns -- `*u256*` -+ -Token balance of `account`. +- Token balance of `account`. ==== `owner_of` @@ -280,6 +278,10 @@ Token balance of `account`. Returns the owner address of `token_id`. +This function panics if: + +- `token_id` does not exist. + ===== Arguments - `*token_id*` @@ -288,9 +290,7 @@ The token to query. ===== Returns -- `*ContractAddress*` -+ -Owner address of `token_id`. +- Owner address of `token_id`. ==== `transfer_from` @@ -300,14 +300,18 @@ Owner address of `token_id`. Transfer ownership of `token_id` from `from` to `to`. -Note that the caller is responsible to confirm that the recipient is -capable of receiving ERC721 transfers or else they may be permanently lost. -Usage of <> prevents loss, though -the caller must understand this adds an external call which potentially -creates a reentrancy vulnerability. +Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 transfers or else they may be permanently lost. +Usage of <> prevents loss, though the caller must understand this adds an external call which potentially creates a reentrancy vulnerability. Emits a <> event. +This function panics if: + +- Caller is neither approved nor the `token_id` owner. +- `to` is the zero address. +- `from` is not the token owner. +- `token_id` does not exist. + ===== Arguments - `*from*` @@ -333,6 +337,14 @@ awareness of the ERC721 protocol, see <>(TODO!). Emits a <> event. +This function panics if: + +- Caller is neither approved nor the `token_id` owner. +- `to` is the zero address. +- `from` is not the token owner. +- `token_id` does not exist. +- `to` neither is an account contract nor supports the IERC721Receiver interface. + ===== Arguments - `*from*` @@ -356,6 +368,14 @@ Additional data with no specified format, sent in call to `to`. Change or reaffirm the approved address for an NFT. +Emits an <> event. + +This function panics if: + +- Caller is neither an approved operator nor the `token_id` owner. +- `to` is either the `owner` or the zero address. +- `token_id` does not exist. + ===== Arguments - `*to*` @@ -371,11 +391,14 @@ The NFT to approve. [[set_approval_for_all]] ==== `[.contract-item-name]#++set_approval_for_all++#++(self: @ContractState, operator: ContractAddress, approved: bool)++` -Enable or disable approval for `operator` to manage all of the -caller's assets. +Enable or disable approval for `operator` to manage all of the caller's assets. Emits an <> event. +This function panics if: + +- `owner` is the `operator`. + ===== Arguments - `*operator*` @@ -393,6 +416,10 @@ Address to add to the set of authorized operators. Returns the address approved for `token_id`. +This function panics if: + +- `token_id` does not exist. + ===== Arguments - `*token_id*` @@ -401,9 +428,7 @@ The token ID to query. ===== Returns -- `*ContractAddress*` -+ -Approved address for the `token_id` NFT, or `0` if there is none. +- Approved address for the `token_id` NFT, or `0` if there is none. ==== `is_approved_for_all` @@ -424,9 +449,7 @@ The address that acts on behalf of the `owner`. ===== Returns -- `*bool*` -+ -`true` if `operator` is an authorized operator for `owner`. +- `true` if `operator` is an authorized operator for `owner`. === Events @@ -543,9 +566,7 @@ Returns the NFT name. ===== Returns -- `*felt252*` -+ -The NFT name. +- The NFT name. ==== `symbol` @@ -557,9 +578,7 @@ Returns the NFT ticker symbol. ===== Returns -- `*felt252*` -+ -The NFT symbol. +- The NFT symbol. ==== `token_uri` @@ -578,9 +597,7 @@ The NFT symbol. ===== Returns -- `*felt252*` -+ -The URI of `token_id`. +- The URI of `token_id`. === IERC721Receiver API @@ -709,9 +726,7 @@ The token to query. ===== Returns -- `*ContractAddress*` -+ -The owner address of `token_id`. +- The owner address of `token_id`. ==== `_exists` @@ -731,9 +746,7 @@ The token to query. ===== Returns -- `*bool*` -+ -`true` if the token exists, `false` otherwise. +- `true` if the token exists, `false` otherwise. ==== `_is_approved_or_owner` @@ -743,7 +756,9 @@ The token to query. Internal function that returns whether `spender` is allowed to manage `token_id`. -This function panics if `token_id` does not exist. +This function panics if: + +- `token_id` does not exist. ===== Arguments @@ -756,11 +771,7 @@ The token to query. ===== Returns -- `*bool*` -+ -`true` if the `spender` is either the owner or approved, `false` otherwise. - -fn _approve(ref self: ContractState, to: ContractAddress, token_id: u256); +- `true` if the `spender` is either the owner or approved, `false` otherwise. ==== `_approve` @@ -770,7 +781,10 @@ fn _approve(ref self: ContractState, to: ContractAddress, token_id: u256); Internal function that changes or reaffirms the approved address for an NFT. -This function panics if `token_id` does not exist or if `to` is the current token owner. +This function panics if: + +- `token_id` does not exist. +- `to` is the current token owner. Emits an <> event. @@ -794,7 +808,9 @@ Internal function that enables or disables approval for `operator` to manage all Emits an <> event. -This function panics if `owner` is also `operator`. +This function panics if: + +-`owner` is the `operator`. ===== Arguments @@ -820,7 +836,10 @@ WARNING: Usage of this method is discouraged, use <<_safe_mint,_safe_mint>> when Emits an <> event. -This function panics if `to` is the zero address or if the `token_id` already exists. +This function panics if: + +- `to` is the zero address. +- `token_id` already exists. ===== Arguments @@ -843,9 +862,9 @@ Emits an <> event. This function panics if: -* `to` is the zero address. -* `from` is not the token owner. -* `token_id` does not exist. +- `to` is the zero address. +- `from` is not the token owner. +- `token_id` does not exist. ===== Arguments @@ -873,7 +892,7 @@ Emits an <> event. This function panics if: -* `token_id` does not exist. +- `token_id` does not exist. ===== Arguments @@ -894,8 +913,8 @@ Emits an <> event. This function panics if: -* `token_id` does not exist. -* `to` neither is an account contract nor supports the IERC721Receiver interface. +- `token_id` does not exist. +- `to` neither is an account contract nor supports the IERC721Receiver interface. ===== Arguments @@ -909,3 +928,61 @@ The newly created NFT to transfer. + Additional data with no specified format, sent in call to `to`. +==== `_safe_transfer` + +[.contract-item] +[[_safe_transfer]] +==== `[.contract-item-name]#++_safe_transfer++#++(self: @ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` + +Internal function that safely transfers `token_id` token from `from` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. + +`data` is additional data, it has no specified format and it is sent in call to `to`. + +This function is equivalent to `safe_transfer_from`, and can be used to e.g. implement alternative mechanisms to perform signature-based token transfers. + +Emits an <> event. + +This function panics if: + +- `to` is the zero address. +- `from` is not the token owner. +- `token_id` does not exist. +- `to` neither is an account contract nor supports the IERC721Receiver interface. + +===== Arguments + +- `*from*` ++ +The current NFT owner. +- `*to*` ++ +The new owner. +- `*token_id*` ++ +The NFT to transfer. +- `*data*` ++ +Additional data with no specified format, sent in call to `to`. + + fn _set_token_uri(ref self: ContractState, token_id: u256, token_uri: felt252) { + +==== `_set_token_uri` + +[.contract-item] +[[_set_token_uri]] +==== `[.contract-item-name]#++_set_token_uri++#++(self: @ContractState, token_id: u256, token_uri: felt252)++` + +Sets the `token_uri` of `token_id`. + +This function panics if: + +- `token_id` does not exist. + +===== Arguments + +- `*token_id*` ++ +The target NFT. +- `*token_uri*` ++ +The new token URI for `token_id`. \ No newline at end of file From 83076441b0d7b965f9c533c774f5c3d7b39381cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Fri, 18 Aug 2023 18:18:23 -0300 Subject: [PATCH 092/246] Update README and fix scarb package (#688) * update README to cairo-2 and scarb usage. bump scarb package version * update readme fix linter issues fix linter issues fix linter issues update readme * add main to CI * update readme * update readme * rename starknet * fix linter * update readme * Update README.md Co-authored-by: Andrew Fleming * Update README.md Co-authored-by: Andrew Fleming * update CONTRIBUTING * fix linting * Update CONTRIBUTING.md Co-authored-by: Andrew Fleming * Update CONTRIBUTING.md Co-authored-by: Andrew Fleming * apply review changes * fix linter * fix typo --------- Co-authored-by: Andrew Fleming --- .github/workflows/test.yml | 2 + CONTRIBUTING.md | 91 ++---------- README.md | 292 ++++++++++--------------------------- Scarb.toml | 2 +- 4 files changed, 98 insertions(+), 289 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0a9921611..ef91ff089 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,9 +3,11 @@ name: Lint and test on: pull_request: branches: + - main - cairo-2 push: branches: + - main - cairo-2 jobs: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 49e9b88d0..8adb5ee4b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,69 +6,11 @@ We really appreciate and value contributions to OpenZeppelin Contracts for Cairo Before starting development, please [create an issue](https://github.com/OpenZeppelin/cairo-contracts/issues/new/choose) to open the discussion, validate that the PR is wanted, and coordinate overall implementation details. -Also, consider that snake case is used for Cairo development in general due to its strong Python bias. -This project follows our [Extensibility pattern](https://docs.openzeppelin.com/contracts-cairo/extensibility), camelCasing all exposed function names and their parameters: - -```cairo -@external -func exposedFunc(paramOne, paramTwo){ -} -``` - -All internal and otherwise unexposed functions should resort to snake_case: - -```cairo -func internal_func(param_one, param_two){ -} -``` - -Compare our preset contracts with the libraries from which they're derived such as the [ERC20 preset](src/token/erc20/presets/ERC20.cairo) and [ERC20 library](src/token/erc20/presets/ERC20.cairo) for full examples. -See [Function names and coding style](https://docs.openzeppelin.com/contracts-cairo/0.4.0/extensibility#function_names_and_coding_style) for more information. - -And make sure to always include tests and documentation for the new developments. Please consider the following conventions: - -- Naming - - Libraries should be named `library.cairo`, e.g. `erc20/library.cairo` - - Contracts should be PascalCased i.e. `MyContract.cairo` - - Interfaces should be prefixed with an `I`, as in `IAccount.cairo` - - Test modules should begin with `test_` followed by the contract name i.e. `test_MyContract.py` - -- Structure - - Libraries should cede their names to their parent directory and are named `library.cairo` instead - - Interfaces should be alongside the library that the interface defines - - Preset contracts should be within a `presets` directory of the library to which they are a preset - - Here are example paths: - - `openzeppelin.token.erc20.library` - - `openzeppelin.token.erc20.IERC20` - - `openzeppelin.token.erc20.presets.ERC20Mintable` - - And a visual guide: - -```python - openzeppelin - └──token - └── erc20 - ├── library.cairo - ├── IERC20.cairo - └── presets - └── ERC20Mintable.cairo -``` - -- Preset contract testing - - Though, inheritance is not possible in Cairo, this repo utilizes inheritance for testing. This proves useful for testing multiple contracts that stem from the same base library. For example, the preset contracts [ERC20Mintable](src/token/erc20/presets/ERC20Mintable.cairo) and [ERC20Burnable](src/token/erc20/presets/ERC20Burnable.cairo) both share the base ERC20 functionality. To reduce code repetition, we follow these guidelines: - - `BaseSuites` - - module names are not prefixed with `test_` - - set base tests inside a class - - class name should not be prefixed with `Test`; otherwise, these tests run twice - - - test modules - - define the base fixture (`contract_factory`) and any other fixtures not used in the base suite i.e. `erc721_minted` - - define the test class and inherit the base class i.e. `class TestERC20(OwnableBase)` - - add tests specific to the preset flavor within the test class - - - fixtures - - are not defined in the base suite but are passed, unpacked, and used - - are defined in the tests where they are used - - for modularity, the basic contract factory fixture is always called `contract_factory` +### Coding style + +After a few radical changes in the Cairo language (mainly the transition to Cairo 1), our coding style guidelines became automatically deprecated. +That's why [we're working on setting new ones](https://github.com/OpenZeppelin/cairo-contracts/issues/696). +Feel free to read, contribute, discuss, and ask questions in the issue. ## Creating Pull Requests (PRs) @@ -98,32 +40,29 @@ As a contributor, you are expected to fork this repository, work on your own for 3. Make your changes, add your files, update documentation ([see Documentation section](#documentation)), commit, and push to your fork. ```sh - git add SomeFile.js + git add src/file.cairo git commit "Fix some bug short description #123" git push origin fix/some-bug-short-description-#123 ``` -4. Run tests, linter, etc. This can be done by running local continuous integration and make sure it passes. We recommend to use a [python virtual environment](https://docs.python.org/3/tutorial/venv.html). +4. Run tests and linter. This can be done by running local continuous integration and make sure it passes. ```bash - # install tox from testing dependencies - pip install .[testing] # '.[testing]' in zsh - # run tests - tox + scarb test - # stop the build if there are Markdown documentation errors - tox -e lint + # run linter + scarb fmt --check ``` -5. Go to [github.com/OpenZeppelin/cairo-contracts](https://github.com/OpenZeppelin/cairo-contracts) in your web browser and issue a new pull request. +5. Go to [OpenZeppelin/cairo-contracts](https://github.com/OpenZeppelin/cairo-contracts) in your web browser and issue a new pull request. Begin the body of the PR with "Fixes #123" or "Resolves #123" to link the PR to the issue that it is resolving. *IMPORTANT* Read the PR template very carefully and make sure to follow all the instructions. These instructions refer to some very important conditions that your PR must meet in order to be accepted, such as making sure that all PR checks pass. 6. Maintainers will review your code and possibly ask for changes before your code is pulled in to the main repository. We'll check that all tests pass, review the coding style, and check for general code correctness. If everything is OK, we'll merge your pull request and your code will be part of OpenZeppelin Contracts for Cairo. - *IMPORTANT* Please pay attention to the maintainer's feedback, since its a necessary step to keep up with the standards OpenZeppelin Contracts attains to. + *IMPORTANT* Please pay attention to the maintainer's feedback, since it's a necessary step to keep up with the standards OpenZeppelin Contracts attains to. ## Documentation @@ -145,15 +84,13 @@ If you want to run the documentation UI locally: ## Integration tests -Currently, [starknet's test suite](https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/starknet/testing/starknet.py) has important differences with public networks. Like [not checking signature hints toward the end of the tx flow](https://github.com/OpenZeppelin/cairo-contracts/issues/386). - -That's why we strongly suggest testing new features against a testnet before submitting the PR, to make sure that everything works as expected in a real environment. +Currently, Starknet's test suite has important differences with public networks. We strongly suggest testing new features against a testnet before submitting the PR, to make sure that everything works as expected in a real environment. We are looking into defining a better process for these integration tests, but for now the PR author/contributor must suggest an approach to test the feature when applicable, which has to be agreed and reproduced by the reviewer. ## All set -If you have any questions, feel free to post them to github.com/OpenZeppelin/cairo-contracts/issues. +If you have any questions, feel free to post them as an [issue](https://github.com/OpenZeppelin/cairo-contracts/issues). Finally, if you're looking to collaborate and want to find easy tasks to start, look at the issues we marked as ["Good first issue"](https://github.com/OpenZeppelin/cairo-contracts/labels/good%20first%20issue). diff --git a/README.md b/README.md index c5efd3044..3ac087332 100644 --- a/README.md +++ b/README.md @@ -1,117 +1,107 @@ # OpenZeppelin Contracts for Cairo -[![Tests and linter](https://github.com/OpenZeppelin/cairo-contracts/actions/workflows/coverage.yml/badge.svg)](https://github.com/OpenZeppelin/cairo-contracts/actions/workflows/coverage.yml) -[![codecov](https://codecov.io/github/OpenZeppelin/cairo-contracts/branch/main/graph/badge.svg?token=LFSZH8RPOL)](https://codecov.io/github/OpenZeppelin/cairo-contracts) +[![Lint and test](https://github.com/OpenZeppelin/cairo-contracts/actions/workflows/test.yml/badge.svg)](https://github.com/OpenZeppelin/cairo-contracts/actions/workflows/test.yml) -**A library for secure smart contract development** written in Cairo for [StarkNet](https://starkware.co/product/starknet/), a decentralized ZK Rollup. +**A library for secure smart contract development** written in Cairo for [Starknet](https://starkware.co/product/starknet/), a decentralized ZK Rollup. -## Usage - -> ## ⚠️ WARNING! ⚠️ -> +> **Warning** > This repo contains highly experimental code. -> Expect rapid iteration. +> It has no code coverage checks. +> It hasn't been audited. > **Use at your own risk.** -### First time? +## Usage -Before installing Cairo on your machine, you need to install `gmp`: +> **Warning** +> Expect rapid iteration. +> Some contracts or features are not ready to be deployed. +> Check the **Unsupported** section below. -```bash -sudo apt install -y libgmp3-dev # linux -brew install gmp # mac -``` +### Prepare the environment -> If you have any troubles installing gmp on your Apple M1 computer, [here’s a list of potential solutions](https://github.com/OpenZeppelin/nile/issues/22). +Simply [install Cairo and scarb](https://docs.swmansion.com/scarb/download). ### Set up your project -Create a directory for your project, then `cd` into it and create a Python virtual environment. +Create a new project and `cd` into it. ```bash -mkdir my-project -cd my-project -python3 -m venv env -source env/bin/activate +scarb new my_project && cd my_project ``` -Install the [Nile](https://github.com/OpenZeppelin/nile) development environment and then run `init` to kickstart a new project. Nile will create the project directory structure and install [the Cairo language](https://www.cairo-lang.org/docs/quickstart.html), a [local network](https://github.com/Shard-Labs/starknet-devnet/), and a [testing framework](https://docs.pytest.org/en/6.2.x/). +The contents of `my_project` should look like this: ```bash -pip install cairo-nile -nile init -``` - -### Install the library +$ ls -```bash -pip install openzeppelin-cairo-contracts +Scarb.toml src ``` -> ⚠️ Warning! ⚠️ -Installing directly the `main` branch may contain incomplete or breaking implementations, download [official releases](https://github.com/OpenZeppelin/cairo-contracts/releases/) only. - -### Use a basic preset +### Install the library -Presets are ready-to-use contracts that you can deploy right away. They also serve as examples of how to use library modules. [Read more about presets](https://docs.openzeppelin.com/contracts-cairo/0.6.1/extensibility#presets). +Edit `scarb.toml` and add: -```cairo -// contracts/MyToken.cairo - -%lang starknet - -from openzeppelin.token.erc20.presets.ERC20 import ( - constructor, - name, - symbol, - totalSupply, - decimals, - balanceOf, - allowance, - transfer, - transferFrom, - approve, - increaseAllowance, - decreaseAllowance -) +```toml +[dependencies] +openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.7.0" } ``` -Compile and deploy it right away: +Build the project to download it: ```bash -nile compile +$ scarb build -nile deploy MyToken --alias my_token +Updating git repository https://github.com/OpenZeppelin/cairo-contracts +Compiling my_project v0.1.0 (~/my_project/Scarb.toml) +Finished release target(s) in 6 seconds ``` -> Note that `` is expected to be two integers i.e. `1` `0`. See [Uint256](https://docs.openzeppelin.com/contracts-cairo/0.6.1/utilities#uint256) for more information. +### Using the library -### Write a custom contract using library modules +Open `src/lib.cairo` and write your contract. -[Read more about libraries](https://docs.openzeppelin.com/contracts-cairo/0.6.1/extensibility#libraries). +For example, this how to extend the ERC20 standard contract: ```cairo -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.uint256 import Uint256 -from openzeppelin.security.pausable.library import Pausable -from openzeppelin.token.erc20.library import ERC20 - -(...) - -@external -func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - recipient: felt, amount: Uint256 -) -> (success: felt) { - Pausable.assert_not_paused(); - return ERC20.transfer(recipient, amount); +#[starknet::contract] +mod MyToken { + use starknet::ContractAddress; + use openzeppelin::token::erc20::ERC20; + + #[storage] + struct Storage {} + + #[constructor] + fn constructor( + ref self: ContractState, + initial_supply: u256, + recipient: ContractAddress + ) { + let name = 'MyToken'; + let symbol = 'MTK'; + + let mut unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::InternalImpl::initializer(ref unsafe_state, name, symbol); + ERC20::InternalImpl::_mint(ref unsafe_state, recipient, initial_supply); + } + + #[external(v0)] + fn name(self: @ContractState) -> felt252 { + let unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::ERC20Impl::name(@unsafe_state) + } + + ... } ``` +### Unsupported + +`DualCase` dispatchers rely on Sierra's ability to catch a revert to resume execution. Currently, Starknet live chains (testnets and mainnet) don't implement that behavior. Starknet's testing framework does support it. + ## Learn -### Documentation + ### Cairo -- [StarkNet official documentation](https://www.cairo-lang.org/docs/hello_starknet/index.html#hello-starknet) -- [Cairo language documentation](https://www.cairo-lang.org/docs/hello_cairo/index.html#hello-cairo) -- Perama's [Cairo by example](https://perama-v.github.io/cairo/by-example/) -- [Cairo 101 workshops](https://www.youtube.com/playlist?list=PLcIyXLwiPilV5RBZj43AX1FY4FJMWHFTY) +- [Cairo book](https://book.cairo-lang.org/) +- [Cairo language documentation](https://docs.cairo-lang.org/) +- [Starknet book](https://book.starknet.io/) +- [Starknet documentation](https://docs.starknet.io/documentation/) +- [Cairo 1.0 mini-docs](https://github.com/Starknet-Africa-Edu/Cairo1.0) +- [Cairopractice](https://cairopractice.com/) -### Nile +### Tooling -- [Getting started with StarkNet using Nile](https://medium.com/coinmonks/starknet-tutorial-for-beginners-using-nile-6af9c2270c15) -- [How to manage smart contract deployments with Nile](https://medium.com/@martriay/manage-your-starknet-deployments-with-nile-%EF%B8%8F-e849d40546dd) +- [Scarb](https://docs.swmansion.com/scarb) ## Development ### Set up the project -Clone the repository +Clone the repository: ```bash git clone git@github.com:OpenZeppelin/cairo-contracts.git ``` -`cd` into it and create a Python virtual environment: - -```bash -cd cairo-contracts -python3 -m venv env -source env/bin/activate -``` - -Install dependencies: +`cd` into it and build: ```bash -python -m pip install . -``` - -### Compile the contracts +$ cd cairo-contracts +$ scarb build -```bash -nile compile --directory src - -🤖 Compiling all Cairo contracts in the src directory -🔨 Compiling src/openzeppelin/token/erc20/library.cairo -🔨 Compiling src/openzeppelin/token/erc20/presets/ERC20Mintable.cairo -🔨 Compiling src/openzeppelin/token/erc20/presets/ERC20Pausable.cairo -🔨 Compiling src/openzeppelin/token/erc20/presets/ERC20Upgradeable.cairo -🔨 Compiling src/openzeppelin/token/erc20/presets/ERC20.cairo -🔨 Compiling src/openzeppelin/token/erc20/IERC20.cairo -🔨 Compiling src/openzeppelin/token/erc721/enumerable/library.cairo -🔨 Compiling src/openzeppelin/token/erc721/library.cairo -🔨 Compiling src/openzeppelin/token/erc721/utils/ERC721Holder.cairo -🔨 Compiling src/openzeppelin/token/erc721/presets/ERC721MintablePausable.cairo -🔨 Compiling src/openzeppelin/token/erc721/presets/ERC721MintableBurnable.cairo -🔨 Compiling src/openzeppelin/token/erc721/presets/ERC721EnumerableMintableBurnable.cairo -🔨 Compiling src/openzeppelin/token/erc721/IERC721.cairo -🔨 Compiling src/openzeppelin/token/erc721/IERC721Metadata.cairo -🔨 Compiling src/openzeppelin/token/erc721/IERC721Receiver.cairo -🔨 Compiling src/openzeppelin/token/erc721/enumerable/IERC721Enumerable.cairo -🔨 Compiling src/openzeppelin/access/ownable/library.cairo -🔨 Compiling src/openzeppelin/security/reentrancyguard/library.cairo -🔨 Compiling src/openzeppelin/security/safemath/library.cairo -🔨 Compiling src/openzeppelin/security/pausable/library.cairo -🔨 Compiling src/openzeppelin/security/initializable/library.cairo -🔨 Compiling src/openzeppelin/utils/constants/library.cairo -🔨 Compiling src/openzeppelin/introspection/erc165/library.cairo -🔨 Compiling src/openzeppelin/introspection/erc165/IERC165.cairo -🔨 Compiling src/openzeppelin/upgrades/library.cairo -🔨 Compiling src/openzeppelin/upgrades/presets/Proxy.cairo -🔨 Compiling src/openzeppelin/account/library.cairo -🔨 Compiling src/openzeppelin/account/presets/EthAccount.cairo -🔨 Compiling src/openzeppelin/account/presets/Account.cairo -🔨 Compiling src/openzeppelin/account/presets/AddressRegistry.cairo -🔨 Compiling src/openzeppelin/account/IAccount.cairo -✅ Done +Compiling lib(openzeppelin) openzeppelin v0.7.0 (~/cairo-contracts/Scarb.toml) +Compiling starknet-contract(openzeppelin) openzeppelin v0.7.0 (~/cairo-contracts/Scarb.toml) +Finished release target(s) in 16 seconds ``` ### Run tests -Run tests using [tox](https://tox.wiki/en/latest/), tox automatically creates an isolated testing environment: - -```bash -tox - -====================== test session starts ====================== -platform linux -- Python 3.7.2, pytest-7.1.2, py-1.11.0, pluggy-1.0.0 -rootdir: /home/readme/cairo-contracts, configfile: tox.ini -plugins: asyncio-0.18.3, xdist-2.5.0, forked-1.4.0, web3-5.29.0, typeguard-2.13.3 -asyncio: mode=auto -gw0 [185] / gw1 [185] -...................................................................................... -...................................................................................... -............ [100%] -``` - -### Run Tests in Docker - -For M1 users or those who are having trouble with library/python versions you can alternatively run the tests within a docker container. Using the following as a Dockerfile placed in the root directory of the project: - -```dockerfile -FROM python:3.7 - -RUN pip install tox -RUN mkdir cairo-contracts -COPY . cairo-contracts -WORKDIR cairo-contracts -ENTRYPOINT tox -``` - -After its placed there run: - ```bash -docker build -t cairo-tests . -docker run cairo-tests -``` - -### Parallel Testing - -This repo utilizes the [pytest-xdist](https://pytest-xdist.readthedocs.io/en/latest/) plugin which runs tests in parallel. This feature increases testing speed; however, conflicts with a shared state can occur since tests do not run in order. To overcome this, independent cached versions of contracts being tested should be provisioned to each test case. Here's a simple fixture example: - -```python -from utils import get_contract_class, cached_contract - -@pytest.fixture -def foo_factory(): - # get contract class - foo_cls = get_contract_class('Foo') - - # deploy contract - starknet = await Starknet.empty() - foo = await starknet.deploy(contract_class=foo_cls) - - # copy the state and cache contract - state = starknet.state.copy() - cached_foo = cached_contract(state, foo_cls, foo) - - return cached_foo +scarb test ``` -See [Memoization](https://docs.openzeppelin.com/contracts-cairo/0.6.1/utilities#memoization) in the Utilities documentation for a more thorough example on caching contracts. - -> Note that this does not apply for stateless libraries such as SafeMath. - ## Security > ⚠️ Warning! ⚠️ @@ -277,24 +165,6 @@ Refer to [SECURITY.md](SECURITY.md) for more details. OpenZeppelin Contracts for Cairo exists thanks to its contributors. There are many ways you can participate and help build high quality software. Check out the [contribution](CONTRIBUTING.md) guide! -### Markdown linter - -To keep the markdown files neat and easy to edit, we utilize DavidAnson's [markdownlint](https://github.com/DavidAnson/markdownlint) linter. You can find the listed rules [here](https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md). Note that the following rules are disabled: - -- `MD013: line length` - - - to enable paragraphs without internal line breaks - -- `MD033: inline HTML` - - - to enable .md files to have duplicate headers and separate them by identifiers - -Before creating a PR, check that documentation changes are compliant with our markdown rules by running: - -```bash -tox -e lint -``` - ## License OpenZeppelin Contracts for Cairo is released under the [MIT License](LICENSE). diff --git a/Scarb.toml b/Scarb.toml index e36e29c60..bf42b24ee 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -1,6 +1,6 @@ [package] name = "openzeppelin" -version = "0.1.0" +version = "0.7.0" cairo-version = "2.1.0-rc1" authors = ["OpenZeppelin Community "] description = "OpenZeppelin Contracts written in Cairo for StarkNet, a decentralized ZK Rollup" From b3761ca34822b616ea96fc26178c7b4c50ac2412 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 19 Aug 2023 17:43:57 -0400 Subject: [PATCH 093/246] clean up ERC721Received section --- docs/modules/ROOT/pages/erc721.adoc | 106 ++++++++++------------------ 1 file changed, 38 insertions(+), 68 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 348be5fcb..1d75f5e28 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -41,13 +41,22 @@ The `erc721.cairo` contract implements an approximation of https://eips.ethereum ** <> *** <> *** <> - *** <> - ** <> - *** <> - *** <> - *** <> + *** <> ** <> *** <> + ** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> == IERC721 @@ -93,7 +102,6 @@ trait ISRC5 { Although StarkNet is not EVM compatible, this implementation aims to be as close as possible to the ERC721 standard in the following ways: * It uses Cairo's `u256` instead of `felt252`. -* It returns `true` as success. * It makes use of Cairo's short strings to simulate `name` and `symbol`. But some differences can still be found, such as: @@ -104,25 +112,19 @@ If a token's URI is not set, the returned value is `0`. Note that URIs cannot exceed 31 characters at this time. See <>. * ``interface_id``s are hardcoded and initialized by the constructor. -The hardcoded values derive from Solidity's selector calculations. +The hardcoded values derive from Starknet's selector calculcations. See <>. -* `safeTransferFrom` can only be expressed as a single function in Cairo as opposed to the two functions declared in EIP721. +* `safe_transfer_from` can only be expressed as a single function in Cairo as opposed to the two functions declared in EIP721. The difference between both functions consists of accepting `data` as an argument. -Because function overloading is currently not possible in Cairo, `safeTransferFrom` by default accepts the `data` argument. -If `data` is not used, simply insert `0`. -* `safeTransferFrom` is specified such that the optional `data` argument should be of type bytes. +Because function overloading is currently not possible in Cairo, `safe_transfer_from` by default accepts the `data` argument. +If `data` is not used, simply pass an empty array. +* `safe_transfer_from` is specified such that the optional `data` argument should be of type bytes. In Solidity, this means a dynamically-sized array. To be as close as possible to the standard, it accepts a dynamic array of felts. -In Cairo, arrays are expressed with the array length preceding the actual array; -hence, the method accepts `data_len` and `data` respectively as types `felt` and `felt*`. -* `ERC165.register_interface` allows contracts to set and communicate which interfaces they support. -This follows OpenZeppelin's https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v0.6.1/contracts/utils/introspection/ERC165Storage.sol[ERC165Storage]. -* `IERC721Receiver` compliant contracts (`ERC721Holder`) return a hardcoded selector id according to EVM selectors, since selectors are calculated differently in Cairo. -This is in line with the ERC165 interfaces design choice towards EVM compatibility. -See the xref:introspection.adoc[Introspection docs] for more info. -* `IERC721Receiver` compliant contracts (`ERC721Holder`) must support ERC165 by registering the `IERC721Receiver` selector id in its constructor and exposing the `supportsInterface` method. +* `SRC5.register_interface` allows contracts to set and communicate which interfaces they support. +This is similar to OpenZeppelin's https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v0.6.1/contracts/utils/introspection/ERC165Storage.sol[ERC165Storage]. +* `IERC721Receiver` compliant contracts return a hardcoded selector id according to Starknet selectors (as opposed to selector calculation in Solidity). In doing so, recipient contracts (both accounts and non-accounts) can be verified that they support ERC721 transfers. -* `ERC721Enumerable` tracks the total number of tokens with the `all_tokens` and `all_tokens_len` storage variables mimicking the array of the Solidity implementation. == Usage @@ -141,8 +143,6 @@ The `safe_transfer_from` method incorporates the following conditional logic: The current implementation of `safe_transfer_from` checks for `on_erc721_received` and requires that the recipient contract supports SRC5 and exposes the `supports_interface` method. See <>. -Fix me^ - === Interpreting ERC721 URIs Token URIs in Cairo are stored as single field elements. @@ -155,22 +155,18 @@ Therefore, this library's ERC721 implementation sets URIs as a single field elem === ERC721Received In order to be sure a contract can safely accept ERC721 tokens, said contract must implement the `IERC721Receiver` interface (as expressed in the EIP721 specification). -Methods such as `safe_transfer_from` and `safe_mint` call the recipient contract's `on_erc721_received` method. +Methods such as `safe_transfer_from` and `_safe_mint` call the recipient contract's `on_erc721_received` method. If the contract fails to return the correct magic value, the transaction fails. Starknet contracts that support safe transfers, however, must also support xref:introspection.adoc#src5[SRC5] and include `supports_interface` as proposed (originally as ERC165) in https://github.com/OpenZeppelin/cairo-contracts/discussions/100[#100]. `safe_transfer_from` requires a means of differentiating between account and non-account contracts. -`on_erc721_received` will call `supports_interface` with the SRC6 magic value (INSERT ME) on the recipient address. +Account contracts must support the Starknet standard account interface in order to communicate the contract's ability to receive safe NFT transfers. +The standard account interface is drafted and defined as https://community.starknet.io/t/snip-starknet-standard-account/95665[SRC-6 in Starknet Shamans]. +`on_erc721_received` will call `supports_interface` with the SRC6 magic value _0x2ceccef7f994940b3962a6c67e0ba4fcd37df7d131417c604f91e03caecc1cd_ on the recipient address. SRC6-compliant account contracts will return `true` thus communicating that the recipient is an account contract. Non-account contracts, however, _must_ register support for ERC721 safe transfers. Otherwise, the safe transfer will fail. -Currently, Starknet does not support error handling from the contract level; -therefore, the current ERC721 implementation requires that all contracts that support safe ERC721 transfers (both accounts and non-accounts) include the `supportsInterface` method. -Further, `supportsInterface` should return `TRUE` if the recipient contract supports the `IERC721Receiver` magic value `0x150b7a02` (which invokes `onERC721Received`). -If the recipient contract supports the `IAccount` magic value `0x50b70dcb`, `supportsInterface` should return `TRUE`. -Otherwise, `safeTransferFrom` should fail. - ==== IERC721Receiver Interface for any contract that wants to support safe transfers from ERC721 asset contracts. @@ -255,7 +251,6 @@ fn is_approved_for_all( ==== `balance_of` [.contract-item] -[[balance_of]] ==== `[.contract-item-name]#++balance_of++#++(self: @ContractState, account: ContractAddress) → u256++` Returns the number of NFTs owned by `account`. @@ -273,7 +268,6 @@ The account balance to query. ==== `owner_of` [.contract-item] -[[owner_of]] ==== `[.contract-item-name]#++owner_of++#++(self: @ContractState, token_id: u256) → ContractAddress++` Returns the owner address of `token_id`. @@ -295,7 +289,6 @@ The token to query. ==== `transfer_from` [.contract-item] -[[transfer_from]] ==== `[.contract-item-name]#++transfer_from++#++(self: @ContractState, from: ContractAddress, to: ContractAddress, token_id: u256)++` Transfer ownership of `token_id` from `from` to `to`. @@ -327,7 +320,6 @@ The NFT to transfer. ==== `safe_transfer_from` [.contract-item] -[[safe_transfer_from]] ==== `[.contract-item-name]#++safe_transfer_from++#++(self: @ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` Safely transfer ownership of `token_id` from `from` to `to`, checking first @@ -363,7 +355,6 @@ Additional data with no specified format, sent in call to `to`. ==== `approve` [.contract-item] -[[approve]] ==== `[.contract-item-name]#++approve++#++(self: @ContractState, to: ContractAddress, token_id: u256)++` Change or reaffirm the approved address for an NFT. @@ -388,7 +379,6 @@ The NFT to approve. ==== `set_approval_for_all` [.contract-item] -[[set_approval_for_all]] ==== `[.contract-item-name]#++set_approval_for_all++#++(self: @ContractState, operator: ContractAddress, approved: bool)++` Enable or disable approval for `operator` to manage all of the caller's assets. @@ -411,7 +401,6 @@ Address to add to the set of authorized operators. ==== `get_approved` [.contract-item] -[[get_approved]] ==== `[.contract-item-name]#++get_approved++#++(self: @ContractState, token_id: u256) -> ContractAddress++` Returns the address approved for `token_id`. @@ -433,7 +422,6 @@ The token ID to query. ==== `is_approved_for_all` [.contract-item] -[[is_approved_for_all]] ==== `[.contract-item-name]#++is_approved_for_all++#++(self: @ContractState, owner: ContractAddress, operator: ContractAddress) -> bool++` Query if `operator` is an authorized operator for `owner`. @@ -559,7 +547,6 @@ trait IERC721Metadata { ==== `name` [.contract-item] -[[name]] ==== `[.contract-item-name]#++name++#++(self: @ContractState)++` Returns the NFT name. @@ -571,7 +558,6 @@ Returns the NFT name. ==== `symbol` [.contract-item] -[[symbol]] ==== `[.contract-item-name]#++symbol++#++(self: @ContractState)++` Returns the NFT ticker symbol. @@ -583,7 +569,6 @@ Returns the NFT ticker symbol. ==== `token_uri` [.contract-item] -[[token_uri]] ==== `[.contract-item-name]#++token_uri++#++(self: @ContractState, token_id: u256)++` Returns the Uniform Resource Identifier (URI) for the `token_id` token. @@ -620,7 +605,6 @@ trait IERC721Receiver { ==== `on_erc721_received` [.contract-item] -[[on_erc721_received]] ==== `[.contract-item-name]#++on_erc721_received++#++(self: @TState, operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span) -> felt252++` Whenever an IERC721 `token_id` token is transferred to this non-account contract through `safe_transfer_from`, this function is called. @@ -694,7 +678,6 @@ fn _set_token_uri(ref self: ContractState, token_id: u256, token_uri: felt252); ==== `initializer` [.contract-item] -[[initializer]] ==== `[.contract-item-name]#++initializer++#++(self: @ContractState, name_: felt252, symbol_: felt252)++` Initializes the contract by setting the token name and symbol. @@ -709,10 +692,9 @@ The token name. + The token symbol. -==== `_owner_of` +==== `_owner_of` [[owner_of_internal]] [.contract-item] -[[_owner_of]] ==== `[.contract-item-name]#++_owner_of++#++(self: @ContractState, token_id: u256) -> ContractAddress++` Internal function that returns the owner address of `token_id`. @@ -728,10 +710,9 @@ The token to query. - The owner address of `token_id`. -==== `_exists` +==== `_exists` [[exists_internal]] [.contract-item] -[[_exists]] ==== `[.contract-item-name]#++_exists++#++(self: @ContractState, token_id: u256) -> bool++` Internal function that returns whether `token_id` exists. @@ -748,10 +729,9 @@ The token to query. - `true` if the token exists, `false` otherwise. -==== `_is_approved_or_owner` +==== `_is_approved_or_owner` [[is_approved_or_owner_internal]] [.contract-item] -[[_is_approved_or_owner]] ==== `[.contract-item-name]#++_is_approved_or_owner++#++(self: @ContractState, spender: ContractAddress, token_id: u256) -> bool++` Internal function that returns whether `spender` is allowed to manage `token_id`. @@ -773,10 +753,9 @@ The token to query. - `true` if the `spender` is either the owner or approved, `false` otherwise. -==== `_approve` +==== `_approve` [[approve_internal]] [.contract-item] -[[_approve]] ==== `[.contract-item-name]#++_approve++#++(self: @ContractState, to: ContractAddress, token_id: u256)++` Internal function that changes or reaffirms the approved address for an NFT. @@ -797,10 +776,9 @@ The new approved NFT controller. + The NFT to approve. -==== `_set_approval_for_all` +==== `_set_approval_for_all` [[set_approval_for_all_internal]] [.contract-item] -[[_set_approval_for_all]] ==== `[.contract-item-name]#++_set_approval_for_all++#++(self: @ContractState, owner: ContractAddress, operator: ContractAddress, approved: bool)++` Internal function that enables or disables approval for `operator` to manage all of the @@ -824,10 +802,9 @@ Address to add to the set of authorized operators. + `true` if `operator` is approved, `false` to revoke approval. -==== `_mint` +==== `_mint` [[mint_internal]] [.contract-item] -[[_mint]] ==== `[.contract-item-name]#++_mint++#++(self: @ContractState, to: ContractAddress, token_id: u256)++` Internal function that mints `token_id` and transfer it to `to`. @@ -850,10 +827,9 @@ The new owner of the NFT. + The newly created NFT to transfer. -==== `_transfer` +==== `_transfer` [[transfer_internal]] [.contract-item] -[[_transfer]] ==== `[.contract-item-name]#++_transfer++#++(self: @ContractState, from: ContractAddress, to: ContractAddress, token_id: u256)++` Internal function that transfers `token_id` from `from` to `to`. @@ -878,10 +854,9 @@ The new owner. + The NFT to transfer. -==== `_burn` +==== `_burn` [[burn_internal]] [.contract-item] -[[_burn]] ==== `[.contract-item-name]#++_burn++#++(self: @ContractState, token_id: u256)++` Internal function that destroys `token_id`. @@ -900,10 +875,9 @@ This function panics if: + The NFT to burn. -==== `_safe_mint` +==== `_safe_mint` [[safe_mint_internal]] [.contract-item] -[[_safe_mint]] ==== `[.contract-item-name]#++_safe_mint++#++(self: @ContractState, to: ContractAddress, token_id: u256, data: Span)++` Internal function that safely mints `token_id` and transfers it to `to`. @@ -928,10 +902,9 @@ The newly created NFT to transfer. + Additional data with no specified format, sent in call to `to`. -==== `_safe_transfer` +==== `_safe_transfer` [[safe_transfer_internal]] [.contract-item] -[[_safe_transfer]] ==== `[.contract-item-name]#++_safe_transfer++#++(self: @ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` Internal function that safely transfers `token_id` token from `from` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. @@ -964,12 +937,9 @@ The NFT to transfer. + Additional data with no specified format, sent in call to `to`. - fn _set_token_uri(ref self: ContractState, token_id: u256, token_uri: felt252) { - -==== `_set_token_uri` +==== `_set_token_uri` [[set_token_uri_internal]] [.contract-item] -[[_set_token_uri]] ==== `[.contract-item-name]#++_set_token_uri++#++(self: @ContractState, token_id: u256, token_uri: felt252)++` Sets the `token_uri` of `token_id`. @@ -985,4 +955,4 @@ This function panics if: The target NFT. - `*token_uri*` + -The new token URI for `token_id`. \ No newline at end of file +The new token URI for `token_id`. From 38c6ce62424e1a31a19b315e7bf993bdb9706923 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 22 Aug 2023 12:22:09 -0400 Subject: [PATCH 094/246] trim comments --- src/token/erc721/erc721.cairo | 111 ++++++++++++---------------------- 1 file changed, 38 insertions(+), 73 deletions(-) diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index cd78c59c4..458f1b315 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -1,5 +1,3 @@ -//! # License -//! //! SPDX-License-Identifier: MIT //! OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/erc721.cairo) //! @@ -79,11 +77,6 @@ mod ERC721 { } /// Emitted when `token_id` token is transferred from `from` to `to`. - /// - /// # Arguments - /// * `from` - The current owner of the NFT. - /// * `to` - The new owner of the NFT. - /// * `token_id` - The NFT to transfer. #[derive(Drop, starknet::Event)] struct Transfer { from: ContractAddress, @@ -92,11 +85,6 @@ mod ERC721 { } /// Emitted when `owner` enables `approved` to manage the `token_id` token. - /// - /// # Arguments - /// * `owner` - The owner of the NFT. - /// * `approved` - The new approved NFT controller. - /// * `token_id` - The NFT to approve. #[derive(Drop, starknet::Event)] struct Approval { owner: ContractAddress, @@ -106,11 +94,6 @@ mod ERC721 { /// Emitted when `owner` enables or disables (approved) `operator` to manage /// all of its assets. - /// - /// # Arguments - /// * `owner` - The owner of the NFT. - /// * `operator` - Address to add to the set of authorized operators. - /// * `approved` - `true` if the operator is approved, `false` to revoke approval. #[derive(Drop, starknet::Event)] struct ApprovalForAll { owner: ContractAddress, @@ -120,10 +103,6 @@ mod ERC721 { /// Initializes the state of the ERC721 contract. This includes setting the /// NFT name and symbol. - /// - /// # Arguments - /// * `name` - The NFT name. - /// * `symbol` - The NFT symbol. #[constructor] fn constructor(ref self: ContractState, name: felt252, symbol: felt252) { self.initializer(name, symbol); @@ -138,11 +117,6 @@ mod ERC721 { /// Checks if the contract supports a specific interface as defined by /// `interface_id`. /// See: https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md - /// - /// # Arguments - /// * `interface_id` - The calculated interface ID to query. - /// # Returns - /// `true` if the interface is supported. fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { let unsafe_state = src5::SRC5::unsafe_new_contract_state(); src5::SRC5::SRC5Impl::supports_interface(@unsafe_state, interface_id) @@ -162,28 +136,17 @@ mod ERC721 { #[external(v0)] impl ERC721MetadataImpl of interface::IERC721Metadata { /// Returns the NFT name. - /// - /// # Returns - /// NFT name. fn name(self: @ContractState) -> felt252 { self._name.read() } /// Returns the NFT symbol. - /// - /// # Returns - /// NFT symbol. fn symbol(self: @ContractState) -> felt252 { self._symbol.read() } /// Returns the Uniform Resource Identifier (URI) for the `token_id` token. /// If the URI is not set for the `token_id`, the return value will be `0`. - /// - /// # Arguments - /// `token_id` - The token to query. - /// # Returns - /// URI of `token_id`. fn token_uri(self: @ContractState, token_id: u256) -> felt252 { assert(self._exists(token_id), 'ERC721: invalid token ID'); self._token_uri.read(token_id) @@ -203,44 +166,33 @@ mod ERC721 { #[external(v0)] impl ERC721Impl of interface::IERC721 { /// Returns the number of NFTs owned by `account`. + /// + /// # Panics /// - /// # Arguments - /// `account` - The address to query. - /// # Returns - /// Number of NFTs owned by `account`. + /// This function panics if: + /// - `token_id` does not exist. fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { assert(!account.is_zero(), 'ERC721: invalid account'); self._balances.read(account) } /// Returns the owner address of `token_id`. - /// - /// # Arguments - /// `token_id` - The token to query. - /// # Returns - /// Owner address of `token_id`. fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress { self._owner_of(token_id) } /// Returns the address approved for `token_id`. /// - /// # Arguments - /// `token_id` - The token ID to query. - /// # Returns - /// Approved address for the `token_id` NFT, or `0` if there is none. + /// # Panics + /// + /// This function panics if: + /// - `token_id` does not exist. fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { assert(self._exists(token_id), 'ERC721: invalid token ID'); self._token_approvals.read(token_id) } /// Query if `operator` is an authorized operator for `owner`. - /// - /// # Arguments - /// `owner` - The address that owns the NFT. - /// `operator` - The address that acts on behalf of the `owner`. - /// # Returns - /// `true` if `operator` is an authorized operator for `owner`. fn is_approved_for_all( self: @ContractState, owner: ContractAddress, operator: ContractAddress ) -> bool { @@ -248,10 +200,13 @@ mod ERC721 { } /// Change or reaffirm the approved address for an NFT. + /// Emits an [Approval](Approval) event. /// - /// # Arguments - /// `to` - The new approved NFT controller. - /// `token_id` - The NFT to approve. + /// # Panics + /// + /// This function panics if: + /// - The caller is neither the NFT owner nor authorized. + /// - The NFT owner is `to`. fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) { let owner = self._owner_of(token_id); @@ -265,12 +220,7 @@ mod ERC721 { /// Enable or disable approval for `operator` to manage all of the /// caller's assets. - /// /// Emits an [Approval](Approval) event. - /// - /// # Arguments - /// `operator` - Address to add to the set of authorized operators. - /// `approved` - `true` if operator is approved, `false` to revoke approval. fn set_approval_for_all( ref self: ContractState, operator: ContractAddress, approved: bool ) { @@ -287,10 +237,12 @@ mod ERC721 { /// /// Emits a [Transfer](Transfer) event. /// - /// # Arguments - /// `from` - The current owner of the NFT. - /// `to` - The new owner. - /// `token_id` - The NFT to transfer. + /// # Panics + /// + /// - The caller is neither approved nor the `token_id` owner. + /// - `to` is the zero address. + /// - `from` is not the token owner. + /// - `token_id` does not exist. fn transfer_from( ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 ) { @@ -308,11 +260,14 @@ mod ERC721 { /// /// Emits a [Transfer](Transfer) event. /// - /// # Arguments - /// `from` - The current owner of the NFT. - /// `to` - The new owner. - /// `token_id` - The NFT to transfer. - /// `data` - Additional data with no specified format, sent in call to `to`. + /// # Panics + /// + /// This functions panics if: + /// - The caller is neither approved nor the `token_id` owner. + /// - `to` is the zero address. + /// - `from` is not the token owner. + /// - `token_id` does not exist. + /// - `to` neither is an account contract nor supports the IERC721Receiver interface. fn safe_transfer_from( ref self: ContractState, from: ContractAddress, @@ -389,6 +344,8 @@ mod ERC721 { #[generate_trait] impl InternalImpl of InternalTrait { + /// Initializes the contract by setting the token name and symbol. + /// This should be used inside the contract's constructor. fn initializer(ref self: ContractState, name_: felt252, symbol_: felt252) { self._name.write(name_); self._symbol.write(symbol_); @@ -400,6 +357,12 @@ mod ERC721 { ); } + /// Returns the owner address of `token_id`. + /// + /// # Panics + /// + /// This function panics if: + /// - `token_id` does not exist. fn _owner_of(self: @ContractState, token_id: u256) -> ContractAddress { let owner = self._owners.read(token_id); match owner.is_zero() { @@ -408,10 +371,12 @@ mod ERC721 { } } + /// Returns whether `token_id` exists. fn _exists(self: @ContractState, token_id: u256) -> bool { !self._owners.read(token_id).is_zero() } + /// Returns whether `spender` is allowed to manage `token_id`. fn _is_approved_or_owner( self: @ContractState, spender: ContractAddress, token_id: u256 ) -> bool { From cece32ba90a6d4e1e01a9a09040c3bad3275d27b Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Tue, 22 Aug 2023 23:49:22 -0400 Subject: [PATCH 095/246] Bump scarb and fix dual721 test context (#703) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * bump scarb and cairo version * fix testing context --------- Co-authored-by: Martín Triay --- .github/workflows/test.yml | 2 +- Scarb.toml | 4 ++-- src/tests/token/test_dual721.cairo | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ef91ff089..8357486b9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/checkout@v3 - uses: software-mansion/setup-scarb@v1 with: - scarb-version: "0.6.0-alpha.1" + scarb-version: "0.6.2" - name: Markdown lint uses: DavidAnson/markdownlint-cli2-action@5b7c9f74fec47e6b15667b2cc23c63dff11e449e # v9 with: diff --git a/Scarb.toml b/Scarb.toml index bf42b24ee..d07fbd1b3 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -1,7 +1,7 @@ [package] name = "openzeppelin" version = "0.7.0" -cairo-version = "2.1.0-rc1" +cairo-version = "2.1.1" authors = ["OpenZeppelin Community "] description = "OpenZeppelin Contracts written in Cairo for StarkNet, a decentralized ZK Rollup" documentation = "https://docs.openzeppelin.com/contracts-cairo" @@ -11,7 +11,7 @@ license-file = "LICENSE" keywords = ["openzeppelin", "starknet", "cairo", "contracts", "security", "standards"] [dependencies] -starknet = ">=2.1.0-rc0" +starknet = ">=2.1.1" [lib] diff --git a/src/tests/token/test_dual721.cairo b/src/tests/token/test_dual721.cairo index 55349de11..b5f02f7e9 100644 --- a/src/tests/token/test_dual721.cairo +++ b/src/tests/token/test_dual721.cairo @@ -62,7 +62,7 @@ fn setup_snake() -> (DualCaseERC721, IERC721Dispatcher) { calldata.append_serde(SYMBOL); calldata.append_serde(TOKEN_ID); calldata.append_serde(URI); - set_caller_address(OWNER()); + set_contract_address(OWNER()); let target = utils::deploy(SnakeERC721Mock::TEST_CLASS_HASH, calldata); (DualCaseERC721 { contract_address: target }, IERC721Dispatcher { contract_address: target }) } @@ -73,7 +73,7 @@ fn setup_camel() -> (DualCaseERC721, IERC721CamelOnlyDispatcher) { calldata.append_serde(SYMBOL); calldata.append_serde(TOKEN_ID); calldata.append_serde(URI); - set_caller_address(OWNER()); + set_contract_address(OWNER()); let target = utils::deploy(CamelERC721Mock::TEST_CLASS_HASH, calldata); ( DualCaseERC721 { From 32f99742c86cffb29319167133cd416e496759cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Wed, 23 Aug 2023 00:51:29 -0300 Subject: [PATCH 096/246] add rc version (#708) --- Scarb.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scarb.toml b/Scarb.toml index d07fbd1b3..73083f109 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -1,6 +1,6 @@ [package] name = "openzeppelin" -version = "0.7.0" +version = "0.7.0-rc.0" cairo-version = "2.1.1" authors = ["OpenZeppelin Community "] description = "OpenZeppelin Contracts written in Cairo for StarkNet, a decentralized ZK Rollup" From b40c35b5ca82598ae5bd9610aa3ffd0ded1f3c5f Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Tue, 22 Aug 2023 23:52:11 -0400 Subject: [PATCH 097/246] remove unused imports (#707) --- src/tests/mocks/camel_accesscontrol_mock.cairo | 1 - src/tests/mocks/snake_accesscontrol_mock.cairo | 1 - 2 files changed, 2 deletions(-) diff --git a/src/tests/mocks/camel_accesscontrol_mock.cairo b/src/tests/mocks/camel_accesscontrol_mock.cairo index 55835f50c..09e2fbf92 100644 --- a/src/tests/mocks/camel_accesscontrol_mock.cairo +++ b/src/tests/mocks/camel_accesscontrol_mock.cairo @@ -4,7 +4,6 @@ mod CamelAccessControlMock { use openzeppelin::access::accesscontrol::AccessControl; use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; use starknet::ContractAddress; - use starknet::get_caller_address; #[storage] struct Storage {} diff --git a/src/tests/mocks/snake_accesscontrol_mock.cairo b/src/tests/mocks/snake_accesscontrol_mock.cairo index 48b291979..614b00627 100644 --- a/src/tests/mocks/snake_accesscontrol_mock.cairo +++ b/src/tests/mocks/snake_accesscontrol_mock.cairo @@ -4,7 +4,6 @@ mod SnakeAccessControlMock { use openzeppelin::access::accesscontrol::AccessControl; use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; use starknet::ContractAddress; - use starknet::get_caller_address; #[storage] struct Storage {} From 755d8f6f477e74e03d5fadf7d731e272a0997e63 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 23 Aug 2023 10:15:43 -0400 Subject: [PATCH 098/246] change code block to js --- docs/modules/ROOT/pages/erc721.adoc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 1d75f5e28..00f140322 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -60,7 +60,7 @@ The `erc721.cairo` contract implements an approximation of https://eips.ethereum == IERC721 -[,rust] +[,javascript] ---- #[starknet::interface] trait IERC721 { @@ -171,7 +171,7 @@ Otherwise, the safe transfer will fail. Interface for any contract that wants to support safe transfers from ERC721 asset contracts. -[,rust] +[,javascript] ---- #[starknet::interface] trait IERC721Receiver { @@ -212,7 +212,7 @@ Note that the `IERC721Metadata` interface id should be removed from the construc ==== IERC721Metadata -[,rust] +[,javascript] ---- #[starknet::interface] trait IERC721Metadata { @@ -226,7 +226,7 @@ trait IERC721Metadata { === IERC721 API -[,rust] +[,javascript] ---- // SRC5 id: 0x33eb2f84c309543403fd69f0d0f363781ef06ef6faeb0131ff16ea3175bd943 @@ -441,7 +441,7 @@ The address that acts on behalf of the `owner`. === Events -[,rust] +[,javascript] ---- #[event] #[derive(Drop, starknet::Event)] @@ -532,7 +532,7 @@ The NFT to transfer. === IERC721Metadata API -[,rust] +[,javascript] ---- // SRC5 id: 0x6069a70848f907fa57668ba1875164eb4dcee693952468581406d131081bbd @@ -586,7 +586,7 @@ The NFT symbol. === IERC721Receiver API -[,rust] +[,javascript] ---- // SRC5 id: 0x3a0dff5f70d80458ad14ae37bb182a728e3c8cdda0402a5daa86620bdf910bc @@ -634,7 +634,7 @@ The IERC721Receiver magic value _0x3a0dff5f70d80458ad14ae37bb182a728e3c8cdda0402 === InternalImpl -[,rust] +[,javascript] ---- fn initializer(ref self: ContractState, name_: felt252, symbol_: felt252); fn _owner_of(self: @ContractState, token_id: u256) -> ContractAddress; From e304e7e4455c79ca0041acfa0794155009294a07 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 23 Aug 2023 23:39:04 -0400 Subject: [PATCH 099/246] remove headers --- docs/modules/ROOT/pages/erc721.adoc | 130 +++++++++------------------- 1 file changed, 40 insertions(+), 90 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 00f140322..beee7e41c 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -38,25 +38,25 @@ The `erc721.cairo` contract implements an approximation of https://eips.ethereum *** <> *** <> *** <> - ** <> + ** <> *** <> *** <> *** <> ** <> - *** <> + *** <> ** <> *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> + *** <<_owner_of,`_owner_of`>> + *** <<_exists,`_exists`>> + *** <<_is_approved_or_owner,`_is_approved_or_owner`>> + *** <<_approve,`_approve`>> + *** <<_set_approval_for_all,`_set_approval_for_all`>> + *** <<_mint,`_mint`>> + *** <<_transfer,`_transfer`>> + *** <<_burn,`_burn`>> + *** <<_safe_mint,`_safe_mint`>> + *** <<_safe_transfer,`_safe_transfer`>> + *** <<_set_token_uri,`_set_token_uri`>> == IERC721 @@ -248,9 +248,7 @@ fn is_approved_for_all( ) -> bool; ---- -==== `balance_of` - -[.contract-item] +[.contract-item#balance_of] ==== `[.contract-item-name]#++balance_of++#++(self: @ContractState, account: ContractAddress) → u256++` Returns the number of NFTs owned by `account`. @@ -265,9 +263,7 @@ The account balance to query. - Token balance of `account`. -==== `owner_of` - -[.contract-item] +[.contract-item#owner_of] ==== `[.contract-item-name]#++owner_of++#++(self: @ContractState, token_id: u256) → ContractAddress++` Returns the owner address of `token_id`. @@ -286,9 +282,7 @@ The token to query. - Owner address of `token_id`. -==== `transfer_from` - -[.contract-item] +[.contract-item#transfer_from] ==== `[.contract-item-name]#++transfer_from++#++(self: @ContractState, from: ContractAddress, to: ContractAddress, token_id: u256)++` Transfer ownership of `token_id` from `from` to `to`. @@ -317,9 +311,7 @@ The new owner. + The NFT to transfer. -==== `safe_transfer_from` - -[.contract-item] +[.contract-item#safe_transfer_from] ==== `[.contract-item-name]#++safe_transfer_from++#++(self: @ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` Safely transfer ownership of `token_id` from `from` to `to`, checking first @@ -352,9 +344,7 @@ The NFT to transfer. + Additional data with no specified format, sent in call to `to`. -==== `approve` - -[.contract-item] +[.contract-item#approve] ==== `[.contract-item-name]#++approve++#++(self: @ContractState, to: ContractAddress, token_id: u256)++` Change or reaffirm the approved address for an NFT. @@ -376,9 +366,7 @@ The new approved NFT controller. + The NFT to approve. -==== `set_approval_for_all` - -[.contract-item] +[.contract-item#set_approval_for_all] ==== `[.contract-item-name]#++set_approval_for_all++#++(self: @ContractState, operator: ContractAddress, approved: bool)++` Enable or disable approval for `operator` to manage all of the caller's assets. @@ -398,9 +386,7 @@ Address to add to the set of authorized operators. + `true` if `operator` is approved, `false` to revoke approval. -==== `get_approved` - -[.contract-item] +[.contract-item#get_approved] ==== `[.contract-item-name]#++get_approved++#++(self: @ContractState, token_id: u256) -> ContractAddress++` Returns the address approved for `token_id`. @@ -419,9 +405,7 @@ The token ID to query. - Approved address for the `token_id` NFT, or `0` if there is none. -==== `is_approved_for_all` - -[.contract-item] +[.contract-item#is_approved_for_all] ==== `[.contract-item-name]#++is_approved_for_all++#++(self: @ContractState, owner: ContractAddress, operator: ContractAddress) -> bool++` Query if `operator` is an authorized operator for `owner`. @@ -473,9 +457,7 @@ struct Transfer { } ---- -==== `Approval` [[Approval]] - -[.contract-item] +[.contract-item#Approval] ==== `[.contract-item-name]#++Approval++#++(owner: ContractAddress, approved: ContractAddress, token_id: u256)++` Emitted when `owner` enables `approved` to manage the `token_id` token. @@ -492,7 +474,7 @@ The new approved NFT controller. + The NFT to approve. -==== `ApprovalForAll` [[ApprovalForAll]] +[.contract-item#ApprovalForAll] [.contract-item] ==== `[.contract-item-name]#++ApprovalForAll++#++(owner: ContractAddress, operator: ContractAddress, approved: bool)++` @@ -511,7 +493,7 @@ Address to add to the set of authorized operators. + `true` if the `operator` is approved, `false` to revoke approval. -==== `Transfer` [[Transfer]] +[.contract-item#Transfer] [.contract-item] ==== `[.contract-item-name]#++Transfer++#++(from: ContractAddress, to: ContractAddress, token_id: u256)++` @@ -544,9 +526,7 @@ trait IERC721Metadata { } ---- -==== `name` - -[.contract-item] +[.contract-item#name] ==== `[.contract-item-name]#++name++#++(self: @ContractState)++` Returns the NFT name. @@ -555,9 +535,7 @@ Returns the NFT name. - The NFT name. -==== `symbol` - -[.contract-item] +[.contract-item#symbol] ==== `[.contract-item-name]#++symbol++#++(self: @ContractState)++` Returns the NFT ticker symbol. @@ -566,9 +544,7 @@ Returns the NFT ticker symbol. - The NFT symbol. -==== `token_uri` - -[.contract-item] +[.contract-item#token_uri] ==== `[.contract-item-name]#++token_uri++#++(self: @ContractState, token_id: u256)++` Returns the Uniform Resource Identifier (URI) for the `token_id` token. @@ -602,9 +578,7 @@ trait IERC721Receiver { } ---- -==== `on_erc721_received` - -[.contract-item] +[.contract-item#on_erc721_received] ==== `[.contract-item-name]#++on_erc721_received++#++(self: @TState, operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span) -> felt252++` Whenever an IERC721 `token_id` token is transferred to this non-account contract through `safe_transfer_from`, this function is called. @@ -675,9 +649,7 @@ fn _safe_transfer( fn _set_token_uri(ref self: ContractState, token_id: u256, token_uri: felt252); ---- -==== `initializer` - -[.contract-item] +[.contract-item#initializer] ==== `[.contract-item-name]#++initializer++#++(self: @ContractState, name_: felt252, symbol_: felt252)++` Initializes the contract by setting the token name and symbol. @@ -692,9 +664,7 @@ The token name. + The token symbol. -==== `_owner_of` [[owner_of_internal]] - -[.contract-item] +[.contract-item#_owner_of] ==== `[.contract-item-name]#++_owner_of++#++(self: @ContractState, token_id: u256) -> ContractAddress++` Internal function that returns the owner address of `token_id`. @@ -710,9 +680,7 @@ The token to query. - The owner address of `token_id`. -==== `_exists` [[exists_internal]] - -[.contract-item] +[.contract-item#_exists] ==== `[.contract-item-name]#++_exists++#++(self: @ContractState, token_id: u256) -> bool++` Internal function that returns whether `token_id` exists. @@ -729,9 +697,7 @@ The token to query. - `true` if the token exists, `false` otherwise. -==== `_is_approved_or_owner` [[is_approved_or_owner_internal]] - -[.contract-item] +[.contract-item#_is_approved_or_owner] ==== `[.contract-item-name]#++_is_approved_or_owner++#++(self: @ContractState, spender: ContractAddress, token_id: u256) -> bool++` Internal function that returns whether `spender` is allowed to manage `token_id`. @@ -753,9 +719,7 @@ The token to query. - `true` if the `spender` is either the owner or approved, `false` otherwise. -==== `_approve` [[approve_internal]] - -[.contract-item] +[.contract-item#_approve] ==== `[.contract-item-name]#++_approve++#++(self: @ContractState, to: ContractAddress, token_id: u256)++` Internal function that changes or reaffirms the approved address for an NFT. @@ -776,9 +740,7 @@ The new approved NFT controller. + The NFT to approve. -==== `_set_approval_for_all` [[set_approval_for_all_internal]] - -[.contract-item] +[.contract-item#_set_approval_for_all] ==== `[.contract-item-name]#++_set_approval_for_all++#++(self: @ContractState, owner: ContractAddress, operator: ContractAddress, approved: bool)++` Internal function that enables or disables approval for `operator` to manage all of the @@ -802,9 +764,7 @@ Address to add to the set of authorized operators. + `true` if `operator` is approved, `false` to revoke approval. -==== `_mint` [[mint_internal]] - -[.contract-item] +[.contract-item#_mint] ==== `[.contract-item-name]#++_mint++#++(self: @ContractState, to: ContractAddress, token_id: u256)++` Internal function that mints `token_id` and transfer it to `to`. @@ -827,9 +787,7 @@ The new owner of the NFT. + The newly created NFT to transfer. -==== `_transfer` [[transfer_internal]] - -[.contract-item] +[.contract-item#_transfer] ==== `[.contract-item-name]#++_transfer++#++(self: @ContractState, from: ContractAddress, to: ContractAddress, token_id: u256)++` Internal function that transfers `token_id` from `from` to `to`. @@ -854,9 +812,7 @@ The new owner. + The NFT to transfer. -==== `_burn` [[burn_internal]] - -[.contract-item] +[.contract-item#_burn] ==== `[.contract-item-name]#++_burn++#++(self: @ContractState, token_id: u256)++` Internal function that destroys `token_id`. @@ -875,9 +831,7 @@ This function panics if: + The NFT to burn. -==== `_safe_mint` [[safe_mint_internal]] - -[.contract-item] +[.contract-item#_safe_mint] ==== `[.contract-item-name]#++_safe_mint++#++(self: @ContractState, to: ContractAddress, token_id: u256, data: Span)++` Internal function that safely mints `token_id` and transfers it to `to`. @@ -902,9 +856,7 @@ The newly created NFT to transfer. + Additional data with no specified format, sent in call to `to`. -==== `_safe_transfer` [[safe_transfer_internal]] - -[.contract-item] +[.contract-item#_safe_transfer] ==== `[.contract-item-name]#++_safe_transfer++#++(self: @ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` Internal function that safely transfers `token_id` token from `from` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. @@ -937,9 +889,7 @@ The NFT to transfer. + Additional data with no specified format, sent in call to `to`. -==== `_set_token_uri` [[set_token_uri_internal]] - -[.contract-item] +[.contract-item#_set_token_uri] ==== `[.contract-item-name]#++_set_token_uri++#++(self: @ContractState, token_id: u256, token_uri: felt252)++` Sets the `token_uri` of `token_id`. From a30d12b0818960133db0c104113677dcc4e88380 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 24 Aug 2023 00:20:24 -0400 Subject: [PATCH 100/246] fix state --- docs/modules/ROOT/pages/erc721.adoc | 101 ++++++++++++++-------------- 1 file changed, 50 insertions(+), 51 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index beee7e41c..023b2b8b3 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -230,26 +230,33 @@ trait IERC721Metadata { ---- // SRC5 id: 0x33eb2f84c309543403fd69f0d0f363781ef06ef6faeb0131ff16ea3175bd943 -fn balance_of(self: @ContractState, account: ContractAddress) -> u256; -fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress; -fn transfer_from(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256); +fn balance_of(self: @TState, account: ContractAddress) -> u256; +fn owner_of(self: @TState, token_id: u256) -> ContractAddress; +fn transfer_from( + ref self: TState, + from: ContractAddress, + to: ContractAddress, + token_id: u256 +); fn safe_transfer_from( - ref self: ContractState, + ref self: TState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span ); -fn approve(ref self: ContractState, to: ContractAddress, token_id: u256); -fn set_approval_for_all(ref self: ContractState, operator: ContractAddress, approved: bool); -fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress; +fn approve(ref self: TState, to: ContractAddress, token_id: u256); +fn set_approval_for_all( + ref self: TState, operator: ContractAddress, approved: bool +); +fn get_approved(self: @TState, token_id: u256) -> ContractAddress; fn is_approved_for_all( - self: @ContractState, owner: ContractAddress, operator: ContractAddress + self: @TState, owner: ContractAddress, operator: ContractAddress ) -> bool; ---- [.contract-item#balance_of] -==== `[.contract-item-name]#++balance_of++#++(self: @ContractState, account: ContractAddress) → u256++` +==== `[.contract-item-name]#++balance_of++#++(self: @TState, account: ContractAddress) → u256++` Returns the number of NFTs owned by `account`. @@ -264,7 +271,7 @@ The account balance to query. - Token balance of `account`. [.contract-item#owner_of] -==== `[.contract-item-name]#++owner_of++#++(self: @ContractState, token_id: u256) → ContractAddress++` +==== `[.contract-item-name]#++owner_of++#++(self: @TState, token_id: u256) → ContractAddress++` Returns the owner address of `token_id`. @@ -283,7 +290,7 @@ The token to query. - Owner address of `token_id`. [.contract-item#transfer_from] -==== `[.contract-item-name]#++transfer_from++#++(self: @ContractState, from: ContractAddress, to: ContractAddress, token_id: u256)++` +==== `[.contract-item-name]#++transfer_from++#++(self: @TState, from: ContractAddress, to: ContractAddress, token_id: u256)++` Transfer ownership of `token_id` from `from` to `to`. @@ -312,7 +319,7 @@ The new owner. The NFT to transfer. [.contract-item#safe_transfer_from] -==== `[.contract-item-name]#++safe_transfer_from++#++(self: @ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` +==== `[.contract-item-name]#++safe_transfer_from++#++(self: @TState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` Safely transfer ownership of `token_id` from `from` to `to`, checking first that `to` is aware of the ERC721 protocol to prevent tokens being locked @@ -345,7 +352,7 @@ The NFT to transfer. Additional data with no specified format, sent in call to `to`. [.contract-item#approve] -==== `[.contract-item-name]#++approve++#++(self: @ContractState, to: ContractAddress, token_id: u256)++` +==== `[.contract-item-name]#++approve++#++(self: @TState, to: ContractAddress, token_id: u256)++` Change or reaffirm the approved address for an NFT. @@ -367,7 +374,7 @@ The new approved NFT controller. The NFT to approve. [.contract-item#set_approval_for_all] -==== `[.contract-item-name]#++set_approval_for_all++#++(self: @ContractState, operator: ContractAddress, approved: bool)++` +==== `[.contract-item-name]#++set_approval_for_all++#++(self: @TState, operator: ContractAddress, approved: bool)++` Enable or disable approval for `operator` to manage all of the caller's assets. @@ -387,7 +394,7 @@ Address to add to the set of authorized operators. `true` if `operator` is approved, `false` to revoke approval. [.contract-item#get_approved] -==== `[.contract-item-name]#++get_approved++#++(self: @ContractState, token_id: u256) -> ContractAddress++` +==== `[.contract-item-name]#++get_approved++#++(self: @TState, token_id: u256) -> ContractAddress++` Returns the address approved for `token_id`. @@ -406,7 +413,7 @@ The token ID to query. - Approved address for the `token_id` NFT, or `0` if there is none. [.contract-item#is_approved_for_all] -==== `[.contract-item-name]#++is_approved_for_all++#++(self: @ContractState, owner: ContractAddress, operator: ContractAddress) -> bool++` +==== `[.contract-item-name]#++is_approved_for_all++#++(self: @TState, owner: ContractAddress, operator: ContractAddress) -> bool++` Query if `operator` is an authorized operator for `owner`. @@ -427,14 +434,6 @@ The address that acts on behalf of the `owner`. [,javascript] ---- -#[event] -#[derive(Drop, starknet::Event)] -enum Event { - Transfer: Transfer, - Approval: Approval, - ApprovalForAll: ApprovalForAll -} - #[derive(Drop, starknet::Event)] struct Approval { owner: ContractAddress, @@ -527,7 +526,7 @@ trait IERC721Metadata { ---- [.contract-item#name] -==== `[.contract-item-name]#++name++#++(self: @ContractState)++` +==== `[.contract-item-name]#++name++#++(self: @TState)++` Returns the NFT name. @@ -536,7 +535,7 @@ Returns the NFT name. - The NFT name. [.contract-item#symbol] -==== `[.contract-item-name]#++symbol++#++(self: @ContractState)++` +==== `[.contract-item-name]#++symbol++#++(self: @TState)++` Returns the NFT ticker symbol. @@ -545,7 +544,7 @@ Returns the NFT ticker symbol. - The NFT symbol. [.contract-item#token_uri] -==== `[.contract-item-name]#++token_uri++#++(self: @ContractState, token_id: u256)++` +==== `[.contract-item-name]#++token_uri++#++(self: @TState, token_id: u256)++` Returns the Uniform Resource Identifier (URI) for the `token_id` token. If the URI is not set for the `token_id`, the return value will be `0`. @@ -610,47 +609,47 @@ The IERC721Receiver magic value _0x3a0dff5f70d80458ad14ae37bb182a728e3c8cdda0402 [,javascript] ---- -fn initializer(ref self: ContractState, name_: felt252, symbol_: felt252); -fn _owner_of(self: @ContractState, token_id: u256) -> ContractAddress; -fn _exists(self: @ContractState, token_id: u256) -> bool; +fn initializer(ref self: TState, name_: felt252, symbol_: felt252); +fn _owner_of(self: @TState, token_id: u256) -> ContractAddress; +fn _exists(self: @TState, token_id: u256) -> bool; fn _is_approved_or_owner( - self: @ContractState, + self: @TState, spender: ContractAddress, token_id: u256 ) -> bool; -fn _approve(ref self: ContractState, to: ContractAddress, token_id: u256); +fn _approve(ref self: TState, to: ContractAddress, token_id: u256); fn _set_approval_for_all( - ref self: ContractState, + ref self: TState, owner: ContractAddress, operator: ContractAddress, approved: bool ); -fn _mint(ref self: ContractState, to: ContractAddress, token_id: u256); +fn _mint(ref self: TState, to: ContractAddress, token_id: u256); fn _transfer( - ref self: ContractState, + ref self: TState, from: ContractAddress, to: ContractAddress, token_id: u256 ); -fn _burn(ref self: ContractState, token_id: u256); +fn _burn(ref self: TState, token_id: u256); fn _safe_mint( - ref self: ContractState, + ref self: TState, to: ContractAddress, token_id: u256, data: Span ); fn _safe_transfer( - ref self: ContractState, + ref self: TState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span ); -fn _set_token_uri(ref self: ContractState, token_id: u256, token_uri: felt252); +fn _set_token_uri(ref self: TState, token_id: u256, token_uri: felt252); ---- [.contract-item#initializer] -==== `[.contract-item-name]#++initializer++#++(self: @ContractState, name_: felt252, symbol_: felt252)++` +==== `[.contract-item-name]#++initializer++#++(ref self: TState, name_: felt252, symbol_: felt252)++` Initializes the contract by setting the token name and symbol. This should be used inside the contract's constructor. @@ -665,7 +664,7 @@ The token name. The token symbol. [.contract-item#_owner_of] -==== `[.contract-item-name]#++_owner_of++#++(self: @ContractState, token_id: u256) -> ContractAddress++` +==== `[.contract-item-name]#++_owner_of++#++(self: @TState, token_id: u256) -> ContractAddress++` Internal function that returns the owner address of `token_id`. This function will panic if the token does not exist. @@ -681,7 +680,7 @@ The token to query. - The owner address of `token_id`. [.contract-item#_exists] -==== `[.contract-item-name]#++_exists++#++(self: @ContractState, token_id: u256) -> bool++` +==== `[.contract-item-name]#++_exists++#++(self: @TState, token_id: u256) -> bool++` Internal function that returns whether `token_id` exists. @@ -698,7 +697,7 @@ The token to query. - `true` if the token exists, `false` otherwise. [.contract-item#_is_approved_or_owner] -==== `[.contract-item-name]#++_is_approved_or_owner++#++(self: @ContractState, spender: ContractAddress, token_id: u256) -> bool++` +==== `[.contract-item-name]#++_is_approved_or_owner++#++(self: @TState, spender: ContractAddress, token_id: u256) -> bool++` Internal function that returns whether `spender` is allowed to manage `token_id`. @@ -720,7 +719,7 @@ The token to query. - `true` if the `spender` is either the owner or approved, `false` otherwise. [.contract-item#_approve] -==== `[.contract-item-name]#++_approve++#++(self: @ContractState, to: ContractAddress, token_id: u256)++` +==== `[.contract-item-name]#++_approve++#++(ref self: TState, to: ContractAddress, token_id: u256)++` Internal function that changes or reaffirms the approved address for an NFT. @@ -741,7 +740,7 @@ The new approved NFT controller. The NFT to approve. [.contract-item#_set_approval_for_all] -==== `[.contract-item-name]#++_set_approval_for_all++#++(self: @ContractState, owner: ContractAddress, operator: ContractAddress, approved: bool)++` +==== `[.contract-item-name]#++_set_approval_for_all++#++(ref self: TState, owner: ContractAddress, operator: ContractAddress, approved: bool)++` Internal function that enables or disables approval for `operator` to manage all of the `owner` assets. @@ -765,7 +764,7 @@ Address to add to the set of authorized operators. `true` if `operator` is approved, `false` to revoke approval. [.contract-item#_mint] -==== `[.contract-item-name]#++_mint++#++(self: @ContractState, to: ContractAddress, token_id: u256)++` +==== `[.contract-item-name]#++_mint++#++(ref self: TState, to: ContractAddress, token_id: u256)++` Internal function that mints `token_id` and transfer it to `to`. @@ -788,7 +787,7 @@ The new owner of the NFT. The newly created NFT to transfer. [.contract-item#_transfer] -==== `[.contract-item-name]#++_transfer++#++(self: @ContractState, from: ContractAddress, to: ContractAddress, token_id: u256)++` +==== `[.contract-item-name]#++_transfer++#++(ref self: TState, from: ContractAddress, to: ContractAddress, token_id: u256)++` Internal function that transfers `token_id` from `from` to `to`. @@ -813,7 +812,7 @@ The new owner. The NFT to transfer. [.contract-item#_burn] -==== `[.contract-item-name]#++_burn++#++(self: @ContractState, token_id: u256)++` +==== `[.contract-item-name]#++_burn++#++(ref self: TState, token_id: u256)++` Internal function that destroys `token_id`. The approval is cleared when the token is burned. @@ -832,7 +831,7 @@ This function panics if: The NFT to burn. [.contract-item#_safe_mint] -==== `[.contract-item-name]#++_safe_mint++#++(self: @ContractState, to: ContractAddress, token_id: u256, data: Span)++` +==== `[.contract-item-name]#++_safe_mint++#++(ref self: TState, to: ContractAddress, token_id: u256, data: Span)++` Internal function that safely mints `token_id` and transfers it to `to`. If `to` is not an account contract, `to` must support IERC721Receiver; otherwise, the transaction will fail. @@ -857,7 +856,7 @@ The newly created NFT to transfer. Additional data with no specified format, sent in call to `to`. [.contract-item#_safe_transfer] -==== `[.contract-item-name]#++_safe_transfer++#++(self: @ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` +==== `[.contract-item-name]#++_safe_transfer++#++(ref self: TState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` Internal function that safely transfers `token_id` token from `from` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. @@ -890,7 +889,7 @@ The NFT to transfer. Additional data with no specified format, sent in call to `to`. [.contract-item#_set_token_uri] -==== `[.contract-item-name]#++_set_token_uri++#++(self: @ContractState, token_id: u256, token_uri: felt252)++` +==== `[.contract-item-name]#++_set_token_uri++#++(ref self: TState, token_id: u256, token_uri: felt252)++` Sets the `token_uri` of `token_id`. From d43e393a9da40ef9e5a0a589db6da54d5258222b Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 24 Aug 2023 00:22:13 -0400 Subject: [PATCH 101/246] remove panics --- src/token/erc721/erc721.cairo | 37 ----------------------------------- 1 file changed, 37 deletions(-) diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 458f1b315..175a24944 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -166,11 +166,6 @@ mod ERC721 { #[external(v0)] impl ERC721Impl of interface::IERC721 { /// Returns the number of NFTs owned by `account`. - /// - /// # Panics - /// - /// This function panics if: - /// - `token_id` does not exist. fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { assert(!account.is_zero(), 'ERC721: invalid account'); self._balances.read(account) @@ -182,11 +177,6 @@ mod ERC721 { } /// Returns the address approved for `token_id`. - /// - /// # Panics - /// - /// This function panics if: - /// - `token_id` does not exist. fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { assert(self._exists(token_id), 'ERC721: invalid token ID'); self._token_approvals.read(token_id) @@ -201,12 +191,6 @@ mod ERC721 { /// Change or reaffirm the approved address for an NFT. /// Emits an [Approval](Approval) event. - /// - /// # Panics - /// - /// This function panics if: - /// - The caller is neither the NFT owner nor authorized. - /// - The NFT owner is `to`. fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) { let owner = self._owner_of(token_id); @@ -236,13 +220,6 @@ mod ERC721 { /// creates a reentrancy vulnerability. /// /// Emits a [Transfer](Transfer) event. - /// - /// # Panics - /// - /// - The caller is neither approved nor the `token_id` owner. - /// - `to` is the zero address. - /// - `from` is not the token owner. - /// - `token_id` does not exist. fn transfer_from( ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 ) { @@ -259,15 +236,6 @@ mod ERC721 { /// awareness of the ERC721 protocol, see [ERC721Received](TODO!). /// /// Emits a [Transfer](Transfer) event. - /// - /// # Panics - /// - /// This functions panics if: - /// - The caller is neither approved nor the `token_id` owner. - /// - `to` is the zero address. - /// - `from` is not the token owner. - /// - `token_id` does not exist. - /// - `to` neither is an account contract nor supports the IERC721Receiver interface. fn safe_transfer_from( ref self: ContractState, from: ContractAddress, @@ -358,11 +326,6 @@ mod ERC721 { } /// Returns the owner address of `token_id`. - /// - /// # Panics - /// - /// This function panics if: - /// - `token_id` does not exist. fn _owner_of(self: @ContractState, token_id: u256) -> ContractAddress { let owner = self._owners.read(token_id); match owner.is_zero() { From c5a81cb328bad3724754bbc7de5fb7cc7970543c Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 24 Aug 2023 00:23:30 -0400 Subject: [PATCH 102/246] remove example --- src/token/erc721/erc721.cairo | 38 ----------------------------------- 1 file changed, 38 deletions(-) diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 175a24944..0dad11acb 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -2,44 +2,6 @@ //! OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/erc721.cairo) //! //! # ERC721 Contract and Implementation -//! -//! # Example -//! -//! How to extend the ERC721 contract: -//! ``` -//! #[starknet::contract] -//! mod MyToken { -//! use starknet::ContractAddress; -//! use openzeppelin::token::erc721::ERC721; -//! -//! #[storage] -//! struct Storage {} -//! -//! #[constructor] -//! fn constructor( -//! ref self: ContractState, -//! recipient: ContractAddress, -//! token_id: u256 -//! ) { -//! let name = 'MyNFT'; -//! let symbol = 'NFT'; -//! -//! let mut unsafe_state = ERC721::unsafe_new_contract_state(); -//! ERC721::InternalImpl::initializer(ref unsafe_state, name, symbol); -//! ERC721::InternalImpl::_mint(ref unsafe_state, recipient, token_id); -//! } -//! -//! // Define methods that extend the ERC721 standard contract. -//! #[external(v0)] -//! fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { -//! let unsafe_state = ERC721::unsafe_new_contract_state(); -//! ERC721::ERC721Impl::balance_of(@unsafe_state, account) -//! } -//! -//! ... -//! -//! } -//! ``` #[starknet::contract] mod ERC721 { use array::SpanTrait; From 2da60ba276da41f92f2301ec94cec429cb46389e Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 24 Aug 2023 01:26:04 -0400 Subject: [PATCH 103/246] add section overview --- src/token/erc721/erc721.cairo | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 0dad11acb..745be2064 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -2,6 +2,9 @@ //! OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/erc721.cairo) //! //! # ERC721 Contract and Implementation +//! +//! This ERC721 contract includes both a library and a basic preset implementation +//! which includes the IERC721Metadata implementation. #[starknet::contract] mod ERC721 { use array::SpanTrait; From 935b24184cc334313b2e808637e2902edd941b5e Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 24 Aug 2023 01:43:16 -0400 Subject: [PATCH 104/246] fix typo --- docs/modules/ROOT/pages/erc721.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 023b2b8b3..6e030d623 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -766,7 +766,7 @@ Address to add to the set of authorized operators. [.contract-item#_mint] ==== `[.contract-item-name]#++_mint++#++(ref self: TState, to: ContractAddress, token_id: u256)++` -Internal function that mints `token_id` and transfer it to `to`. +Internal function that mints `token_id` and transfers it to `to`. WARNING: Usage of this method is discouraged, use <<_safe_mint,_safe_mint>> whenever possible. From b58d90cf16804aa7f039b54813d9310acf98b706 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 24 Aug 2023 01:44:16 -0400 Subject: [PATCH 105/246] finish internal fn comments --- src/token/erc721/erc721.cairo | 37 +++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 745be2064..d1db66398 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -177,13 +177,6 @@ mod ERC721 { } /// Transfer ownership of `token_id` from `from` to `to`. - /// - /// Note that the caller is responsible to confirm that the recipient is - /// capable of receiving ERC721 transfers or else they may be permanently lost. - /// Usage of [safe_transfer_from](safe_transfer_from) prevents loss, though - /// the caller must understand this adds an external call which potentially - /// creates a reentrancy vulnerability. - /// /// Emits a [Transfer](Transfer) event. fn transfer_from( ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 @@ -197,9 +190,7 @@ mod ERC721 { /// Safely transfer ownership of `token_id` from `from` to `to`, checking first /// that `to` is aware of the ERC721 protocol to prevent tokens being locked - /// forever. For information regarding how contracts communicate their - /// awareness of the ERC721 protocol, see [ERC721Received](TODO!). - /// + /// forever. /// Emits a [Transfer](Transfer) event. fn safe_transfer_from( ref self: ContractState, @@ -315,6 +306,8 @@ mod ERC721 { || spender == ERC721Impl::get_approved(self, token_id) } + /// Internal function that changes or reaffirms the approved address for an NFT. + /// Emits an [Approval[(Approval) event. fn _approve(ref self: ContractState, to: ContractAddress, token_id: u256) { let owner = self._owner_of(token_id); assert(owner != to, 'ERC721: approval to owner'); @@ -323,6 +316,9 @@ mod ERC721 { self.emit(Approval { owner, approved: to, token_id }); } + /// Internal function that enables or disables approval for `operator` + /// to manage all of the `owner` assets. + /// Emits an [Approval[(Approval) event. fn _set_approval_for_all( ref self: ContractState, owner: ContractAddress, @@ -334,6 +330,8 @@ mod ERC721 { self.emit(ApprovalForAll { owner, operator, approved }); } + /// Internal function that mints `token_id` and transfers it to `to`. + /// Emits a [Transfer](Transfer) event. fn _mint(ref self: ContractState, to: ContractAddress, token_id: u256) { assert(!to.is_zero(), 'ERC721: invalid receiver'); assert(!self._exists(token_id), 'ERC721: token already minted'); @@ -344,6 +342,8 @@ mod ERC721 { self.emit(Transfer { from: Zeroable::zero(), to, token_id }); } + /// Internal function that transfers `token_id` from `from` to `to`. + /// Emits a [Transfer](Transfer) event. fn _transfer( ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 ) { @@ -361,6 +361,11 @@ mod ERC721 { self.emit(Transfer { from, to, token_id }); } + /// Internal function that destroys `token_id`. The approval is cleared when + /// the token is burned. + /// This internal function does not check if the sender is authorized + /// to operate on the token. + /// Emits a [Transfer](Transfer) event. fn _burn(ref self: ContractState, token_id: u256) { let owner = self._owner_of(token_id); @@ -373,6 +378,10 @@ mod ERC721 { self.emit(Transfer { from: owner, to: Zeroable::zero(), token_id }); } + /// Internal function that safely mints `token_id` and transfers it to `to`. + /// If `to` is not an account contract, `to` must support IERC721Receiver; + /// otherwise, the transaction will fail. + /// Emits a [Transfer](Transfer) event. fn _safe_mint( ref self: ContractState, to: ContractAddress, token_id: u256, data: Span ) { @@ -383,6 +392,10 @@ mod ERC721 { ); } + /// Internal function that safely transfers `token_id` token from `from` to `to`, + /// checking first that contract recipients are aware of the ERC721 protocol to + /// prevent tokens from being forever locked. + /// Emits a [Transfer](Transfer) event. fn _safe_transfer( ref self: ContractState, from: ContractAddress, @@ -396,6 +409,7 @@ mod ERC721 { ); } + /// Sets the `token_uri` of `token_id`. fn _set_token_uri(ref self: ContractState, token_id: u256, token_uri: felt252) { assert(self._exists(token_id), 'ERC721: invalid token ID'); self._token_uri.write(token_id, token_uri) @@ -403,6 +417,9 @@ mod ERC721 { } #[internal] + /// Internal function that checks if `to` either is an account contract or + /// has registered support for the ERC721 interface through SRC-5. + /// The transaction does not execute if both cases are false. fn _check_on_erc721_received( from: ContractAddress, to: ContractAddress, token_id: u256, data: Span ) -> bool { From fbdd7593681a1515dabcbf722fb99c88edf200fe Mon Sep 17 00:00:00 2001 From: "Bal7hazar @ Carbonable" Date: Thu, 24 Aug 2023 23:04:41 +0200 Subject: [PATCH 106/246] Upgrade Cairo version and Scarb version to latest releases (#712) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 👽️ Update code due to breaking changes * ⬆️ Upgrade scarb and cairo versions * 💄 Code format --- .github/workflows/test.yml | 2 +- Scarb.toml | 4 +- src/account/account.cairo | 2 +- src/tests/access/test_accesscontrol.cairo | 20 ++--- .../access/test_dual_accesscontrol.cairo | 57 +++++------- src/tests/access/test_dual_ownable.cairo | 30 +++---- src/tests/access/test_ownable.cairo | 26 +++--- src/tests/account/test_account.cairo | 8 +- src/tests/account/test_dual_account.cairo | 45 ++++------ src/tests/introspection/test_dual_src5.cairo | 13 ++- src/tests/introspection/test_src5.cairo | 2 +- src/tests/mocks/reentrancy_mock.cairo | 5 +- src/tests/security/test_initializable.cairo | 2 +- src/tests/security/test_pausable.cairo | 8 +- src/tests/security/test_reentrancyguard.cairo | 4 +- src/tests/token/test_dual20.cairo | 38 ++++---- src/tests/token/test_dual721.cairo | 80 ++++++++--------- src/tests/token/test_dual721_receiver.cairo | 27 ++---- src/tests/token/test_erc20.cairo | 46 +++++----- src/tests/token/test_erc721.cairo | 90 +++++++++---------- src/tests/upgrades/test_upgradeable.cairo | 4 +- src/token/erc721/erc721.cairo | 9 +- src/upgrades/upgradeable.cairo | 2 +- 23 files changed, 239 insertions(+), 285 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8357486b9..1dccc55bf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/checkout@v3 - uses: software-mansion/setup-scarb@v1 with: - scarb-version: "0.6.2" + scarb-version: "0.7.0" - name: Markdown lint uses: DavidAnson/markdownlint-cli2-action@5b7c9f74fec47e6b15667b2cc23c63dff11e449e # v9 with: diff --git a/Scarb.toml b/Scarb.toml index 73083f109..13ffeb95c 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -1,7 +1,7 @@ [package] name = "openzeppelin" version = "0.7.0-rc.0" -cairo-version = "2.1.1" +cairo-version = "2.2.0" authors = ["OpenZeppelin Community "] description = "OpenZeppelin Contracts written in Cairo for StarkNet, a decentralized ZK Rollup" documentation = "https://docs.openzeppelin.com/contracts-cairo" @@ -11,7 +11,7 @@ license-file = "LICENSE" keywords = ["openzeppelin", "starknet", "cairo", "contracts", "security", "standards"] [dependencies] -starknet = ">=2.1.1" +starknet = ">=2.2.0" [lib] diff --git a/src/account/account.cairo b/src/account/account.cairo index 8fffb55ea..3bda0e34a 100644 --- a/src/account/account.cairo +++ b/src/account/account.cairo @@ -241,6 +241,6 @@ mod Account { #[internal] fn _execute_single_call(call: Call) -> Span { let Call{to, selector, calldata } = call; - starknet::call_contract_syscall(to, selector, calldata.span()).unwrap_syscall() + starknet::call_contract_syscall(to, selector, calldata.span()).unwrap() } } diff --git a/src/tests/access/test_accesscontrol.cairo b/src/tests/access/test_accesscontrol.cairo index 8dd4af473..c8aeab26b 100644 --- a/src/tests/access/test_accesscontrol.cairo +++ b/src/tests/access/test_accesscontrol.cairo @@ -111,7 +111,7 @@ fn test_assert_only_role() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Caller is missing role', ))] +#[should_panic(expected: ('Caller is missing role',))] fn test_assert_only_role_unauthorized() { let state = setup(); testing::set_caller_address(OTHER()); @@ -120,7 +120,7 @@ fn test_assert_only_role_unauthorized() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Caller is missing role', ))] +#[should_panic(expected: ('Caller is missing role',))] fn test_assert_only_role_unauthorized_when_authorized_for_another_role() { let mut state = setup(); AccessControlImpl::grant_role(ref state, ROLE, AUTHORIZED()); @@ -181,7 +181,7 @@ fn test_grantRole_multiple_times_for_granted_role() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Caller is missing role', ))] +#[should_panic(expected: ('Caller is missing role',))] fn test_grant_role_unauthorized() { let mut state = setup(); testing::set_caller_address(AUTHORIZED()); @@ -190,7 +190,7 @@ fn test_grant_role_unauthorized() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Caller is missing role', ))] +#[should_panic(expected: ('Caller is missing role',))] fn test_grantRole_unauthorized() { let mut state = setup(); testing::set_caller_address(AUTHORIZED()); @@ -277,7 +277,7 @@ fn test_revokeRole_multiple_times_for_granted_role() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Caller is missing role', ))] +#[should_panic(expected: ('Caller is missing role',))] fn test_revoke_role_unauthorized() { let mut state = setup(); testing::set_caller_address(OTHER()); @@ -286,7 +286,7 @@ fn test_revoke_role_unauthorized() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Caller is missing role', ))] +#[should_panic(expected: ('Caller is missing role',))] fn test_revokeRole_unauthorized() { let mut state = setup(); testing::set_caller_address(OTHER()); @@ -380,7 +380,7 @@ fn test_renounceRole_multiple_times_for_granted_role() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Can only renounce role for self', ))] +#[should_panic(expected: ('Can only renounce role for self',))] fn test_renounce_role_unauthorized() { let mut state = setup(); testing::set_caller_address(ADMIN()); @@ -392,7 +392,7 @@ fn test_renounce_role_unauthorized() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Can only renounce role for self', ))] +#[should_panic(expected: ('Can only renounce role for self',))] fn test_renounceRole_unauthorized() { let mut state = setup(); testing::set_caller_address(ADMIN()); @@ -456,7 +456,7 @@ fn test_new_admin_can_revoke_roles() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Caller is missing role', ))] +#[should_panic(expected: ('Caller is missing role',))] fn test_previous_admin_cannot_grant_roles() { let mut state = setup(); InternalImpl::_set_role_admin(ref state, ROLE, OTHER_ROLE); @@ -466,7 +466,7 @@ fn test_previous_admin_cannot_grant_roles() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Caller is missing role', ))] +#[should_panic(expected: ('Caller is missing role',))] fn test_previous_admin_cannot_revoke_roles() { let mut state = setup(); InternalImpl::_set_role_admin(ref state, ROLE, OTHER_ROLE); diff --git a/src/tests/access/test_dual_accesscontrol.cairo b/src/tests/access/test_dual_accesscontrol.cairo index 094f44a21..0a2f3527d 100644 --- a/src/tests/access/test_dual_accesscontrol.cairo +++ b/src/tests/access/test_dual_accesscontrol.cairo @@ -28,11 +28,8 @@ fn setup_snake() -> (DualCaseAccessControl, IAccessControlDispatcher) { calldata.append_serde(ADMIN()); let target = utils::deploy(SnakeAccessControlMock::TEST_CLASS_HASH, calldata); ( - DualCaseAccessControl { - contract_address: target - }, IAccessControlDispatcher { - contract_address: target - } + DualCaseAccessControl { contract_address: target }, + IAccessControlDispatcher { contract_address: target } ) } @@ -41,11 +38,8 @@ fn setup_camel() -> (DualCaseAccessControl, IAccessControlCamelDispatcher) { calldata.append_serde(ADMIN()); let target = utils::deploy(CamelAccessControlMock::TEST_CLASS_HASH, calldata); ( - DualCaseAccessControl { - contract_address: target - }, IAccessControlCamelDispatcher { - contract_address: target - } + DualCaseAccessControl { contract_address: target }, + IAccessControlCamelDispatcher { contract_address: target } ) } @@ -58,11 +52,8 @@ fn setup_accesscontrol_panic() -> (DualCaseAccessControl, DualCaseAccessControl) let snake_target = utils::deploy(SnakeAccessControlPanicMock::TEST_CLASS_HASH, array![]); let camel_target = utils::deploy(CamelAccessControlPanicMock::TEST_CLASS_HASH, array![]); ( - DualCaseAccessControl { - contract_address: snake_target - }, DualCaseAccessControl { - contract_address: camel_target - } + DualCaseAccessControl { contract_address: snake_target }, + DualCaseAccessControl { contract_address: camel_target } ) } @@ -79,7 +70,7 @@ fn test_dual_supports_interface() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_supports_interface() { let dispatcher = setup_non_accesscontrol(); dispatcher.supports_interface(IACCESSCONTROL_ID); @@ -87,7 +78,7 @@ fn test_dual_no_supports_interface() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_supports_interface_exists_and_panics() { let (dispatcher, _) = setup_accesscontrol_panic(); dispatcher.supports_interface(IACCESSCONTROL_ID); @@ -102,7 +93,7 @@ fn test_dual_has_role() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_has_role() { let dispatcher = setup_non_accesscontrol(); dispatcher.has_role(DEFAULT_ADMIN_ROLE, ADMIN()); @@ -110,7 +101,7 @@ fn test_dual_no_has_role() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_has_role_exists_and_panics() { let (dispatcher, _) = setup_accesscontrol_panic(); dispatcher.has_role(DEFAULT_ADMIN_ROLE, ADMIN()); @@ -125,7 +116,7 @@ fn test_dual_get_role_admin() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_get_role_admin() { let dispatcher = setup_non_accesscontrol(); dispatcher.get_role_admin(ROLE); @@ -133,7 +124,7 @@ fn test_dual_no_get_role_admin() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_get_role_admin_exists_and_panics() { let (dispatcher, _) = setup_accesscontrol_panic(); dispatcher.get_role_admin(ROLE); @@ -150,7 +141,7 @@ fn test_dual_grant_role() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_grant_role() { let dispatcher = setup_non_accesscontrol(); dispatcher.grant_role(ROLE, AUTHORIZED()); @@ -158,7 +149,7 @@ fn test_dual_no_grant_role() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_grant_role_exists_and_panics() { let (dispatcher, _) = setup_accesscontrol_panic(); dispatcher.grant_role(ROLE, AUTHORIZED()); @@ -175,7 +166,7 @@ fn test_dual_revoke_role() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_revoke_role() { let dispatcher = setup_non_accesscontrol(); dispatcher.revoke_role(ROLE, AUTHORIZED()); @@ -183,7 +174,7 @@ fn test_dual_no_revoke_role() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_revoke_role_exists_and_panics() { let (dispatcher, _) = setup_accesscontrol_panic(); dispatcher.revoke_role(ROLE, AUTHORIZED()); @@ -200,7 +191,7 @@ fn test_dual_renounce_role() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_renounce_role() { let dispatcher = setup_non_accesscontrol(); dispatcher.renounce_role(DEFAULT_ADMIN_ROLE, ADMIN()); @@ -208,7 +199,7 @@ fn test_dual_no_renounce_role() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_renounce_role_exists_and_panics() { let (dispatcher, _) = setup_accesscontrol_panic(); dispatcher.renounce_role(DEFAULT_ADMIN_ROLE, ADMIN()); @@ -227,7 +218,7 @@ fn test_dual_supportsInterface() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_supportsInterface_exists_and_panics() { let (_, dispatcher) = setup_accesscontrol_panic(); dispatcher.supports_interface(IACCESSCONTROL_ID); @@ -242,7 +233,7 @@ fn test_dual_hasRole() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_hasRole_exists_and_panics() { let (_, dispatcher) = setup_accesscontrol_panic(); dispatcher.has_role(DEFAULT_ADMIN_ROLE, ADMIN()); @@ -257,7 +248,7 @@ fn test_dual_getRoleAdmin() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_getRoleAdmin_exists_and_panics() { let (_, dispatcher) = setup_accesscontrol_panic(); dispatcher.get_role_admin(ROLE); @@ -274,7 +265,7 @@ fn test_dual_grantRole() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_grantRole_exists_and_panics() { let (_, dispatcher) = setup_accesscontrol_panic(); dispatcher.grant_role(ROLE, AUTHORIZED()); @@ -292,7 +283,7 @@ fn test_dual_revokeRole() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_revokeRole_exists_and_panics() { let (_, dispatcher) = setup_accesscontrol_panic(); dispatcher.revoke_role(ROLE, AUTHORIZED()); @@ -309,7 +300,7 @@ fn test_dual_renounceRole() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_renounceRole_exists_and_panics() { let (_, dispatcher) = setup_accesscontrol_panic(); dispatcher.renounce_role(DEFAULT_ADMIN_ROLE, ADMIN()); diff --git a/src/tests/access/test_dual_ownable.cairo b/src/tests/access/test_dual_ownable.cairo index 380672757..8055bd1fc 100644 --- a/src/tests/access/test_dual_ownable.cairo +++ b/src/tests/access/test_dual_ownable.cairo @@ -45,11 +45,8 @@ fn setup_camel() -> (DualCaseOwnable, IOwnableCamelOnlyDispatcher) { calldata.append_serde(OWNER()); let target = utils::deploy(CamelOwnableMock::TEST_CLASS_HASH, calldata); ( - DualCaseOwnable { - contract_address: target - }, IOwnableCamelOnlyDispatcher { - contract_address: target - } + DualCaseOwnable { contract_address: target }, + IOwnableCamelOnlyDispatcher { contract_address: target } ) } @@ -63,11 +60,8 @@ fn setup_ownable_panic() -> (DualCaseOwnable, DualCaseOwnable) { let snake_target = utils::deploy(SnakeOwnablePanicMock::TEST_CLASS_HASH, ArrayTrait::new()); let camel_target = utils::deploy(CamelOwnablePanicMock::TEST_CLASS_HASH, ArrayTrait::new()); ( - DualCaseOwnable { - contract_address: snake_target - }, DualCaseOwnable { - contract_address: camel_target - } + DualCaseOwnable { contract_address: snake_target }, + DualCaseOwnable { contract_address: camel_target } ) } @@ -86,7 +80,7 @@ fn test_dual_owner() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_owner() { let dispatcher = setup_non_ownable(); dispatcher.owner(); @@ -94,7 +88,7 @@ fn test_dual_no_owner() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_owner_exists_and_panics() { let (dispatcher, _) = setup_ownable_panic(); dispatcher.owner(); @@ -115,7 +109,7 @@ fn test_dual_transfer_ownership() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_transfer_ownership() { let dispatcher = setup_non_ownable(); dispatcher.transfer_ownership(NEW_OWNER()); @@ -123,7 +117,7 @@ fn test_dual_no_transfer_ownership() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_transfer_ownership_exists_and_panics() { let (dispatcher, _) = setup_ownable_panic(); dispatcher.transfer_ownership(NEW_OWNER()); @@ -141,7 +135,7 @@ fn test_dual_renounce_ownership() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_renounce_ownership() { let dispatcher = setup_non_ownable(); dispatcher.renounce_ownership(); @@ -149,7 +143,7 @@ fn test_dual_no_renounce_ownership() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_renounce_ownership_exists_and_panics() { let (dispatcher, _) = setup_ownable_panic(); dispatcher.renounce_ownership(); @@ -170,7 +164,7 @@ fn test_dual_transferOwnership() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_transferOwnership_exists_and_panics() { let (_, dispatcher) = setup_ownable_panic(); dispatcher.transfer_ownership(NEW_OWNER()); @@ -187,7 +181,7 @@ fn test_dual_renounceOwnership() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_renounceOwnership_exists_and_panics() { let (_, dispatcher) = setup_ownable_panic(); dispatcher.renounce_ownership(); diff --git a/src/tests/access/test_ownable.cairo b/src/tests/access/test_ownable.cairo index 214329b9b..04b9033a2 100644 --- a/src/tests/access/test_ownable.cairo +++ b/src/tests/access/test_ownable.cairo @@ -2,7 +2,7 @@ use openzeppelin::access::ownable::Ownable::InternalImpl; use openzeppelin::access::ownable::Ownable::OwnableCamelOnlyImpl; use openzeppelin::access::ownable::Ownable::OwnableImpl; use openzeppelin::access::ownable::Ownable::OwnershipTransferred; -use openzeppelin::access::ownable::Ownable::_owner::InternalContractStateTrait; +use openzeppelin::access::ownable::Ownable::_owner::InternalContractMemberStateTrait; use openzeppelin::access::ownable::Ownable; use openzeppelin::tests::utils::constants::{ZERO, OTHER, OWNER}; use openzeppelin::tests::utils; @@ -57,7 +57,7 @@ fn test_assert_only_owner() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Caller is not the owner', ))] +#[should_panic(expected: ('Caller is not the owner',))] fn test_assert_only_owner_when_not_owner() { let state = setup(); testing::set_caller_address(OTHER()); @@ -66,7 +66,7 @@ fn test_assert_only_owner_when_not_owner() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Caller is the zero address', ))] +#[should_panic(expected: ('Caller is the zero address',))] fn test_assert_only_owner_when_caller_zero() { let state = setup(); InternalImpl::assert_only_owner(@state); @@ -105,7 +105,7 @@ fn test_transfer_ownership() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('New owner is the zero address', ))] +#[should_panic(expected: ('New owner is the zero address',))] fn test_transfer_ownership_to_zero() { let mut state = setup(); testing::set_caller_address(OWNER()); @@ -114,7 +114,7 @@ fn test_transfer_ownership_to_zero() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Caller is the zero address', ))] +#[should_panic(expected: ('Caller is the zero address',))] fn test_transfer_ownership_from_zero() { let mut state = setup(); OwnableImpl::transfer_ownership(ref state, OTHER()); @@ -122,7 +122,7 @@ fn test_transfer_ownership_from_zero() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Caller is not the owner', ))] +#[should_panic(expected: ('Caller is not the owner',))] fn test_transfer_ownership_from_nonowner() { let mut state = setup(); testing::set_caller_address(OTHER()); @@ -143,7 +143,7 @@ fn test_transferOwnership() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('New owner is the zero address', ))] +#[should_panic(expected: ('New owner is the zero address',))] fn test_transferOwnership_to_zero() { let mut state = setup(); testing::set_caller_address(OWNER()); @@ -152,7 +152,7 @@ fn test_transferOwnership_to_zero() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Caller is the zero address', ))] +#[should_panic(expected: ('Caller is the zero address',))] fn test_transferOwnership_from_zero() { let mut state = setup(); OwnableCamelOnlyImpl::transferOwnership(ref state, OTHER()); @@ -160,7 +160,7 @@ fn test_transferOwnership_from_zero() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Caller is not the owner', ))] +#[should_panic(expected: ('Caller is not the owner',))] fn test_transferOwnership_from_nonowner() { let mut state = setup(); testing::set_caller_address(OTHER()); @@ -185,7 +185,7 @@ fn test_renounce_ownership() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Caller is the zero address', ))] +#[should_panic(expected: ('Caller is the zero address',))] fn test_renounce_ownership_from_zero_address() { let mut state = setup(); OwnableImpl::renounce_ownership(ref state); @@ -193,7 +193,7 @@ fn test_renounce_ownership_from_zero_address() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Caller is not the owner', ))] +#[should_panic(expected: ('Caller is not the owner',))] fn test_renounce_ownership_from_nonowner() { let mut state = setup(); testing::set_caller_address(OTHER()); @@ -214,7 +214,7 @@ fn test_renounceOwnership() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Caller is the zero address', ))] +#[should_panic(expected: ('Caller is the zero address',))] fn test_renounceOwnership_from_zero_address() { let mut state = setup(); OwnableCamelOnlyImpl::renounceOwnership(ref state); @@ -222,7 +222,7 @@ fn test_renounceOwnership_from_zero_address() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Caller is not the owner', ))] +#[should_panic(expected: ('Caller is not the owner',))] fn test_renounceOwnership_from_nonowner() { let mut state = setup(); testing::set_caller_address(OTHER()); diff --git a/src/tests/account/test_account.cairo b/src/tests/account/test_account.cairo index d66e9fbfe..83bea533b 100644 --- a/src/tests/account/test_account.cairo +++ b/src/tests/account/test_account.cairo @@ -423,7 +423,7 @@ fn test_multicall_zero_calls() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Account: invalid caller', ))] +#[should_panic(expected: ('Account: invalid caller',))] fn test_account_called_from_contract() { let calls = array![]; let caller = contract_address_const::<0x123>(); @@ -465,7 +465,7 @@ fn test_public_key_setter_and_getter() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Account: unauthorized', ))] +#[should_panic(expected: ('Account: unauthorized',))] fn test_public_key_setter_different_account() { let mut state = STATE(); let caller = contract_address_const::<0x123>(); @@ -506,7 +506,7 @@ fn test_public_key_setter_and_getter_camel() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Account: unauthorized', ))] +#[should_panic(expected: ('Account: unauthorized',))] fn test_public_key_setter_different_account_camel() { let mut state = STATE(); let caller = contract_address_const::<0x123>(); @@ -543,7 +543,7 @@ fn test_assert_only_self_true() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Account: unauthorized', ))] +#[should_panic(expected: ('Account: unauthorized',))] fn test_assert_only_self_false() { testing::set_contract_address(ACCOUNT_ADDRESS()); let other = contract_address_const::<0x4567>(); diff --git a/src/tests/account/test_dual_account.cairo b/src/tests/account/test_dual_account.cairo index f38c308c5..2c2156ed6 100644 --- a/src/tests/account/test_dual_account.cairo +++ b/src/tests/account/test_dual_account.cairo @@ -29,11 +29,8 @@ fn setup_snake() -> (DualCaseAccount, AccountABIDispatcher) { let mut calldata = array![PUBLIC_KEY]; let target = utils::deploy(SnakeAccountMock::TEST_CLASS_HASH, calldata); ( - DualCaseAccount { - contract_address: target - }, AccountABIDispatcher { - contract_address: target - } + DualCaseAccount { contract_address: target }, + AccountABIDispatcher { contract_address: target } ) } @@ -41,11 +38,8 @@ fn setup_camel() -> (DualCaseAccount, AccountCamelABIDispatcher) { let mut calldata = array![PUBLIC_KEY]; let target = utils::deploy(CamelAccountMock::TEST_CLASS_HASH, calldata); ( - DualCaseAccount { - contract_address: target - }, AccountCamelABIDispatcher { - contract_address: target - } + DualCaseAccount { contract_address: target }, + AccountCamelABIDispatcher { contract_address: target } ) } @@ -59,11 +53,8 @@ fn setup_account_panic() -> (DualCaseAccount, DualCaseAccount) { let snake_target = utils::deploy(SnakeAccountPanicMock::TEST_CLASS_HASH, array![]); let camel_target = utils::deploy(CamelAccountPanicMock::TEST_CLASS_HASH, array![]); ( - DualCaseAccount { - contract_address: snake_target - }, DualCaseAccount { - contract_address: camel_target - } + DualCaseAccount { contract_address: snake_target }, + DualCaseAccount { contract_address: camel_target } ) } @@ -84,7 +75,7 @@ fn test_dual_set_public_key() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_set_public_key() { let dispatcher = setup_non_account(); dispatcher.set_public_key(NEW_PUBLIC_KEY); @@ -92,7 +83,7 @@ fn test_dual_no_set_public_key() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_set_public_key_exists_and_panics() { let (dispatcher, _) = setup_account_panic(); dispatcher.set_public_key(NEW_PUBLIC_KEY); @@ -107,7 +98,7 @@ fn test_dual_get_public_key() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_get_public_key() { let dispatcher = setup_non_account(); dispatcher.get_public_key(); @@ -115,7 +106,7 @@ fn test_dual_no_get_public_key() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_get_public_key_exists_and_panics() { let (dispatcher, _) = setup_account_panic(); dispatcher.get_public_key(); @@ -139,7 +130,7 @@ fn test_dual_is_valid_signature() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_is_valid_signature() { let hash = 0x0; let signature = array![]; @@ -150,7 +141,7 @@ fn test_dual_no_is_valid_signature() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_is_valid_signature_exists_and_panics() { let hash = 0x0; let signature = array![]; @@ -168,7 +159,7 @@ fn test_dual_supports_interface() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_supports_interface() { let dispatcher = setup_non_account(); dispatcher.supports_interface(ISRC5_ID); @@ -176,7 +167,7 @@ fn test_dual_no_supports_interface() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_supports_interface_exists_and_panics() { let (dispatcher, _) = setup_account_panic(); dispatcher.supports_interface(ISRC5_ID); @@ -199,7 +190,7 @@ fn test_dual_setPublicKey() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_setPublicKey_exists_and_panics() { let (_, dispatcher) = setup_account_panic(); dispatcher.set_public_key(NEW_PUBLIC_KEY); @@ -214,7 +205,7 @@ fn test_dual_getPublicKey() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_getPublicKey_exists_and_panics() { let (_, dispatcher) = setup_account_panic(); dispatcher.get_public_key(); @@ -238,7 +229,7 @@ fn test_dual_isValidSignature() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_isValidSignature_exists_and_panics() { let hash = 0x0; let signature = array![]; @@ -256,7 +247,7 @@ fn test_dual_supportsInterface() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_supportsInterface_exists_and_panics() { let (_, dispatcher) = setup_account_panic(); dispatcher.supports_interface(ISRC5_ID); diff --git a/src/tests/introspection/test_dual_src5.cairo b/src/tests/introspection/test_dual_src5.cairo index 84157605b..f2122f2e1 100644 --- a/src/tests/introspection/test_dual_src5.cairo +++ b/src/tests/introspection/test_dual_src5.cairo @@ -39,11 +39,8 @@ fn setup_src5_panic() -> (DualCaseSRC5, DualCaseSRC5) { let snake_target = utils::deploy(SnakeSRC5PanicMock::TEST_CLASS_HASH, array![]); let camel_target = utils::deploy(CamelSRC5PanicMock::TEST_CLASS_HASH, array![]); ( - DualCaseSRC5 { - contract_address: snake_target - }, DualCaseSRC5 { - contract_address: camel_target - } + DualCaseSRC5 { contract_address: snake_target }, + DualCaseSRC5 { contract_address: camel_target } ) } @@ -60,7 +57,7 @@ fn test_dual_supports_interface() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_supports_interface() { let dispatcher = setup_non_src5(); dispatcher.supports_interface(ISRC5_ID); @@ -68,7 +65,7 @@ fn test_dual_no_supports_interface() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_supports_interface_exists_and_panics() { let (dispatcher, _) = setup_src5_panic(); dispatcher.supports_interface(ISRC5_ID); @@ -87,7 +84,7 @@ fn test_dual_supportsInterface() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_supportsInterface_exists_and_panics() { let (_, dispatcher) = setup_src5_panic(); dispatcher.supports_interface(ISRC5_ID); diff --git a/src/tests/introspection/test_src5.cairo b/src/tests/introspection/test_src5.cairo index d81cf3e37..a8ca2b699 100644 --- a/src/tests/introspection/test_src5.cairo +++ b/src/tests/introspection/test_src5.cairo @@ -44,7 +44,7 @@ fn test_deregister_interface() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('SRC5: invalid id', ))] +#[should_panic(expected: ('SRC5: invalid id',))] fn test_deregister_default_interface() { let mut state = STATE(); InternalImpl::deregister_interface(ref state, ISRC5_ID); diff --git a/src/tests/mocks/reentrancy_mock.cairo b/src/tests/mocks/reentrancy_mock.cairo index 1204afa47..dc81bb9ea 100644 --- a/src/tests/mocks/reentrancy_mock.cairo +++ b/src/tests/mocks/reentrancy_mock.cairo @@ -72,9 +72,8 @@ mod ReentrancyMock { if n != 0 { self.count(); let this: ContractAddress = get_contract_address(); - IReentrancyGuardedDispatcher { - contract_address: this - }.count_external_recursive(n - 1) + IReentrancyGuardedDispatcher { contract_address: this } + .count_external_recursive(n - 1) } ReentrancyGuard::InternalImpl::end(ref unsafe_state); diff --git a/src/tests/security/test_initializable.cairo b/src/tests/security/test_initializable.cairo index 66d2b89dc..cd34ea375 100644 --- a/src/tests/security/test_initializable.cairo +++ b/src/tests/security/test_initializable.cairo @@ -16,7 +16,7 @@ fn test_initialize() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Initializable: is initialized', ))] +#[should_panic(expected: ('Initializable: is initialized',))] fn test_initialize_when_initialized() { let mut state = STATE(); InternalImpl::initialize(ref state); diff --git a/src/tests/security/test_pausable.cairo b/src/tests/security/test_pausable.cairo index f48a2904b..26497e121 100644 --- a/src/tests/security/test_pausable.cairo +++ b/src/tests/security/test_pausable.cairo @@ -49,7 +49,7 @@ fn test_assert_paused_when_paused() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Pausable: not paused', ))] +#[should_panic(expected: ('Pausable: not paused',))] fn test_assert_paused_when_not_paused() { let state = STATE(); InternalImpl::assert_paused(@state); @@ -61,7 +61,7 @@ fn test_assert_paused_when_not_paused() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Pausable: paused', ))] +#[should_panic(expected: ('Pausable: paused',))] fn test_assert_not_paused_when_paused() { let mut state = STATE(); InternalImpl::_pause(ref state); @@ -93,7 +93,7 @@ fn test_pause_when_unpaused() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Pausable: paused', ))] +#[should_panic(expected: ('Pausable: paused',))] fn test_pause_when_paused() { let mut state = STATE(); InternalImpl::_pause(ref state); @@ -121,7 +121,7 @@ fn test_unpause_when_paused() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Pausable: not paused', ))] +#[should_panic(expected: ('Pausable: not paused',))] fn test_unpause_when_unpaused() { let mut state = STATE(); assert(!PausableImpl::is_paused(@state), 'Should be paused'); diff --git a/src/tests/security/test_reentrancyguard.cairo b/src/tests/security/test_reentrancyguard.cairo index b608706db..b5aa84056 100644 --- a/src/tests/security/test_reentrancyguard.cairo +++ b/src/tests/security/test_reentrancyguard.cairo @@ -1,5 +1,5 @@ use openzeppelin::security::reentrancyguard::ReentrancyGuard::InternalImpl; -use openzeppelin::security::reentrancyguard::ReentrancyGuard::entered::InternalContractStateTrait; +use openzeppelin::security::reentrancyguard::ReentrancyGuard::entered::InternalContractMemberStateTrait; use openzeppelin::security::reentrancyguard::ReentrancyGuard; use openzeppelin::tests::mocks::reentrancy_attacker_mock::Attacker; use openzeppelin::tests::mocks::reentrancy_mock::IReentrancyMockDispatcher; @@ -33,7 +33,7 @@ fn test_reentrancy_guard_start() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ReentrancyGuard: reentrant call', ))] +#[should_panic(expected: ('ReentrancyGuard: reentrant call',))] fn test_reentrancy_guard_start_when_started() { let mut state = STATE(); diff --git a/src/tests/token/test_dual20.cairo b/src/tests/token/test_dual20.cairo index 6e1e5dc86..08eaf82c7 100644 --- a/src/tests/token/test_dual20.cairo +++ b/src/tests/token/test_dual20.cairo @@ -90,7 +90,7 @@ fn test_dual_name() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_name() { let dispatcher = setup_non_erc20(); dispatcher.name(); @@ -98,7 +98,7 @@ fn test_dual_no_name() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_name_exists_and_panics() { let (dispatcher, _) = setup_erc20_panic(); dispatcher.name(); @@ -115,7 +115,7 @@ fn test_dual_symbol() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_symbol() { let dispatcher = setup_non_erc20(); dispatcher.symbol(); @@ -123,7 +123,7 @@ fn test_dual_no_symbol() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_symbol_exists_and_panics() { let (dispatcher, _) = setup_erc20_panic(); dispatcher.symbol(); @@ -140,7 +140,7 @@ fn test_dual_decimals() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_decimals() { let dispatcher = setup_non_erc20(); dispatcher.decimals(); @@ -148,7 +148,7 @@ fn test_dual_no_decimals() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_decimals_exists_and_panics() { let (dispatcher, _) = setup_erc20_panic(); dispatcher.decimals(); @@ -170,7 +170,7 @@ fn test_dual_transfer() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_transfer() { let dispatcher = setup_non_erc20(); dispatcher.transfer(RECIPIENT(), VALUE); @@ -178,7 +178,7 @@ fn test_dual_no_transfer() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_transfer_exists_and_panics() { let (dispatcher, _) = setup_erc20_panic(); dispatcher.transfer(RECIPIENT(), VALUE); @@ -200,7 +200,7 @@ fn test_dual_approve() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_approve() { let dispatcher = setup_non_erc20(); dispatcher.approve(SPENDER(), VALUE); @@ -208,7 +208,7 @@ fn test_dual_no_approve() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_approve_exists_and_panics() { let (dispatcher, _) = setup_erc20_panic(); dispatcher.approve(SPENDER(), VALUE); @@ -227,7 +227,7 @@ fn test_dual_total_supply() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_total_supply() { let dispatcher = setup_non_erc20(); dispatcher.total_supply(); @@ -235,7 +235,7 @@ fn test_dual_no_total_supply() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_total_supply_exists_and_panics() { let (dispatcher, _) = setup_erc20_panic(); dispatcher.total_supply(); @@ -250,7 +250,7 @@ fn test_dual_balance_of() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_balance_of() { let dispatcher = setup_non_erc20(); dispatcher.balance_of(OWNER()); @@ -258,7 +258,7 @@ fn test_dual_no_balance_of() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_balance_of_exists_and_panics() { let (dispatcher, _) = setup_erc20_panic(); dispatcher.balance_of(OWNER()); @@ -278,7 +278,7 @@ fn test_dual_transfer_from() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_transfer_from() { let dispatcher = setup_non_erc20(); dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); @@ -286,7 +286,7 @@ fn test_dual_no_transfer_from() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_transfer_from_exists_and_panics() { let (dispatcher, _) = setup_erc20_panic(); dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); @@ -305,7 +305,7 @@ fn test_dual_totalSupply() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_totalSupply_exists_and_panics() { let (_, dispatcher) = setup_erc20_panic(); dispatcher.total_supply(); @@ -320,7 +320,7 @@ fn test_dual_balanceOf() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_balanceOf_exists_and_panics() { let (_, dispatcher) = setup_erc20_panic(); dispatcher.balance_of(OWNER()); @@ -340,7 +340,7 @@ fn test_dual_transferFrom() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_transferFrom_exists_and_panics() { let (_, dispatcher) = setup_erc20_panic(); dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); diff --git a/src/tests/token/test_dual721.cairo b/src/tests/token/test_dual721.cairo index b5f02f7e9..6c1744258 100644 --- a/src/tests/token/test_dual721.cairo +++ b/src/tests/token/test_dual721.cairo @@ -76,11 +76,8 @@ fn setup_camel() -> (DualCaseERC721, IERC721CamelOnlyDispatcher) { set_contract_address(OWNER()); let target = utils::deploy(CamelERC721Mock::TEST_CLASS_HASH, calldata); ( - DualCaseERC721 { - contract_address: target - }, IERC721CamelOnlyDispatcher { - contract_address: target - } + DualCaseERC721 { contract_address: target }, + IERC721CamelOnlyDispatcher { contract_address: target } ) } @@ -94,11 +91,8 @@ fn setup_erc721_panic() -> (DualCaseERC721, DualCaseERC721) { let snake_target = utils::deploy(SnakeERC721PanicMock::TEST_CLASS_HASH, array![]); let camel_target = utils::deploy(CamelERC721PanicMock::TEST_CLASS_HASH, array![]); ( - DualCaseERC721 { - contract_address: snake_target - }, DualCaseERC721 { - contract_address: camel_target - } + DualCaseERC721 { contract_address: snake_target }, + DualCaseERC721 { contract_address: camel_target } ) } @@ -121,7 +115,7 @@ fn test_dual_name() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_name() { let dispatcher = setup_non_erc721(); dispatcher.name(); @@ -129,7 +123,7 @@ fn test_dual_no_name() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_name_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.name(); @@ -146,7 +140,7 @@ fn test_dual_symbol() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_symbol() { let dispatcher = setup_non_erc721(); dispatcher.symbol(); @@ -154,7 +148,7 @@ fn test_dual_no_symbol() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_symbol_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.symbol(); @@ -176,7 +170,7 @@ fn test_dual_approve() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_approve() { let dispatcher = setup_non_erc721(); dispatcher.approve(SPENDER(), TOKEN_ID); @@ -184,7 +178,7 @@ fn test_dual_no_approve() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_approve_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.approve(SPENDER(), TOKEN_ID); @@ -203,7 +197,7 @@ fn test_dual_balance_of() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_balance_of() { let dispatcher = setup_non_erc721(); dispatcher.balance_of(OWNER()); @@ -211,7 +205,7 @@ fn test_dual_no_balance_of() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_balance_of_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.balance_of(OWNER()); @@ -226,7 +220,7 @@ fn test_dual_owner_of() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_owner_of() { let dispatcher = setup_non_erc721(); dispatcher.owner_of(TOKEN_ID); @@ -234,7 +228,7 @@ fn test_dual_no_owner_of() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_owner_of_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.owner_of(TOKEN_ID); @@ -250,7 +244,7 @@ fn test_dual_transfer_from() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_transfer_from() { let dispatcher = setup_non_erc721(); dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID); @@ -258,7 +252,7 @@ fn test_dual_no_transfer_from() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_transfer_from_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID); @@ -275,7 +269,7 @@ fn test_dual_safe_transfer_from() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_safe_transfer_from() { let dispatcher = setup_non_erc721(); dispatcher.safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); @@ -283,7 +277,7 @@ fn test_dual_no_safe_transfer_from() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_safe_transfer_from_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); @@ -300,7 +294,7 @@ fn test_dual_get_approved() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_get_approved() { let dispatcher = setup_non_erc721(); dispatcher.get_approved(TOKEN_ID); @@ -308,7 +302,7 @@ fn test_dual_no_get_approved() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_get_approved_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.get_approved(TOKEN_ID); @@ -325,7 +319,7 @@ fn test_dual_set_approval_for_all() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_set_approval_for_all() { let dispatcher = setup_non_erc721(); dispatcher.set_approval_for_all(OPERATOR(), true); @@ -333,7 +327,7 @@ fn test_dual_no_set_approval_for_all() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_set_approval_for_all_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.set_approval_for_all(OPERATOR(), true); @@ -350,7 +344,7 @@ fn test_dual_is_approved_for_all() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_is_approved_for_all() { let dispatcher = setup_non_erc721(); dispatcher.is_approved_for_all(OWNER(), OPERATOR()); @@ -358,7 +352,7 @@ fn test_dual_no_is_approved_for_all() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_is_approved_for_all_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.is_approved_for_all(OWNER(), OPERATOR()); @@ -373,7 +367,7 @@ fn test_dual_token_uri() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_token_uri() { let dispatcher = setup_non_erc721(); dispatcher.token_uri(TOKEN_ID); @@ -381,7 +375,7 @@ fn test_dual_no_token_uri() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_token_uri_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.token_uri(TOKEN_ID); @@ -396,7 +390,7 @@ fn test_dual_supports_interface() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_supports_interface() { let dispatcher = setup_non_erc721(); dispatcher.supports_interface(IERC721_ID); @@ -404,7 +398,7 @@ fn test_dual_no_supports_interface() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_supports_interface_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.supports_interface(IERC721_ID); @@ -423,7 +417,7 @@ fn test_dual_balanceOf() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_balanceOf_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); dispatcher.balance_of(OWNER()); @@ -438,7 +432,7 @@ fn test_dual_ownerOf() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_ownerOf_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); dispatcher.owner_of(TOKEN_ID); @@ -455,7 +449,7 @@ fn test_dual_transferFrom() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_transferFrom_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID); @@ -472,7 +466,7 @@ fn test_dual_safeTransferFrom() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_safeTransferFrom_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); dispatcher.safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); @@ -489,7 +483,7 @@ fn test_dual_getApproved() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_getApproved_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); dispatcher.get_approved(TOKEN_ID); @@ -506,7 +500,7 @@ fn test_dual_setApprovalForAll() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_setApprovalForAll_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); dispatcher.set_approval_for_all(OPERATOR(), true); @@ -523,7 +517,7 @@ fn test_dual_isApprovedForAll() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_isApprovedForAll_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); dispatcher.is_approved_for_all(OWNER(), OPERATOR()); @@ -538,7 +532,7 @@ fn test_dual_tokenUri() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_tokenUri_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); dispatcher.token_uri(TOKEN_ID); @@ -553,7 +547,7 @@ fn test_dual_supportsInterface() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_supportsInterface_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); dispatcher.supports_interface(IERC721_ID); diff --git a/src/tests/token/test_dual721_receiver.cairo b/src/tests/token/test_dual721_receiver.cairo index 9fde537c0..876661b8c 100644 --- a/src/tests/token/test_dual721_receiver.cairo +++ b/src/tests/token/test_dual721_receiver.cairo @@ -49,11 +49,8 @@ fn setup_snake() -> (DualCaseERC721Receiver, IERC721ReceiverDispatcher) { let mut calldata = ArrayTrait::new(); let target = utils::deploy(SnakeERC721ReceiverMock::TEST_CLASS_HASH, calldata); ( - DualCaseERC721Receiver { - contract_address: target - }, IERC721ReceiverDispatcher { - contract_address: target - } + DualCaseERC721Receiver { contract_address: target }, + IERC721ReceiverDispatcher { contract_address: target } ) } @@ -61,11 +58,8 @@ fn setup_camel() -> (DualCaseERC721Receiver, IERC721ReceiverCamelDispatcher) { let mut calldata = ArrayTrait::new(); let target = utils::deploy(CamelERC721ReceiverMock::TEST_CLASS_HASH, calldata); ( - DualCaseERC721Receiver { - contract_address: target - }, IERC721ReceiverCamelDispatcher { - contract_address: target - } + DualCaseERC721Receiver { contract_address: target }, + IERC721ReceiverCamelDispatcher { contract_address: target } ) } @@ -83,11 +77,8 @@ fn setup_erc721_receiver_panic() -> (DualCaseERC721Receiver, DualCaseERC721Recei CamelERC721ReceiverPanicMock::TEST_CLASS_HASH, ArrayTrait::new() ); ( - DualCaseERC721Receiver { - contract_address: snake_target - }, DualCaseERC721Receiver { - contract_address: camel_target - } + DualCaseERC721Receiver { contract_address: snake_target }, + DualCaseERC721Receiver { contract_address: camel_target } ) } @@ -108,7 +99,7 @@ fn test_dual_on_erc721_received() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_on_erc721_received() { let dispatcher = setup_non_erc721_receiver(); dispatcher.on_erc721_received(OPERATOR(), OWNER(), TOKEN_ID, DATA(true)); @@ -116,7 +107,7 @@ fn test_dual_no_on_erc721_received() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_on_erc721_received_exists_and_panics() { let (dispatcher, _) = setup_erc721_receiver_panic(); dispatcher.on_erc721_received(OPERATOR(), OWNER(), TOKEN_ID, DATA(true)); @@ -139,7 +130,7 @@ fn test_dual_onERC721Received() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] fn test_dual_onERC721Received_exists_and_panics() { let (_, dispatcher) = setup_erc721_receiver_panic(); dispatcher.on_erc721_received(OPERATOR(), OWNER(), TOKEN_ID, DATA(true)); diff --git a/src/tests/token/test_erc20.cairo b/src/tests/token/test_erc20.cairo index a2e609a1b..4109e909a 100644 --- a/src/tests/token/test_erc20.cairo +++ b/src/tests/token/test_erc20.cairo @@ -130,7 +130,7 @@ fn test_approve() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ERC20: approve from 0', ))] +#[should_panic(expected: ('ERC20: approve from 0',))] fn test_approve_from_zero() { let mut state = setup(); ERC20Impl::approve(ref state, SPENDER(), VALUE); @@ -138,7 +138,7 @@ fn test_approve_from_zero() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ERC20: approve to 0', ))] +#[should_panic(expected: ('ERC20: approve to 0',))] fn test_approve_to_zero() { let mut state = setup(); testing::set_caller_address(OWNER()); @@ -160,7 +160,7 @@ fn test__approve() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ERC20: approve from 0', ))] +#[should_panic(expected: ('ERC20: approve from 0',))] fn test__approve_from_zero() { let mut state = setup(); InternalImpl::_approve(ref state, Zeroable::zero(), SPENDER(), VALUE); @@ -168,7 +168,7 @@ fn test__approve_from_zero() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ERC20: approve to 0', ))] +#[should_panic(expected: ('ERC20: approve to 0',))] fn test__approve_to_zero() { let mut state = setup(); testing::set_caller_address(OWNER()); @@ -207,7 +207,7 @@ fn test__transfer() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('u256_sub Overflow', ))] +#[should_panic(expected: ('u256_sub Overflow',))] fn test__transfer_not_enough_balance() { let mut state = setup(); testing::set_caller_address(OWNER()); @@ -218,7 +218,7 @@ fn test__transfer_not_enough_balance() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ERC20: transfer from 0', ))] +#[should_panic(expected: ('ERC20: transfer from 0',))] fn test__transfer_from_zero() { let mut state = setup(); InternalImpl::_transfer(ref state, Zeroable::zero(), RECIPIENT(), VALUE); @@ -226,7 +226,7 @@ fn test__transfer_from_zero() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ERC20: transfer to 0', ))] +#[should_panic(expected: ('ERC20: transfer to 0',))] fn test__transfer_to_zero() { let mut state = setup(); InternalImpl::_transfer(ref state, OWNER(), Zeroable::zero(), VALUE); @@ -274,7 +274,7 @@ fn test_transfer_from_doesnt_consume_infinite_allowance() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('u256_sub Overflow', ))] +#[should_panic(expected: ('u256_sub Overflow',))] fn test_transfer_from_greater_than_allowance() { let mut state = setup(); testing::set_caller_address(OWNER()); @@ -287,7 +287,7 @@ fn test_transfer_from_greater_than_allowance() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ERC20: transfer to 0', ))] +#[should_panic(expected: ('ERC20: transfer to 0',))] fn test_transfer_from_to_zero_address() { let mut state = setup(); testing::set_caller_address(OWNER()); @@ -299,7 +299,7 @@ fn test_transfer_from_to_zero_address() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('u256_sub Overflow', ))] +#[should_panic(expected: ('u256_sub Overflow',))] fn test_transfer_from_from_zero_address() { let mut state = setup(); ERC20Impl::transfer_from(ref state, Zeroable::zero(), RECIPIENT(), VALUE); @@ -348,7 +348,7 @@ fn test_transferFrom_doesnt_consume_infinite_allowance() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('u256_sub Overflow', ))] +#[should_panic(expected: ('u256_sub Overflow',))] fn test_transferFrom_greater_than_allowance() { let mut state = setup(); testing::set_caller_address(OWNER()); @@ -361,7 +361,7 @@ fn test_transferFrom_greater_than_allowance() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ERC20: transfer to 0', ))] +#[should_panic(expected: ('ERC20: transfer to 0',))] fn test_transferFrom_to_zero_address() { let mut state = setup(); testing::set_caller_address(OWNER()); @@ -373,7 +373,7 @@ fn test_transferFrom_to_zero_address() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('u256_sub Overflow', ))] +#[should_panic(expected: ('u256_sub Overflow',))] fn test_transferFrom_from_zero_address() { let mut state = setup(); ERC20CamelOnlyImpl::transferFrom(ref state, Zeroable::zero(), RECIPIENT(), VALUE); @@ -399,7 +399,7 @@ fn test_increase_allowance() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ERC20: approve to 0', ))] +#[should_panic(expected: ('ERC20: approve to 0',))] fn test_increase_allowance_to_zero_address() { let mut state = setup(); testing::set_caller_address(OWNER()); @@ -408,7 +408,7 @@ fn test_increase_allowance_to_zero_address() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ERC20: approve from 0', ))] +#[should_panic(expected: ('ERC20: approve from 0',))] fn test_increase_allowance_from_zero_address() { let mut state = setup(); ERC20::increase_allowance(ref state, SPENDER(), VALUE); @@ -430,7 +430,7 @@ fn test_increaseAllowance() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ERC20: approve to 0', ))] +#[should_panic(expected: ('ERC20: approve to 0',))] fn test_increaseAllowance_to_zero_address() { let mut state = setup(); testing::set_caller_address(OWNER()); @@ -439,7 +439,7 @@ fn test_increaseAllowance_to_zero_address() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ERC20: approve from 0', ))] +#[should_panic(expected: ('ERC20: approve from 0',))] fn test_increaseAllowance_from_zero_address() { let mut state = setup(); ERC20::increaseAllowance(ref state, SPENDER(), VALUE); @@ -465,7 +465,7 @@ fn test_decrease_allowance() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('u256_sub Overflow', ))] +#[should_panic(expected: ('u256_sub Overflow',))] fn test_decrease_allowance_to_zero_address() { let mut state = setup(); testing::set_caller_address(OWNER()); @@ -474,7 +474,7 @@ fn test_decrease_allowance_to_zero_address() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('u256_sub Overflow', ))] +#[should_panic(expected: ('u256_sub Overflow',))] fn test_decrease_allowance_from_zero_address() { let mut state = setup(); ERC20::decrease_allowance(ref state, SPENDER(), VALUE); @@ -496,7 +496,7 @@ fn test_decreaseAllowance() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('u256_sub Overflow', ))] +#[should_panic(expected: ('u256_sub Overflow',))] fn test_decreaseAllowance_to_zero_address() { let mut state = setup(); testing::set_caller_address(OWNER()); @@ -505,7 +505,7 @@ fn test_decreaseAllowance_to_zero_address() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('u256_sub Overflow', ))] +#[should_panic(expected: ('u256_sub Overflow',))] fn test_decreaseAllowance_from_zero_address() { let mut state = setup(); ERC20::decreaseAllowance(ref state, SPENDER(), VALUE); @@ -564,7 +564,7 @@ fn test__mint() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ERC20: mint to 0', ))] +#[should_panic(expected: ('ERC20: mint to 0',))] fn test__mint_to_zero() { let mut state = STATE(); InternalImpl::_mint(ref state, Zeroable::zero(), VALUE); @@ -587,7 +587,7 @@ fn test__burn() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ERC20: burn from 0', ))] +#[should_panic(expected: ('ERC20: burn from 0',))] fn test__burn_from_zero() { let mut state = setup(); InternalImpl::_burn(ref state, Zeroable::zero(), VALUE); diff --git a/src/tests/token/test_erc721.cairo b/src/tests/token/test_erc721.cairo index b39b24a01..1351e0385 100644 --- a/src/tests/token/test_erc721.cairo +++ b/src/tests/token/test_erc721.cairo @@ -1,5 +1,5 @@ -use ERC721::_owners::InternalContractStateTrait as OwnersTrait; -use ERC721::_token_approvals::InternalContractStateTrait as TokenApprovalsTrait; +use ERC721::_owners::InternalContractMemberStateTrait as OwnersTrait; +use ERC721::_token_approvals::InternalContractMemberStateTrait as TokenApprovalsTrait; use array::ArrayTrait; use integer::u256; @@ -138,7 +138,7 @@ fn test_balance_of() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid account', ))] +#[should_panic(expected: ('ERC721: invalid account',))] fn test_balance_of_zero() { ERC721Impl::balance_of(@STATE(), ZERO()); } @@ -152,14 +152,14 @@ fn test_owner_of() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID', ))] +#[should_panic(expected: ('ERC721: invalid token ID',))] fn test_owner_of_non_minted() { ERC721Impl::owner_of(@STATE(), u256_from_felt252(7)); } #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID', ))] +#[should_panic(expected: ('ERC721: invalid token ID',))] fn test_token_uri_non_minted() { ERC721MetadataImpl::token_uri(@STATE(), u256_from_felt252(7)); } @@ -178,7 +178,7 @@ fn test_get_approved() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID', ))] +#[should_panic(expected: ('ERC721: invalid token ID',))] fn test_get_approved_nonexistent() { ERC721Impl::get_approved(@STATE(), u256_from_felt252(7)); } @@ -242,7 +242,7 @@ fn test_approve_from_operator() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: unauthorized caller', ))] +#[should_panic(expected: ('ERC721: unauthorized caller',))] fn test_approve_from_unauthorized() { let mut state = setup(); @@ -252,7 +252,7 @@ fn test_approve_from_unauthorized() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: approval to owner', ))] +#[should_panic(expected: ('ERC721: approval to owner',))] fn test_approve_to_owner() { let mut state = setup(); @@ -262,7 +262,7 @@ fn test_approve_to_owner() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID', ))] +#[should_panic(expected: ('ERC721: invalid token ID',))] fn test_approve_nonexistent() { let mut state = STATE(); ERC721Impl::approve(ref state, SPENDER(), TOKEN_ID); @@ -282,7 +282,7 @@ fn test__approve() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: approval to owner', ))] +#[should_panic(expected: ('ERC721: approval to owner',))] fn test__approve_to_owner() { let mut state = setup(); InternalImpl::_approve(ref state, OWNER(), TOKEN_ID); @@ -290,7 +290,7 @@ fn test__approve_to_owner() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID', ))] +#[should_panic(expected: ('ERC721: invalid token ID',))] fn test__approve_nonexistent() { let mut state = STATE(); InternalImpl::_approve(ref state, SPENDER(), TOKEN_ID); @@ -327,7 +327,7 @@ fn test_set_approval_for_all() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: self approval', ))] +#[should_panic(expected: ('ERC721: self approval',))] fn test_set_approval_for_all_owner_equal_operator_true() { let mut state = STATE(); testing::set_caller_address(OWNER()); @@ -336,7 +336,7 @@ fn test_set_approval_for_all_owner_equal_operator_true() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: self approval', ))] +#[should_panic(expected: ('ERC721: self approval',))] fn test_set_approval_for_all_owner_equal_operator_false() { let mut state = STATE(); testing::set_caller_address(OWNER()); @@ -368,7 +368,7 @@ fn test__set_approval_for_all() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: self approval', ))] +#[should_panic(expected: ('ERC721: self approval',))] fn test__set_approval_for_all_owner_equal_operator_true() { let mut state = STATE(); InternalImpl::_set_approval_for_all(ref state, OWNER(), OWNER(), true); @@ -376,7 +376,7 @@ fn test__set_approval_for_all_owner_equal_operator_true() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: self approval', ))] +#[should_panic(expected: ('ERC721: self approval',))] fn test__set_approval_for_all_owner_equal_operator_false() { let mut state = STATE(); InternalImpl::_set_approval_for_all(ref state, OWNER(), OWNER(), false); @@ -430,7 +430,7 @@ fn test_transferFrom_owner() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID', ))] +#[should_panic(expected: ('ERC721: invalid token ID',))] fn test_transfer_from_nonexistent() { let mut state = STATE(); ERC721Impl::transfer_from(ref state, ZERO(), RECIPIENT(), TOKEN_ID); @@ -438,7 +438,7 @@ fn test_transfer_from_nonexistent() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID', ))] +#[should_panic(expected: ('ERC721: invalid token ID',))] fn test_transferFrom_nonexistent() { let mut state = STATE(); ERC721CamelOnlyImpl::transferFrom(ref state, ZERO(), RECIPIENT(), TOKEN_ID); @@ -446,7 +446,7 @@ fn test_transferFrom_nonexistent() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid receiver', ))] +#[should_panic(expected: ('ERC721: invalid receiver',))] fn test_transfer_from_to_zero() { let mut state = setup(); testing::set_caller_address(OWNER()); @@ -455,7 +455,7 @@ fn test_transfer_from_to_zero() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid receiver', ))] +#[should_panic(expected: ('ERC721: invalid receiver',))] fn test_transferFrom_to_zero() { let mut state = setup(); @@ -579,7 +579,7 @@ fn test_transferFrom_approved_for_all() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: unauthorized caller', ))] +#[should_panic(expected: ('ERC721: unauthorized caller',))] fn test_transfer_from_unauthorized() { let mut state = setup(); testing::set_caller_address(OTHER()); @@ -588,7 +588,7 @@ fn test_transfer_from_unauthorized() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: unauthorized caller', ))] +#[should_panic(expected: ('ERC721: unauthorized caller',))] fn test_transferFrom_unauthorized() { let mut state = setup(); testing::set_caller_address(OTHER()); @@ -737,7 +737,7 @@ fn test_safeTransferFrom_to_receiver_camel() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: safe transfer failed', ))] +#[should_panic(expected: ('ERC721: safe transfer failed',))] fn test_safe_transfer_from_to_receiver_failure() { let mut state = setup(); let receiver = setup_receiver(); @@ -750,7 +750,7 @@ fn test_safe_transfer_from_to_receiver_failure() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: safe transfer failed', ))] +#[should_panic(expected: ('ERC721: safe transfer failed',))] fn test_safeTransferFrom_to_receiver_failure() { let mut state = setup(); let receiver = setup_receiver(); @@ -763,7 +763,7 @@ fn test_safeTransferFrom_to_receiver_failure() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: safe transfer failed', ))] +#[should_panic(expected: ('ERC721: safe transfer failed',))] fn test_safe_transfer_from_to_receiver_failure_camel() { let mut state = setup(); let receiver = setup_camel_receiver(); @@ -776,7 +776,7 @@ fn test_safe_transfer_from_to_receiver_failure_camel() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: safe transfer failed', ))] +#[should_panic(expected: ('ERC721: safe transfer failed',))] fn test_safeTransferFrom_to_receiver_failure_camel() { let mut state = setup(); let receiver = setup_camel_receiver(); @@ -789,7 +789,7 @@ fn test_safeTransferFrom_to_receiver_failure_camel() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_safe_transfer_from_to_non_receiver() { let mut state = setup(); let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); @@ -802,7 +802,7 @@ fn test_safe_transfer_from_to_non_receiver() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_safeTransferFrom_to_non_receiver() { let mut state = setup(); let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); @@ -815,7 +815,7 @@ fn test_safeTransferFrom_to_non_receiver() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID', ))] +#[should_panic(expected: ('ERC721: invalid token ID',))] fn test_safe_transfer_from_nonexistent() { let mut state = STATE(); ERC721Impl::safe_transfer_from(ref state, ZERO(), RECIPIENT(), TOKEN_ID, DATA(true)); @@ -823,7 +823,7 @@ fn test_safe_transfer_from_nonexistent() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID', ))] +#[should_panic(expected: ('ERC721: invalid token ID',))] fn test_safeTransferFrom_nonexistent() { let mut state = STATE(); ERC721CamelOnlyImpl::safeTransferFrom(ref state, ZERO(), RECIPIENT(), TOKEN_ID, DATA(true)); @@ -831,7 +831,7 @@ fn test_safeTransferFrom_nonexistent() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid receiver', ))] +#[should_panic(expected: ('ERC721: invalid receiver',))] fn test_safe_transfer_from_to_zero() { let mut state = setup(); testing::set_caller_address(OWNER()); @@ -840,7 +840,7 @@ fn test_safe_transfer_from_to_zero() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid receiver', ))] +#[should_panic(expected: ('ERC721: invalid receiver',))] fn test_safeTransferFrom_to_zero() { let mut state = setup(); testing::set_caller_address(OWNER()); @@ -1101,7 +1101,7 @@ fn test_safeTransferFrom_approved_for_all_camel() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: unauthorized caller', ))] +#[should_panic(expected: ('ERC721: unauthorized caller',))] fn test_safe_transfer_from_unauthorized() { let mut state = setup(); testing::set_caller_address(OTHER()); @@ -1110,7 +1110,7 @@ fn test_safe_transfer_from_unauthorized() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: unauthorized caller', ))] +#[should_panic(expected: ('ERC721: unauthorized caller',))] fn test_safeTransferFrom_unauthorized() { let mut state = setup(); testing::set_caller_address(OTHER()); @@ -1139,7 +1139,7 @@ fn test__transfer() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID', ))] +#[should_panic(expected: ('ERC721: invalid token ID',))] fn test__transfer_nonexistent() { let mut state = STATE(); InternalImpl::_transfer(ref state, ZERO(), RECIPIENT(), TOKEN_ID); @@ -1147,7 +1147,7 @@ fn test__transfer_nonexistent() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid receiver', ))] +#[should_panic(expected: ('ERC721: invalid receiver',))] fn test__transfer_to_zero() { let mut state = setup(); InternalImpl::_transfer(ref state, OWNER(), ZERO(), TOKEN_ID); @@ -1155,7 +1155,7 @@ fn test__transfer_to_zero() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: wrong sender', ))] +#[should_panic(expected: ('ERC721: wrong sender',))] fn test__transfer_from_invalid_owner() { let mut state = setup(); InternalImpl::_transfer(ref state, RECIPIENT(), OWNER(), TOKEN_ID); @@ -1181,7 +1181,7 @@ fn test__mint() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid receiver', ))] +#[should_panic(expected: ('ERC721: invalid receiver',))] fn test__mint_to_zero() { let mut state = STATE(); InternalImpl::_mint(ref state, ZERO(), TOKEN_ID); @@ -1189,7 +1189,7 @@ fn test__mint_to_zero() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: token already minted', ))] +#[should_panic(expected: ('ERC721: token already minted',))] fn test__mint_already_exist() { let mut state = setup(); InternalImpl::_mint(ref state, RECIPIENT(), TOKEN_ID); @@ -1257,7 +1257,7 @@ fn test__safe_mint_to_account_camel() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test__safe_mint_to_non_receiver() { let mut state = STATE(); let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); @@ -1270,7 +1270,7 @@ fn test__safe_mint_to_non_receiver() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: safe mint failed', ))] +#[should_panic(expected: ('ERC721: safe mint failed',))] fn test__safe_mint_to_receiver_failure() { let mut state = STATE(); let recipient = setup_receiver(); @@ -1283,7 +1283,7 @@ fn test__safe_mint_to_receiver_failure() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: safe mint failed', ))] +#[should_panic(expected: ('ERC721: safe mint failed',))] fn test__safe_mint_to_receiver_failure_camel() { let mut state = STATE(); let recipient = setup_camel_receiver(); @@ -1296,7 +1296,7 @@ fn test__safe_mint_to_receiver_failure_camel() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid receiver', ))] +#[should_panic(expected: ('ERC721: invalid receiver',))] fn test__safe_mint_to_zero() { let mut state = STATE(); InternalImpl::_safe_mint(ref state, ZERO(), TOKEN_ID, DATA(true)); @@ -1304,7 +1304,7 @@ fn test__safe_mint_to_zero() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: token already minted', ))] +#[should_panic(expected: ('ERC721: token already minted',))] fn test__safe_mint_already_exist() { let mut state = setup(); InternalImpl::_safe_mint(ref state, RECIPIENT(), TOKEN_ID, DATA(true)); @@ -1336,7 +1336,7 @@ fn test__burn() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID', ))] +#[should_panic(expected: ('ERC721: invalid token ID',))] fn test__burn_nonexistent() { let mut state = STATE(); InternalImpl::_burn(ref state, TOKEN_ID); @@ -1358,7 +1358,7 @@ fn test__set_token_uri() { #[test] #[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID', ))] +#[should_panic(expected: ('ERC721: invalid token ID',))] fn test__set_token_uri_nonexistent() { let mut state = STATE(); InternalImpl::_set_token_uri(ref state, TOKEN_ID, URI); diff --git a/src/tests/upgrades/test_upgradeable.cairo b/src/tests/upgrades/test_upgradeable.cairo index 66de7a2a7..20b9c48c3 100644 --- a/src/tests/upgrades/test_upgradeable.cairo +++ b/src/tests/upgrades/test_upgradeable.cairo @@ -46,7 +46,7 @@ fn deploy_v1() -> IUpgradesV1Dispatcher { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('Class hash cannot be zero', 'ENTRYPOINT_FAILED', ))] +#[should_panic(expected: ('Class hash cannot be zero', 'ENTRYPOINT_FAILED',))] fn test_upgrade_with_class_hash_zero() { let v1 = deploy_v1(); v1.upgrade(CLASS_HASH_ZERO()); @@ -95,7 +95,7 @@ fn test_remove_selector_passes_in_v1() { #[test] #[available_gas(2000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', ))] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_remove_selector_fails_in_v2() { let v1 = deploy_v1(); v1.upgrade(V2_CLASS_HASH()); diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 32a265c06..70ae7cd39 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -351,12 +351,9 @@ mod ERC721 { fn _check_on_erc721_received( from: ContractAddress, to: ContractAddress, token_id: u256, data: Span ) -> bool { - if (DualCaseSRC5 { - contract_address: to - }.supports_interface(interface::IERC721_RECEIVER_ID)) { - DualCaseERC721Receiver { - contract_address: to - } + if (DualCaseSRC5 { contract_address: to } + .supports_interface(interface::IERC721_RECEIVER_ID)) { + DualCaseERC721Receiver { contract_address: to } .on_erc721_received( get_caller_address(), from, token_id, data ) == interface::IERC721_RECEIVER_ID diff --git a/src/upgrades/upgradeable.cairo b/src/upgrades/upgradeable.cairo index 444cf36b7..b10978d85 100644 --- a/src/upgrades/upgradeable.cairo +++ b/src/upgrades/upgradeable.cairo @@ -22,7 +22,7 @@ mod Upgradeable { impl InternalImpl of InternalState { fn _upgrade(ref self: ContractState, new_class_hash: ClassHash) { assert(!new_class_hash.is_zero(), 'Class hash cannot be zero'); - starknet::replace_class_syscall(new_class_hash).unwrap_syscall(); + starknet::replace_class_syscall(new_class_hash).unwrap(); self.emit(Upgraded { class_hash: new_class_hash }); } } From 7f4e462d05ab1e2fd01da47cd49d2f98907af7fb Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 25 Aug 2023 03:20:15 -0400 Subject: [PATCH 107/246] add erc721 api --- docs/modules/ROOT/nav.adoc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 3c84b7d03..4f68f041e 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -16,4 +16,7 @@ * xref:udc.adoc[Universal Deployer Contract] * xref:utilities.adoc[Utilities] -* xref:contracts::index.adoc[Contracts for Solidity] \ No newline at end of file +* xref:contracts::index.adoc[Contracts for Solidity] + +* API +** xref:/api/erc721.adoc[ERC721] From 249f4bfb8e0edf5d5f03788ec6990e71562b76e1 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 25 Aug 2023 03:20:42 -0400 Subject: [PATCH 108/246] add api page, start metadata --- docs/modules/ROOT/pages/api/erc721.adoc | 206 ++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 docs/modules/ROOT/pages/api/erc721.adoc diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc new file mode 100644 index 000000000..50e91381c --- /dev/null +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -0,0 +1,206 @@ +:github-icon: pass:[] +:eip721: https://eips.ethereum.org/EIPS/eip-721[EIP721] + += ERC721 + +Reference of interfaces, presets, and utilities related to ERC721 contracts. + +TIP: For an overview of ERC721, read our xref:erc721.adoc[ERC721 guide]. + +== Core + +[.contract] +[[IERC721]] +=== `++IERC721++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/cairo-2/src/token/erc721/interface.cairo#L13-L31[{github-icon},role=heading-link] + +[.hljs-theme-light.nopadding] +```javascript +use openzeppelin::token::erc721::interface::IERC721; +``` +Interface of the IERC721 standard as defined in {eip721}. + +[.contract-index] +.Functions +-- +* xref:#IERC721-balance_of[`++balance_of(account)++`] +* xref:#IERC721-owner_of[`++owner_of(token_id)++`] +* xref:#IERC721-transfer_from[`++transfer_from(from, to, token_id)++`] +* xref:#IERC721-safe_transfer_from[`++safe_transfer_from(from, to, token_id, data)++`] +* xref:#IERC721-approve[`++approve(to, token_id)++`] +* xref:#IERC721-set_approval_for_all[`++set_approval_for_all(operator, approved)++`] +* xref:#IERC721-get_approved[`++get_approved(token_id)++`] +* xref:#IERC721-is_approved_for_all[`++is_approved_for_all(owner, operator)++`] + +[.contract-subindex-inherited] +.ISRC5 + +* xref:#IERC721-supports_interface[`++supports_interface(interface_id)++`] +-- + +[.contract-index] +.Events +-- +* xref:#IERC721-Approval[`++Approval(owner, approved, token_id)++`] +* xref:#IERC721-ApprovalForAll[`++ApprovalForAll(owner, operator, approved)++`] +* xref:#IERC721-Transfer[`++Transfer(from, to, token_id)++`] +-- + +[.contract-item] +[[IERC721-balance_of]] +==== `[.contract-item-name]#++balance_of++#++(account: ContractAddress) → u256++` [.item-kind]#external# + +Returns the number of NFTs owned by `account`. + +[.contract-item] +[[IERC721-owner_of]] +==== `[.contract-item-name]#++owner_of++#++(token_id: u256) → ContractAddress++` [.item-kind]#external# + +Returns the owner address of `token_id`. + +This function panics if: + +- `token_id` does not exist. + +[.contract-item] +[[IERC721-transfer_from]] +==== `[.contract-item-name]#++transfer_from++#++(from: ContractAddress, to: ContractAddress, token_id: u256)++` [.item-kind]#external# + +Transfer ownership of `token_id` from `from` to `to`. + +Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 transfers or else they may be permanently lost. +Usage of <> prevents loss, though the caller must understand this adds an external call which potentially creates a reentrancy vulnerability. + +Emits a <> event. + +This function panics if: + +- Caller is neither approved nor the `token_id` owner. +- `to` is the zero address. +- `from` is not the token owner. +- `token_id` does not exist. + +[.contract-item] +[[IERC721-safe_transfer_from]] +==== `[.contract-item-name]#++safe_transfer_from++#++(from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#external# + +Safely transfer ownership of `token_id` from `from` to `to`, checking first +that `to` is aware of the ERC721 protocol to prevent tokens being locked +forever. For information regarding how contracts communicate their +awareness of the ERC721 protocol, see <>(TODO!). + +Emits a <> event. + +This function panics if: + +- Caller is neither approved nor the `token_id` owner. +- `to` is the zero address. +- `from` is not the token owner. +- `token_id` does not exist. +- `to` neither is an account contract nor supports the IERC721Receiver interface. + +[.contract-item] +[[IERC721-approve]] +==== `[.contract-item-name]#++approve++#++(to: ContractAddress, token_id: u256)++` [.item-kind]#external# + +Change or reaffirm the approved address for an NFT. + +Emits an <> event. + +This function panics if: + +- Caller is neither an approved operator nor the `token_id` owner. +- `to` is either the `owner` or the zero address. +- `token_id` does not exist. + +[.contract-item] +[[IERC721-set_approval_for_all]] +==== `[.contract-item-name]#++set_approval_for_all++#++(operator: ContractAddress, approved: bool)++` [.item-kind]#external# + +Enable or disable approval for `operator` to manage all of the caller's assets. + +Emits an <> event. + +This function panics if: + +- `owner` is the `operator`. + +[.contract-item] +[[IERC721-get_approved]] +==== `[.contract-item-name]#++get_approved++#++(token_id: u256) -> u256++` [.item-kind]#external# + +Returns the address approved for `token_id`. + +This function panics if: + +- `token_id` does not exist. + +[.contract-item] +[[IERC721-is_approved_for_all]] +==== `[.contract-item-name]#++is_approved_for_all++#++(owner: ContractAddress, operator: ContractAddress) -> bool++` [.item-kind]#external# + +Query if `operator` is an authorized operator for `owner`. + +[.contract-item] +[[IERC721-Approval]] +==== `[.contract-item-name]#++Approval++#++(owner: ContractAddress, approved: ContractAddress, token_id: u256)++` [.item-kind]#event# + +Emitted when `owner` enables `approved` to manage the `token_id` token. + +[.contract-item] +[[IERC721-ApprovalForAll]] +==== `[.contract-item-name]#++ApprovalForAll++#++(owner: ContractAddress, operator: ContractAddress, approved: bool)++` [.item-kind]#event# + +Emitted when `owner` enables `approved` to manage the `token_id` token. + +[.contract-item] +[[IERC721-Transfer]] +==== `[.contract-item-name]#++Transfer++#++(from: ContractAddress, to: ContractAddress, token_id: u256)++` [.item-kind]#event# + +Emitted when `token_id` token is transferred from `from` to `to`. + +[.contract] +[[IERC721Metadata]] +=== `++IERC721Metadata++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/cairo-2/src/token/erc721/interface.cairo#L54-L59[{github-icon},role=heading-link] + +[.hljs-theme-light.nopadding] +```javascript +use openzeppelin::token::erc721::interface::IERC721Metadata; +``` + +See {eip721}. + +[.contract-index] +.Functions +-- +* xref:#IERC721Metadata-name[`++name()++`] +* xref:#IERC721Metadata-owner_of[`++symbol()++`] +* xref:#IERC721Metadata-token_uri[`++token_uri(token_id)++`] + +[.contract-subindex-inherited] +.IERC721 + +* xref:#IERC721-balance_of[`++balance_of(account)++`] +* xref:#IERC721-owner_of[`++owner_of(token_id)++`] +* xref:#IERC721-transfer_from[`++transfer_from(from, to, token_id)++`] +* xref:#IERC721-safe_transfer_from[`++safe_transfer_from(from, to, token_id, data)++`] +* xref:#IERC721-approve[`++approve(to, token_id)++`] +* xref:#IERC721-set_approval_for_all[`++set_approval_for_all(operator, approved)++`] +* xref:#IERC721-get_approved[`++get_approved(token_id)++`] +* xref:#IERC721-is_approved_for_all[`++is_approved_for_all(owner, operator)++`] + +[.contract-subindex-inherited] +.ISRC5 + +* xref:#IERC721-supports_interface[`++supports_interface(interface_id)++`] +-- + +[.contract-index] +.Events +-- +[.contract-subindex-inherited] +.IERC721 + +* xref:#IERC721-Approval[`++Approval(owner, approved, token_id)++`] +* xref:#IERC721-ApprovalForAll[`++ApprovalForAll(owner, operator, approved)++`] +* xref:#IERC721-Transfer[`++Transfer(from, to, token_id)++`] +-- From 0fba70791cc38a6445ee15136078dda80218c3e8 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 27 Aug 2023 22:11:41 -0400 Subject: [PATCH 109/246] add erc721 api --- docs/modules/ROOT/pages/api/erc721.adoc | 100 ++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 50e91381c..295fdb37d 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -204,3 +204,103 @@ See {eip721}. * xref:#IERC721-ApprovalForAll[`++ApprovalForAll(owner, operator, approved)++`] * xref:#IERC721-Transfer[`++Transfer(from, to, token_id)++`] -- + +[.contract-item] +[[IERC721Metadata-name]] +==== `[.contract-item-name]#++name++#++() -> felt252++` [.item-kind]#external# + +Returns the NFT name. + +[.contract-item] +[[IERC721Metadata-symbol]] +==== `[.contract-item-name]#++symbol++#++() -> felt252++` [.item-kind]#external# + +Returns the NFT ticker symbol. + +[.contract-item] +[[IERC721Metadata-token_uri]] +==== `[.contract-item-name]#++token_uri++#++(token_id: u256) -> felt252++` [.item-kind]#external# + +Returns the Uniform Resource Identifier (URI) as a short string for the `token_id` token. +If the URI is not set for `token_id`, the return value will be `0`. + +[.contract] +[[ERC721]] +=== `++ERC721++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/cairo-2/src/token/erc721/erc721.cairo#L7[{github-icon},role=heading-link] + +[.hljs-theme-light.nopadding] +```javascript +use openzeppelin::token::erc721::ERC721; +``` + +Implementation of ERC721 which includes the IERC721Metadata extension as specified in https://eips.ethereum.org/EIPS/eip-721[EIP-721]. + +[.contract-index] +.Functions +-- +* xref:#ERC721-constructor[`++constructor(name, symbol)++`] + +[.contract-subindex-inherited] +.IERC721Metadata + +* xref:#IERC721Metadata-name[`++name()++`] +* xref:#IERC721Metadata-symbol[`++symbol()++`] +* xref:#IERC721Metadata-token_uri[`++token_uri(token_id)++`] + +[.contract-subindex-inherited] +.IERC721 + +* xref:#IERC721-balance_of[`++balance_of(account)++`] +* xref:#IERC721-owner_of[`++owner_of(token_id)++`] +* xref:#IERC721-transfer_from[`++transfer_from(from, to, token_id)++`] +* xref:#IERC721-safe_transfer_from[`++safe_transfer_from(from, to, token_id, data)++`] +* xref:#IERC721-approve[`++approve(to, token_id)++`] +* xref:#IERC721-set_approval_for_all[`++set_approval_for_all(operator, approved)++`] +* xref:#IERC721-get_approved[`++get_approved(token_id)++`] +* xref:#IERC721-is_approved_for_all[`++is_approved_for_all(owner, operator)++`] + +[.contract-subindex-inherited] +.ISRC5 + +* xref:#ISRC5-supports_interface[`++supports_interface(interface_id)++`] +-- + +[.contract-index] +.Events +-- +[.contract-subindex-inherited] +.IERC721 + +* xref:#IERC721-Approval[`++Approval(owner, approved, token_id)++`] +* xref:#IERC721-ApprovalForAll[`++ApprovalForAll(owner, operator, approved)++`] +* xref:#IERC721-Transfer[`++Transfer(from, to, token_id)++`] +-- + +[.contract-item] +[[ERC721-constructor]] +==== `[.contract-item-name]#++constructor++#++(name: felt252, symbol: felt252)++` [.item-kind]#constructor# + +Initializes the state of the ERC721 contract by setting the token name and symbol. +The constructor also registers the IERC721_ID and IERC721_METADATA_ID interface ids according to SRC-5. + +[.contract] +[[IERC721Receiver]] +=== `++IERC721Receiver++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/cairo-2/src/token/erc721/interface.cairo#L70-L79[{github-icon},role=heading-link] + +[.hljs-theme-light.nopadding] +```javascript +use openzeppelin::token::erc721::interface::IERC721Receiver; +``` + +[.contract-index] +.Functions +-- +* xref:#ERC721Receiver-on_erc721_received[`++on_erc721_received(operator, from, token_id, data)++`] +-- + +[.contract-item] +[[ERC721Receiver-on_erc721_received]] +==== `[.contract-item-name]#++on_erc721_received++#++(operator: ContractAddress, from: ContractAddress, token_id: u256, data Span)++` [.item-kind]#external# + +Whenever an IERC721 `token_id` token is transferred to this non-account contract via <> by `operator` from `from`, this function is called. + From d629a450636f962c756bda35a4353e00d6eef42d Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 27 Aug 2023 22:13:22 -0400 Subject: [PATCH 110/246] remove api, start doc refactor --- docs/modules/ROOT/pages/erc721.adoc | 809 ++-------------------------- 1 file changed, 33 insertions(+), 776 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 6e030d623..26b602ec1 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -6,94 +6,47 @@ The `erc721.cairo` contract implements an approximation of https://eips.ethereum == Table of Contents * <> -* <> +** <> * <> ** <> + ** <> ** <> - ** <> - *** <> ** <> - ** <> -* <> * <> - ** <> - ** <> - ** <> - *** <> +* <> ** <> - *** <> -* <> - ** <> -* <> - ** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - ** <> - *** <> - *** <> - *** <> - ** <> - *** <> - *** <> - *** <> - ** <> - *** <> - ** <> - *** <> - *** <<_owner_of,`_owner_of`>> - *** <<_exists,`_exists`>> - *** <<_is_approved_or_owner,`_is_approved_or_owner`>> - *** <<_approve,`_approve`>> - *** <<_set_approval_for_all,`_set_approval_for_all`>> - *** <<_mint,`_mint`>> - *** <<_transfer,`_transfer`>> - *** <<_burn,`_burn`>> - *** <<_safe_mint,`_safe_mint`>> - *** <<_safe_transfer,`_safe_transfer`>> - *** <<_set_token_uri,`_set_token_uri`>> == IERC721 [,javascript] ---- -#[starknet::interface] -trait IERC721 { - fn balance_of(self: @TState, account: ContractAddress) -> u256; - fn owner_of(self: @TState, token_id: u256) -> ContractAddress; +trait IERC721 { + fn balance_of(account: ContractAddress) -> u256; + fn owner_of(token_id: u256) -> ContractAddress; fn transfer_from( - ref self: TState, from: ContractAddress, to: ContractAddress, token_id: u256 ); fn safe_transfer_from( - ref self: TState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span ); - fn approve(ref self: TState, to: ContractAddress, token_id: u256); + fn approve(to: ContractAddress, token_id: u256); fn set_approval_for_all( - ref self: TState, operator: ContractAddress, approved: bool ); - fn get_approved(self: @TState, token_id: u256) -> ContractAddress; + fn get_approved(token_id: u256) -> ContractAddress; fn is_approved_for_all( - self: @TState, owner: ContractAddress, operator: ContractAddress + owner: ContractAddress, operator: ContractAddress ) -> bool; } -#[starknet::interface] -trait ISRC5 { - fn supports_interface(self: @TState, interface_id: felt252) -> bool; +trait ISRC5 { + fn supports_interface(interface_id: felt252) -> bool; } ---- @@ -128,8 +81,6 @@ In doing so, recipient contracts (both accounts and non-accounts) can be verifie == Usage -TODO - === Token Transfers This library includes `transfer_from` and `safe_transfer_from` to transfer NFTs. @@ -143,16 +94,7 @@ The `safe_transfer_from` method incorporates the following conditional logic: The current implementation of `safe_transfer_from` checks for `on_erc721_received` and requires that the recipient contract supports SRC5 and exposes the `supports_interface` method. See <>. -=== Interpreting ERC721 URIs - -Token URIs in Cairo are stored as single field elements. -Each field element equates to 252-bits (or 31.5 bytes) which means that a token's URI can be no longer than 31 characters. - -NOTE: Storing the URI as an array of felts was considered to accommodate larger strings. -While this approach is more flexible regarding URIs, a returned array further deviates from the standard set in https://eips.ethereum.org/EIPS/eip-721[EIP721]. -Therefore, this library's ERC721 implementation sets URIs as a single field element. - -=== ERC721Received +=== Receiving tokens In order to be sure a contract can safely accept ERC721 tokens, said contract must implement the `IERC721Receiver` interface (as expressed in the EIP721 specification). Methods such as `safe_transfer_from` and `_safe_mint` call the recipient contract's `on_erc721_received` method. @@ -169,14 +111,12 @@ Otherwise, the safe transfer will fail. ==== IERC721Receiver -Interface for any contract that wants to support safe transfers from ERC721 asset contracts. +The IERC721Receiver interface must be implemented in any non-account contract that wants to support safe transfers from ERC721 asset contracts. [,javascript] ---- -#[starknet::interface] -trait IERC721Receiver { +trait IERC721Receiver { fn on_erc721_received( - self: @TState, operator: ContractAddress, from: ContractAddress, token_id: u256, @@ -185,723 +125,40 @@ trait IERC721Receiver { } ---- -=== Supporting Interfaces - -TODO +=== Interpreting ERC721 URIs -=== Ready-to-Use Presets +Token URIs in Cairo are stored as single field elements. +Each field element equates to 252-bits (or 31.5 bytes) which means that a token's URI can be no longer than 31 characters. -ERC721 presets have been created to allow for quick deployments as-is whic are a great option for testing and prototyping. -See <>. +NOTE: Storing the URI as an array of felts was considered to accommodate larger strings. +While this approach is more flexible regarding URIs, a returned array further deviates from the standard set in https://eips.ethereum.org/EIPS/eip-721[EIP721]. +Therefore, this library's ERC721 implementation sets URIs as a single field element. -== Extensibility +=== Supporting Interfaces TODO == Presets -TODO - -=== ERC721Metadata - -The `ERC721Metadata` extension allows your smart contract to be interrogated for its name and for details about the assets which your NFTs represent. - -We follow OpenZeppelin's Solidity approach of integrating the Metadata methods `name`, `symbol`, and `token_uri` (`tokenURI` in Solidity) into all ERC721 implementations. -If preferred, a contract can be created that does not import the Metadata methods from the `ERC721` library. -Note that the `IERC721Metadata` interface id should be removed from the constructor as well. - -==== IERC721Metadata - -[,javascript] ----- -#[starknet::interface] -trait IERC721Metadata { - fn name(self: @TState) -> felt252; - fn symbol(self: @TState) -> felt252; - fn token_uri(self: @TState, token_id: u256) -> felt252; -} ----- - -== API Specification - -=== IERC721 API - -[,javascript] ----- -// SRC5 id: 0x33eb2f84c309543403fd69f0d0f363781ef06ef6faeb0131ff16ea3175bd943 - -fn balance_of(self: @TState, account: ContractAddress) -> u256; -fn owner_of(self: @TState, token_id: u256) -> ContractAddress; -fn transfer_from( - ref self: TState, - from: ContractAddress, - to: ContractAddress, - token_id: u256 -); -fn safe_transfer_from( - ref self: TState, - from: ContractAddress, - to: ContractAddress, - token_id: u256, - data: Span -); -fn approve(ref self: TState, to: ContractAddress, token_id: u256); -fn set_approval_for_all( - ref self: TState, operator: ContractAddress, approved: bool -); -fn get_approved(self: @TState, token_id: u256) -> ContractAddress; -fn is_approved_for_all( - self: @TState, owner: ContractAddress, operator: ContractAddress -) -> bool; ----- - -[.contract-item#balance_of] -==== `[.contract-item-name]#++balance_of++#++(self: @TState, account: ContractAddress) → u256++` - -Returns the number of NFTs owned by `account`. - -===== Arguments - -- `*account*` -+ -The account balance to query. - -===== Returns - -- Token balance of `account`. - -[.contract-item#owner_of] -==== `[.contract-item-name]#++owner_of++#++(self: @TState, token_id: u256) → ContractAddress++` - -Returns the owner address of `token_id`. - -This function panics if: - -- `token_id` does not exist. - -===== Arguments - -- `*token_id*` -+ -The token to query. - -===== Returns - -- Owner address of `token_id`. - -[.contract-item#transfer_from] -==== `[.contract-item-name]#++transfer_from++#++(self: @TState, from: ContractAddress, to: ContractAddress, token_id: u256)++` - -Transfer ownership of `token_id` from `from` to `to`. - -Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 transfers or else they may be permanently lost. -Usage of <> prevents loss, though the caller must understand this adds an external call which potentially creates a reentrancy vulnerability. - -Emits a <> event. - -This function panics if: - -- Caller is neither approved nor the `token_id` owner. -- `to` is the zero address. -- `from` is not the token owner. -- `token_id` does not exist. - -===== Arguments - -- `*from*` -+ -The current owner of the NFT. -- `*to*` -+ -The new owner. -- `*token_id*` -+ -The NFT to transfer. - -[.contract-item#safe_transfer_from] -==== `[.contract-item-name]#++safe_transfer_from++#++(self: @TState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` - -Safely transfer ownership of `token_id` from `from` to `to`, checking first -that `to` is aware of the ERC721 protocol to prevent tokens being locked -forever. For information regarding how contracts communicate their -awareness of the ERC721 protocol, see <>(TODO!). - -Emits a <> event. - -This function panics if: - -- Caller is neither approved nor the `token_id` owner. -- `to` is the zero address. -- `from` is not the token owner. -- `token_id` does not exist. -- `to` neither is an account contract nor supports the IERC721Receiver interface. - -===== Arguments - -- `*from*` -+ -The current owner of the NFT. -- `*to*` -+ -The new owner. -- `*token_id*` -+ -The NFT to transfer. -- `*data*` -+ -Additional data with no specified format, sent in call to `to`. - -[.contract-item#approve] -==== `[.contract-item-name]#++approve++#++(self: @TState, to: ContractAddress, token_id: u256)++` - -Change or reaffirm the approved address for an NFT. - -Emits an <> event. - -This function panics if: - -- Caller is neither an approved operator nor the `token_id` owner. -- `to` is either the `owner` or the zero address. -- `token_id` does not exist. - -===== Arguments - -- `*to*` -+ -The new approved NFT controller. -- `*token_id*` -+ -The NFT to approve. - -[.contract-item#set_approval_for_all] -==== `[.contract-item-name]#++set_approval_for_all++#++(self: @TState, operator: ContractAddress, approved: bool)++` - -Enable or disable approval for `operator` to manage all of the caller's assets. - -Emits an <> event. - -This function panics if: - -- `owner` is the `operator`. - -===== Arguments - -- `*operator*` -+ -Address to add to the set of authorized operators. -- `*approved*` -+ -`true` if `operator` is approved, `false` to revoke approval. - -[.contract-item#get_approved] -==== `[.contract-item-name]#++get_approved++#++(self: @TState, token_id: u256) -> ContractAddress++` - -Returns the address approved for `token_id`. - -This function panics if: - -- `token_id` does not exist. - -===== Arguments - -- `*token_id*` -+ -The token ID to query. - -===== Returns - -- Approved address for the `token_id` NFT, or `0` if there is none. - -[.contract-item#is_approved_for_all] -==== `[.contract-item-name]#++is_approved_for_all++#++(self: @TState, owner: ContractAddress, operator: ContractAddress) -> bool++` - -Query if `operator` is an authorized operator for `owner`. - -===== Arguments - -- `*owner*` -+ -The address that owns the NFT. -- `*operator*` -+ -The address that acts on behalf of the `owner`. - -===== Returns - -- `true` if `operator` is an authorized operator for `owner`. - -=== Events - -[,javascript] ----- -#[derive(Drop, starknet::Event)] -struct Approval { - owner: ContractAddress, - approved: ContractAddress, - token_id: u256 -} - -#[derive(Drop, starknet::Event)] -struct ApprovalForAll { - owner: ContractAddress, - operator: ContractAddress, - approved: bool -} - -#[derive(Drop, starknet::Event)] -struct Transfer { - from: ContractAddress, - to: ContractAddress, - token_id: u256 -} ----- - -[.contract-item#Approval] -==== `[.contract-item-name]#++Approval++#++(owner: ContractAddress, approved: ContractAddress, token_id: u256)++` - -Emitted when `owner` enables `approved` to manage the `token_id` token. - -===== Arguments - -- `*owner*` -+ -The owner of the NFT. -- `*approved*` -+ -The new approved NFT controller. -- `*token_id*` -+ -The NFT to approve. - -[.contract-item#ApprovalForAll] - -[.contract-item] -==== `[.contract-item-name]#++ApprovalForAll++#++(owner: ContractAddress, operator: ContractAddress, approved: bool)++` - -Emitted when `owner` enables or disables (approved) `operator` to manage all of its assets. - -===== Arguments - -- `*owner*` -+ -The owner of the NFT. -- `*operator*` -+ -Address to add to the set of authorized operators. -- `*approved*` -+ -`true` if the `operator` is approved, `false` to revoke approval. - -[.contract-item#Transfer] - -[.contract-item] -==== `[.contract-item-name]#++Transfer++#++(from: ContractAddress, to: ContractAddress, token_id: u256)++` - -Emitted when `token_id` token is transferred from `from` to `to`. - -===== Arguments - -- `*from*` -+ -The current owner of the NFT. -- `*to*` -+ -The new owner of the NFT. -- `*token_id*` -+ -The NFT to transfer. - -=== IERC721Metadata API - -[,javascript] ----- -// SRC5 id: 0x6069a70848f907fa57668ba1875164eb4dcee693952468581406d131081bbd - -#[starknet::interface] -trait IERC721Metadata { - fn name(self: @TState) -> felt252; - fn symbol(self: @TState) -> felt252; - fn token_uri(self: @TState, token_id: u256) -> felt252; -} ----- - -[.contract-item#name] -==== `[.contract-item-name]#++name++#++(self: @TState)++` - -Returns the NFT name. - -===== Returns - -- The NFT name. - -[.contract-item#symbol] -==== `[.contract-item-name]#++symbol++#++(self: @TState)++` - -Returns the NFT ticker symbol. - -===== Returns - -- The NFT symbol. - -[.contract-item#token_uri] -==== `[.contract-item-name]#++token_uri++#++(self: @TState, token_id: u256)++` - -Returns the Uniform Resource Identifier (URI) for the `token_id` token. -If the URI is not set for the `token_id`, the return value will be `0`. - -===== Arguments - -- `*token_id*` -+ -The NFT symbol. +ERC721 presets have been created to allow for quick deployments as-is whic are a great option for testing and prototyping. -===== Returns +== Extensions -- The URI of `token_id`. +ERC721 includes the optional <> extension as well as other forthcoming extensions. -=== IERC721Receiver API +=== ERC721Metadata [,javascript] ---- -// SRC5 id: 0x3a0dff5f70d80458ad14ae37bb182a728e3c8cdda0402a5daa86620bdf910bc - -#[starknet::interface] -trait IERC721Receiver { - fn on_erc721_received( - self: @TState, - operator: ContractAddress, - from: ContractAddress, - token_id: u256, - data: Span - ) -> felt252; +trait IERC721Metadata { + fn name() -> felt252; + fn symbol() -> felt252; + fn token_uri(token_id: u256) -> felt252; } ---- -[.contract-item#on_erc721_received] -==== `[.contract-item-name]#++on_erc721_received++#++(self: @TState, operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span) -> felt252++` - -Whenever an IERC721 `token_id` token is transferred to this non-account contract through `safe_transfer_from`, this function is called. -This function may reject the transfer. -If this function returns anything other than the IERC721_RECEIVER_ID, the transaction must be reverted. - -===== Arguments - -- `*operator*` -+ -The address which called `safe_transfer_from` function. -- `*from*` -+ -The address which previously owned the token. -- `*token_id*` -+ -The NFT identifier which is being transferred. -- `*data*` -+ -Additional data with no specified format. - -Returns: - -- `*felt252*` -+ -The IERC721Receiver magic value _0x3a0dff5f70d80458ad14ae37bb182a728e3c8cdda0402a5daa86620bdf910bc_. - -=== InternalImpl - -[,javascript] ----- -fn initializer(ref self: TState, name_: felt252, symbol_: felt252); -fn _owner_of(self: @TState, token_id: u256) -> ContractAddress; -fn _exists(self: @TState, token_id: u256) -> bool; -fn _is_approved_or_owner( - self: @TState, - spender: ContractAddress, - token_id: u256 -) -> bool; -fn _approve(ref self: TState, to: ContractAddress, token_id: u256); -fn _set_approval_for_all( - ref self: TState, - owner: ContractAddress, - operator: ContractAddress, - approved: bool -); -fn _mint(ref self: TState, to: ContractAddress, token_id: u256); -fn _transfer( - ref self: TState, - from: ContractAddress, - to: ContractAddress, - token_id: u256 -); -fn _burn(ref self: TState, token_id: u256); -fn _safe_mint( - ref self: TState, - to: ContractAddress, - token_id: u256, - data: Span -); -fn _safe_transfer( - ref self: TState, - from: ContractAddress, - to: ContractAddress, - token_id: u256, - data: Span -); -fn _set_token_uri(ref self: TState, token_id: u256, token_uri: felt252); ----- - -[.contract-item#initializer] -==== `[.contract-item-name]#++initializer++#++(ref self: TState, name_: felt252, symbol_: felt252)++` - -Initializes the contract by setting the token name and symbol. -This should be used inside the contract's constructor. - -===== Arguments - -- `*name_*` -+ -The token name. -- `*symbol_*` -+ -The token symbol. - -[.contract-item#_owner_of] -==== `[.contract-item-name]#++_owner_of++#++(self: @TState, token_id: u256) -> ContractAddress++` - -Internal function that returns the owner address of `token_id`. -This function will panic if the token does not exist. - -===== Arguments - -- `*token_id*` -+ -The token to query. - -===== Returns - -- The owner address of `token_id`. - -[.contract-item#_exists] -==== `[.contract-item-name]#++_exists++#++(self: @TState, token_id: u256) -> bool++` - -Internal function that returns whether `token_id` exists. - -Tokens start existing when they are minted (<<_mint,_mint>>), and stop existing when they are burned (<<_burn,_burn>>). - -===== Arguments - -- `*token_id*` -+ -The token to query. - -===== Returns - -- `true` if the token exists, `false` otherwise. - -[.contract-item#_is_approved_or_owner] -==== `[.contract-item-name]#++_is_approved_or_owner++#++(self: @TState, spender: ContractAddress, token_id: u256) -> bool++` - -Internal function that returns whether `spender` is allowed to manage `token_id`. - -This function panics if: - -- `token_id` does not exist. - -===== Arguments - -- `*spender*` -+ -The target address to query. -- `*token_id*` -+ -The token to query. - -===== Returns - -- `true` if the `spender` is either the owner or approved, `false` otherwise. - -[.contract-item#_approve] -==== `[.contract-item-name]#++_approve++#++(ref self: TState, to: ContractAddress, token_id: u256)++` - -Internal function that changes or reaffirms the approved address for an NFT. - -This function panics if: - -- `token_id` does not exist. -- `to` is the current token owner. - -Emits an <> event. - -===== Arguments - -- `*to*` -+ -The new approved NFT controller. -- `*token_id*` -+ -The NFT to approve. - -[.contract-item#_set_approval_for_all] -==== `[.contract-item-name]#++_set_approval_for_all++#++(ref self: TState, owner: ContractAddress, operator: ContractAddress, approved: bool)++` - -Internal function that enables or disables approval for `operator` to manage all of the -`owner` assets. - -Emits an <> event. - -This function panics if: - --`owner` is the `operator`. - -===== Arguments - -- `*owner*` -+ -The current owner of the NFT. -- `*operator*` -+ -Address to add to the set of authorized operators. -- `*approved*` -+ -`true` if `operator` is approved, `false` to revoke approval. - -[.contract-item#_mint] -==== `[.contract-item-name]#++_mint++#++(ref self: TState, to: ContractAddress, token_id: u256)++` - -Internal function that mints `token_id` and transfers it to `to`. - -WARNING: Usage of this method is discouraged, use <<_safe_mint,_safe_mint>> whenever possible. - -Emits an <> event. - -This function panics if: - -- `to` is the zero address. -- `token_id` already exists. - -===== Arguments - -- `*to*` -+ -The new owner of the NFT. -- `*token_id*` -+ -The newly created NFT to transfer. - -[.contract-item#_transfer] -==== `[.contract-item-name]#++_transfer++#++(ref self: TState, from: ContractAddress, to: ContractAddress, token_id: u256)++` - -Internal function that transfers `token_id` from `from` to `to`. - -Emits an <> event. - -This function panics if: - -- `to` is the zero address. -- `from` is not the token owner. -- `token_id` does not exist. - -===== Arguments - -- `*from*` -+ -The current NFT owner. -- `*to*` -+ -The new owner. -- `*token_id*` -+ -The NFT to transfer. - -[.contract-item#_burn] -==== `[.contract-item-name]#++_burn++#++(ref self: TState, token_id: u256)++` - -Internal function that destroys `token_id`. -The approval is cleared when the token is burned. -This internal function does not check if the sender is authorized to operate on the token. - -Emits an <> event. - -This function panics if: - -- `token_id` does not exist. - -===== Arguments - -- `*token_id*` -+ -The NFT to burn. - -[.contract-item#_safe_mint] -==== `[.contract-item-name]#++_safe_mint++#++(ref self: TState, to: ContractAddress, token_id: u256, data: Span)++` - -Internal function that safely mints `token_id` and transfers it to `to`. -If `to` is not an account contract, `to` must support IERC721Receiver; otherwise, the transaction will fail. - -Emits an <> event. - -This function panics if: - -- `token_id` does not exist. -- `to` neither is an account contract nor supports the IERC721Receiver interface. - -===== Arguments - -- `*to*` -+ -The new owner. -- `*token_id*` -+ -The newly created NFT to transfer. -- `*data*` -+ -Additional data with no specified format, sent in call to `to`. - -[.contract-item#_safe_transfer] -==== `[.contract-item-name]#++_safe_transfer++#++(ref self: TState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` - -Internal function that safely transfers `token_id` token from `from` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. - -`data` is additional data, it has no specified format and it is sent in call to `to`. - -This function is equivalent to `safe_transfer_from`, and can be used to e.g. implement alternative mechanisms to perform signature-based token transfers. - -Emits an <> event. - -This function panics if: - -- `to` is the zero address. -- `from` is not the token owner. -- `token_id` does not exist. -- `to` neither is an account contract nor supports the IERC721Receiver interface. - -===== Arguments - -- `*from*` -+ -The current NFT owner. -- `*to*` -+ -The new owner. -- `*token_id*` -+ -The NFT to transfer. -- `*data*` -+ -Additional data with no specified format, sent in call to `to`. - -[.contract-item#_set_token_uri] -==== `[.contract-item-name]#++_set_token_uri++#++(ref self: TState, token_id: u256, token_uri: felt252)++` - -Sets the `token_uri` of `token_id`. - -This function panics if: - -- `token_id` does not exist. - -===== Arguments +The `ERC721Metadata` extension allows your smart contract to be interrogated for its name and for details about the assets which your NFTs represent. -- `*token_id*` -+ -The target NFT. -- `*token_uri*` -+ -The new token URI for `token_id`. +We follow OpenZeppelin's Solidity approach of integrating the Metadata methods `name`, `symbol`, and `token_uri` (`tokenURI` in Solidity) into all ERC721 implementations. +If preferred, a contract can be created that does not import the Metadata methods from the `ERC721` library. +Note that the `IERC721Metadata` interface id should be removed from the constructor as well. From 25884517ca3db3f20bca439a26ec0404abf771eb Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 28 Aug 2023 00:06:20 -0400 Subject: [PATCH 111/246] fix links --- docs/modules/ROOT/pages/erc721.adoc | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 26b602ec1..8859d625e 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -1,7 +1,13 @@ = ERC721 -The ERC721 token standard is a specification for https://docs.openzeppelin.com/contracts/4.x/tokens#different-kinds-of-tokens[non-fungible tokens], or more colloquially: NFTs. -The `erc721.cairo` contract implements an approximation of https://eips.ethereum.org/EIPS/eip-721[EIP-721] in Cairo for StarkNet. +:token-types: https://docs.openzeppelin.com/contracts/4.x/tokens#different-kinds-of-tokens[non-fungible tokens] +:eip721: https://eips.ethereum.org/EIPS/eip-721[EIP-721] +:erc721-api: xref:/api/erc721.adoc[API Reference] + +The ERC721 token standard is a specification for {token-types}, or more colloquially: NFTs. +The `erc721.cairo` contract implements an approximation of {eip721} in Cairo for StarkNet. + +TIP: For detailed information on the usage and implementation check the {erc721-api} section. == Table of Contents @@ -52,6 +58,8 @@ trait ISRC5 { === ERC721 Compatibility +:erc165-storage: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v0.6.1/contracts/utils/introspection/ERC165Storage.sol[ERC165Storage] + Although StarkNet is not EVM compatible, this implementation aims to be as close as possible to the ERC721 standard in the following ways: * It uses Cairo's `u256` instead of `felt252`. @@ -75,7 +83,7 @@ If `data` is not used, simply pass an empty array. In Solidity, this means a dynamically-sized array. To be as close as possible to the standard, it accepts a dynamic array of felts. * `SRC5.register_interface` allows contracts to set and communicate which interfaces they support. -This is similar to OpenZeppelin's https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v0.6.1/contracts/utils/introspection/ERC165Storage.sol[ERC165Storage]. +This is similar to OpenZeppelin's {erc165-storage}. * `IERC721Receiver` compliant contracts return a hardcoded selector id according to Starknet selectors (as opposed to selector calculation in Solidity). In doing so, recipient contracts (both accounts and non-accounts) can be verified that they support ERC721 transfers. @@ -96,14 +104,17 @@ See <>. === Receiving tokens +:erc165-discussion: https://github.com/OpenZeppelin/cairo-contracts/discussions/100[this discussion] +:src-6: https://community.starknet.io/t/snip-starknet-standard-account/95665[SRC-6 in Starknet Shamans] + In order to be sure a contract can safely accept ERC721 tokens, said contract must implement the `IERC721Receiver` interface (as expressed in the EIP721 specification). Methods such as `safe_transfer_from` and `_safe_mint` call the recipient contract's `on_erc721_received` method. If the contract fails to return the correct magic value, the transaction fails. -Starknet contracts that support safe transfers, however, must also support xref:introspection.adoc#src5[SRC5] and include `supports_interface` as proposed (originally as ERC165) in https://github.com/OpenZeppelin/cairo-contracts/discussions/100[#100]. +Starknet contracts that support safe transfers, however, must also support xref:introspection.adoc#src5[SRC5] and include `supports_interface` as proposed (originally as ERC165) in {erc165-discussion}. `safe_transfer_from` requires a means of differentiating between account and non-account contracts. Account contracts must support the Starknet standard account interface in order to communicate the contract's ability to receive safe NFT transfers. -The standard account interface is drafted and defined as https://community.starknet.io/t/snip-starknet-standard-account/95665[SRC-6 in Starknet Shamans]. +The standard account interface is drafted and defined as {src-6}. `on_erc721_received` will call `supports_interface` with the SRC6 magic value _0x2ceccef7f994940b3962a6c67e0ba4fcd37df7d131417c604f91e03caecc1cd_ on the recipient address. SRC6-compliant account contracts will return `true` thus communicating that the recipient is an account contract. Non-account contracts, however, _must_ register support for ERC721 safe transfers. From 05429e4fd34a250ce7a01450190c53275e5c1c0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Mon, 28 Aug 2023 12:48:00 -0300 Subject: [PATCH 112/246] bump antora (#715) --- docs/antora.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/antora.yml b/docs/antora.yml index f75680009..ceea1e622 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -1,5 +1,5 @@ name: contracts-cairo title: Contracts for Cairo -version: 0.6.1 +version: 0.7.0 nav: - modules/ROOT/nav.adoc From adac09f7943bc9c13e4fba5963162ecb53e7268b Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Fri, 8 Sep 2023 16:36:49 -0400 Subject: [PATCH 113/246] Add selector inline macro/fix `tokenURI` (#724) * update selectors * use inline selector * remove selectors file * remove import * fix camel tokenURI * fix formatting * re-add selectors mod * import from selectors mod * fix formatting * import selector --- src/tests/mocks/camel721_mock.cairo | 4 +- src/tests/mocks/erc721_panic_mock.cairo | 2 +- src/tests/token/test_dual721.cairo | 4 +- src/token/erc721/dual721.cairo | 2 +- src/token/erc721/erc721.cairo | 2 +- src/token/erc721/interface.cairo | 2 +- src/utils/selectors.cairo | 108 +++++++++++------------- 7 files changed, 57 insertions(+), 67 deletions(-) diff --git a/src/tests/mocks/camel721_mock.cairo b/src/tests/mocks/camel721_mock.cairo index 9fcbbb248..2117697d8 100644 --- a/src/tests/mocks/camel721_mock.cairo +++ b/src/tests/mocks/camel721_mock.cairo @@ -38,9 +38,9 @@ mod CamelERC721Mock { } #[external(v0)] - fn tokenUri(self: @ContractState, tokenId: u256) -> felt252 { + fn tokenURI(self: @ContractState, tokenId: u256) -> felt252 { let unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721::ERC721MetadataCamelOnlyImpl::tokenUri(@unsafe_state, tokenId) + ERC721::ERC721MetadataCamelOnlyImpl::tokenURI(@unsafe_state, tokenId) } #[external(v0)] diff --git a/src/tests/mocks/erc721_panic_mock.cairo b/src/tests/mocks/erc721_panic_mock.cairo index 6d172c379..ace550d33 100644 --- a/src/tests/mocks/erc721_panic_mock.cairo +++ b/src/tests/mocks/erc721_panic_mock.cairo @@ -107,7 +107,7 @@ mod CamelERC721PanicMock { } #[external(v0)] - fn tokenUri(self: @ContractState, tokenId: u256) -> felt252 { + fn tokenURI(self: @ContractState, tokenId: u256) -> felt252 { panic_with_felt252('Some error'); 3 } diff --git a/src/tests/token/test_dual721.cairo b/src/tests/token/test_dual721.cairo index 6c1744258..fc65cc426 100644 --- a/src/tests/token/test_dual721.cairo +++ b/src/tests/token/test_dual721.cairo @@ -525,7 +525,7 @@ fn test_dual_isApprovedForAll_exists_and_panics() { #[test] #[available_gas(2000000)] -fn test_dual_tokenUri() { +fn test_dual_tokenURI() { let (dispatcher, target) = setup_camel(); assert(dispatcher.token_uri(TOKEN_ID) == URI, 'Should return URI'); } @@ -533,7 +533,7 @@ fn test_dual_tokenUri() { #[test] #[available_gas(2000000)] #[should_panic(expected: ('Some error', 'ENTRYPOINT_FAILED',))] -fn test_dual_tokenUri_exists_and_panics() { +fn test_dual_tokenURI_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); dispatcher.token_uri(TOKEN_ID); } diff --git a/src/token/erc721/dual721.cairo b/src/token/erc721/dual721.cairo index 88cc35abd..041f267a2 100644 --- a/src/token/erc721/dual721.cairo +++ b/src/token/erc721/dual721.cairo @@ -56,7 +56,7 @@ impl DualCaseERC721Impl of DualCaseERC721Trait { args.append_serde(token_id); try_selector_with_fallback( - *self.contract_address, selectors::token_uri, selectors::tokenUri, args.span() + *self.contract_address, selectors::token_uri, selectors::tokenURI, args.span() ) .unwrap_and_cast() } diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 70ae7cd39..0abffb4d1 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -110,7 +110,7 @@ mod ERC721 { #[external(v0)] impl ERC721MetadataCamelOnlyImpl of interface::IERC721MetadataCamelOnly { - fn tokenUri(self: @ContractState, tokenId: u256) -> felt252 { + fn tokenURI(self: @ContractState, tokenId: u256) -> felt252 { assert(self._exists(tokenId), 'ERC721: invalid token ID'); self._token_uri.read(tokenId) } diff --git a/src/token/erc721/interface.cairo b/src/token/erc721/interface.cairo index 4aff670b8..86125721d 100644 --- a/src/token/erc721/interface.cairo +++ b/src/token/erc721/interface.cairo @@ -60,7 +60,7 @@ trait IERC721Metadata { #[starknet::interface] trait IERC721MetadataCamelOnly { - fn tokenUri(self: @TState, tokenId: u256) -> felt252; + fn tokenURI(self: @TState, tokenId: u256) -> felt252; } // diff --git a/src/utils/selectors.cairo b/src/utils/selectors.cairo index 1695e17cb..505a35374 100644 --- a/src/utils/selectors.cairo +++ b/src/utils/selectors.cairo @@ -5,64 +5,57 @@ // AccessControl // -const get_role_admin: felt252 = 0x302e0454f48778e0ca3a2e714a289c4e8d8e03d614b370130abb1a524a47f22; -const getRoleAdmin: felt252 = 0x2034da88f3e3f3382ab0faf97fe5f74fe34d551ddff7fda974d756bf12130a0; -const grant_role: felt252 = 0x18a2f881894a5eb15a2a00f598839abaa75bd7f1fea1a37e42779d7fbcd9cf8; -const grantRole: felt252 = 0x37322ff1aabefe50aec25a14eb84b168b7be4f2d66fbbdb5dd8135e8234c37a; -const has_role: felt252 = 0x30559321b47d576b645ed7bd24089943dd5fd3a359ecdd6fa8f05c1bab67d6b; -const hasRole: felt252 = 0x35ed3407ba17dc741dd3af821fa1548619ebcdc87c95bcea9e3bc510951fae8; -const renounce_role: felt252 = 0xd80093a4ee6a9e649f2ae3c64963d5096948d50cf4ea055500aa03a342fd43; -const renounceRole: felt252 = 0x3c4022816cd5119ac7938fd7a982062e4cacd4777b4eda6e6a8f64d9e6833; -const revoke_role: felt252 = 0x246116ed358bad337e64a4df51cb57a40929189494ad5905a39872c489136ec; -const revokeRole: felt252 = 0xa7ef1739dec1e216a0ba2987650983a3104c707ad0831a30184a3b1382dd7d; +const get_role_admin: felt252 = selector!("get_role_admin"); +const getRoleAdmin: felt252 = selector!("getRoleAdmin"); +const grant_role: felt252 = selector!("grant_role"); +const grantRole: felt252 = selector!("grantRole"); +const has_role: felt252 = selector!("has_role"); +const hasRole: felt252 = selector!("hasRole"); +const renounce_role: felt252 = selector!("renounce_role"); +const renounceRole: felt252 = selector!("renounceRole"); +const revoke_role: felt252 = selector!("revoke_role"); +const revokeRole: felt252 = selector!("revokeRole"); // // Ownable // -const owner: felt252 = 0x2016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0; -const transfer_ownership: felt252 = - 0x2a3bb1eaa05b77c4b0eeee0116a3177c6d62319dd7149ae148185d9e09de74a; -const transferOwnership: felt252 = - 0x14a390f291e2e1f29874769efdef47ddad94d76f77ff516fad206a385e8995f; -const renounce_ownership: felt252 = 0x52580a92c73f4428f1a260c5d768ef462b25955307de00f99957df119865d; -const renounceOwnership: felt252 = 0xd5d33d590e6660853069b37a2aea67c6fdaa0268626bc760350b590490feb5; +const owner: felt252 = selector!("owner"); +const transfer_ownership: felt252 = selector!("transfer_ownership"); +const transferOwnership: felt252 = selector!("transferOwnership"); +const renounce_ownership: felt252 = selector!("renounce_ownership"); +const renounceOwnership: felt252 = selector!("renounceOwnership"); // // ERC721 // -const name: felt252 = 0x361458367e696363fbcc70777d07ebbd2394e89fd0adcaf147faccd1d294d60; -const symbol: felt252 = 0x216b05c387bab9ac31918a3e61672f4618601f3c598a2f3f2710f37053e1ea4; -const token_uri: felt252 = 0x226ad7e84c1fe08eb4c525ed93cccadf9517670341304571e66f7c4f95cbe54; -const tokenUri: felt252 = 0x362dec5b8b67ab667ad08e83a2c3ba1db7fdb4ab8dc3a33c057c4fddec8d3de; -const balance_of: felt252 = 0x35a73cd311a05d46deda634c5ee045db92f811b4e74bca4437fcb5302b7af33; -const balanceOf: felt252 = 0x2e4263afad30923c891518314c3c95dbe830a16874e8abc5777a9a20b54c76e; -const owner_of: felt252 = 0x3552df12bdc6089cf963c40c4cf56fbfd4bd14680c244d1c5494c2790f1ea5c; -const ownerOf: felt252 = 0x2962ba17806af798afa6eaf4aa8c93a9fb60a3e305045b6eea33435086cae9; -const get_approved: felt252 = 0x309065f1424d76d4a4ace2ff671391d59536e0297409434908d38673290a749; -const getApproved: felt252 = 0xb180e2fe9f14914416216da76338ac0beb980443725c802af615f8431fdb1e; -const is_approved_for_all: felt252 = - 0x2aa3ea196f9b8a4f65613b67fcf185e69d8faa9601a3382871d15b3060e30dd; -const isApprovedForAll: felt252 = 0x21cdf9aedfed41bc4485ae779fda471feca12075d9127a0fc70ac6b3b3d9c30; -const approve: felt252 = 0x219209e083275171774dab1df80982e9df2096516f06319c5c6d71ae0a8480c; -const set_approval_for_all: felt252 = - 0xd86ca3d41635e20c180181046b11abcf19e1bdef3dcaa4c180300ccca1813f; -const setApprovalForAll: felt252 = - 0x2d4c8ea4c8fb9f571d1f6f9b7692fff8e5ceaf73b1df98e7da8c1109b39ae9a; -const transfer_from: felt252 = 0x3704ffe8fba161be0e994951751a5033b1462b918ff785c0a636be718dfdb68; -const transferFrom: felt252 = 0x41b033f4a31df8067c24d1e9b550a2ce75fd4a29e1147af9752174f0e6cb20; -const safe_transfer_from: felt252 = - 0x16f0218b33b5cf273196787d7cf139a9ad13d58e6674dcdce722b3bf8389863; -const safeTransferFrom: felt252 = 0x19d59d013d4aa1a8b1ce4c8299086f070733b453c02d0dc46e735edc04d6444; +const name: felt252 = selector!("name"); +const symbol: felt252 = selector!("symbol"); +const token_uri: felt252 = selector!("token_uri"); +const tokenURI: felt252 = selector!("tokenURI"); +const balance_of: felt252 = selector!("balance_of"); +const balanceOf: felt252 = selector!("balanceOf"); +const owner_of: felt252 = selector!("owner_of"); +const ownerOf: felt252 = selector!("ownerOf"); +const get_approved: felt252 = selector!("get_approved"); +const getApproved: felt252 = selector!("getApproved"); +const is_approved_for_all: felt252 = selector!("is_approved_for_all"); +const isApprovedForAll: felt252 = selector!("isApprovedForAll"); +const approve: felt252 = selector!("approve"); +const set_approval_for_all: felt252 = selector!("set_approval_for_all"); +const setApprovalForAll: felt252 = selector!("setApprovalForAll"); +const transfer_from: felt252 = selector!("transfer_from"); +const transferFrom: felt252 = selector!("transferFrom"); +const safe_transfer_from: felt252 = selector!("safe_transfer_from"); +const safeTransferFrom: felt252 = selector!("safeTransferFrom"); // // ERC721Receiver // -const on_erc721_received: felt252 = - 0x38c7ee9f0855dfe219aea022b141d9b2ec0f6b68395d221c3f331c7ca4fb608; -const onERC721Received: felt252 = 0xfa119a8fafc6f1a02deb36fe5efbcc4929ef2021e50cf1cb6d1a780ccd009b; +const on_erc721_received: felt252 = selector!("on_erc721_received"); +const onERC721Received: felt252 = selector!("onERC721Received"); // // ERC20 @@ -70,24 +63,21 @@ const onERC721Received: felt252 = 0xfa119a8fafc6f1a02deb36fe5efbcc4929ef2021e50c // The following ERC20 selectors are already defined in ERC721 above: // name, symbol, balance_of, balanceOf, transfer_from, transferFrom, approve -const decimals: felt252 = 0x4c4fb1ab068f6039d5780c68dd0fa2f8742cceb3426d19667778ca7f3518a9; -const total_supply: felt252 = 0x1557182e4359a1f0c6301278e8f5b35a776ab58d39892581e357578fb287836; -const totalSupply: felt252 = 0x80aa9fdbfaf9615e4afc7f5f722e265daca5ccc655360fa5ccacf9c267936d; -const allowance: felt252 = 0x1e888a1026b19c8c0b57c72d63ed1737106aa10034105b980ba117bd0c29fe1; -const transfer: felt252 = 0x83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e; +const decimals: felt252 = selector!("decimals"); +const total_supply: felt252 = selector!("total_supply"); +const totalSupply: felt252 = selector!("totalSupply"); +const allowance: felt252 = selector!("allowance"); +const transfer: felt252 = selector!("transfer"); // // Account // -const set_public_key: felt252 = 0x2e3e21ff5952b2531241e37999d9c4c8b3034cccc89a202a6bf019bdf5294f9; -const setPublicKey: felt252 = 0xbc0eb87884ab91e330445c3584a50d7ddf4b568f02fbeb456a6242cce3f5d9; -const get_public_key: felt252 = 0x1a35984e05126dbecb7c3bb9929e7dd9106d460c59b1633739a5c733a5fb13b; -const getPublicKey: felt252 = 0x1a6c6a0bdec86cc645c91997d8eea83e87148659e3e61122f72361fd5e94079; -const is_valid_signature: felt252 = - 0x28420862938116cb3bbdbedee07451ccc54d4e9412dbef71142ad1980a30941; -const isValidSignature: felt252 = 0x213dfe25e2ca309c4d615a09cfc95fdb2fc7dc73fbcad12c450fe93b1f2ff9e; -const supports_interface: felt252 = - 0xfe80f537b66d12a00b6d3c072b44afbb716e78dde5c3f0ef116ee93d3e3283; -const supportsInterface: felt252 = - 0x29e211664c0b63c79638fbea474206ca74016b3e9a3dc4f9ac300ffd8bdf2cd; +const set_public_key: felt252 = selector!("set_public_key"); +const setPublicKey: felt252 = selector!("setPublicKey"); +const get_public_key: felt252 = selector!("get_public_key"); +const getPublicKey: felt252 = selector!("getPublicKey"); +const is_valid_signature: felt252 = selector!("is_valid_signature"); +const isValidSignature: felt252 = selector!("isValidSignature"); +const supports_interface: felt252 = selector!("supports_interface"); +const supportsInterface: felt252 = selector!("supportsInterface"); From 00fa582373d8b08487e7635556e3f86ce7a7c28f Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 8 Sep 2023 18:25:12 -0400 Subject: [PATCH 114/246] nest erc721 api --- docs/antora.yml | 3 +++ docs/modules/ROOT/nav.adoc | 4 +--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/antora.yml b/docs/antora.yml index f75680009..71b849a89 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -3,3 +3,6 @@ title: Contracts for Cairo version: 0.6.1 nav: - modules/ROOT/nav.adoc +asciidoc: + attributes: + page-sidebar-collapse-default: true diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 4f68f041e..811c39764 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -9,6 +9,7 @@ * Tokens ** xref:erc20.adoc[ERC20] ** xref:erc721.adoc[ERC721] +*** xref:/api/erc721.adoc[API Reference] ** xref:erc1155.adoc[ERC1155] * xref:security.adoc[Security] @@ -17,6 +18,3 @@ * xref:utilities.adoc[Utilities] * xref:contracts::index.adoc[Contracts for Solidity] - -* API -** xref:/api/erc721.adoc[ERC721] From 2f0b9047bc122f31a529113cc0c3df531ebd32b1 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 9 Sep 2023 21:53:51 -0400 Subject: [PATCH 115/246] add InternalImpl to api --- docs/modules/ROOT/pages/api/erc721.adoc | 190 ++++++++++++++++++++++-- 1 file changed, 176 insertions(+), 14 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 295fdb37d..677f78723 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -238,31 +238,47 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi [.contract-index] .Functions -- -* xref:#ERC721-constructor[`++constructor(name, symbol)++`] +* xref:#ERC721-constructor[`++constructor(self, name, symbol)++`] [.contract-subindex-inherited] .IERC721Metadata -* xref:#IERC721Metadata-name[`++name()++`] -* xref:#IERC721Metadata-symbol[`++symbol()++`] -* xref:#IERC721Metadata-token_uri[`++token_uri(token_id)++`] +* xref:#IERC721Metadata-name[`++name(self)++`] +* xref:#IERC721Metadata-symbol[`++symbol(self)++`] +* xref:#IERC721Metadata-token_uri[`++token_uri(self, token_id)++`] [.contract-subindex-inherited] .IERC721 -* xref:#IERC721-balance_of[`++balance_of(account)++`] -* xref:#IERC721-owner_of[`++owner_of(token_id)++`] -* xref:#IERC721-transfer_from[`++transfer_from(from, to, token_id)++`] -* xref:#IERC721-safe_transfer_from[`++safe_transfer_from(from, to, token_id, data)++`] -* xref:#IERC721-approve[`++approve(to, token_id)++`] -* xref:#IERC721-set_approval_for_all[`++set_approval_for_all(operator, approved)++`] -* xref:#IERC721-get_approved[`++get_approved(token_id)++`] -* xref:#IERC721-is_approved_for_all[`++is_approved_for_all(owner, operator)++`] +* xref:#IERC721-balance_of[`++balance_of(self, account)++`] +* xref:#IERC721-owner_of[`++owner_of(self, token_id)++`] +* xref:#IERC721-transfer_from[`++transfer_from(self, from, to, token_id)++`] +* xref:#IERC721-safe_transfer_from[`++safe_transfer_from(self, from, to, token_id, data)++`] +* xref:#IERC721-approve[`++approve(self, to, token_id)++`] +* xref:#IERC721-set_approval_for_all[`++set_approval_for_all(self, operator, approved)++`] +* xref:#IERC721-get_approved[`++get_approved(self, token_id)++`] +* xref:#IERC721-is_approved_for_all[`++is_approved_for_all(self, owner, operator)++`] [.contract-subindex-inherited] .ISRC5 -* xref:#ISRC5-supports_interface[`++supports_interface(interface_id)++`] +* xref:#ISRC5-supports_interface[`++supports_interface(self, interface_id)++`] + +[.contract-subindex-inherited] +.InternalImpl + +* xref:#ERC721-initializer[`++initializer(self, name_, symbol_)++`] +* xref:#ERC721-_owner_of[`++_owner_of(self, token_id)++`] +* xref:#ERC721-_exists[`++_exists(self, token_id)++`] +* xref:#ERC721-_is_approved_or_owner[`++_is_approved_or_owner(self, spender, token_id)++`] +* xref:#ERC721-_approve[`++_approve(self, to, token_id)++`] +* xref:#ERC721-_set_approval_for_all[`++_set_approval_for_all(self, owner, operator, approved)++`] +* xref:#ERC721-_mint[`++_mint(self, to, token_id)++`] +* xref:#ERC721-_transfer[`++_transfer(self, from, to, token_id)++`] +* xref:#ERC721-_burn[`++_burn(self, token_id)++`] +* xref:#ERC721-_safe_mint[`++_safe_mint(self, to, token_id, data)++`] +* xref:#ERC721-_safe_transfer[`++_safe_transfer(self, from, to, token_id, data)++`] +* xref:#ERC721-_set_token_uri[`++_set_token_uri(self, token_id, token_uri)++`] -- [.contract-index] @@ -278,11 +294,157 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi [.contract-item] [[ERC721-constructor]] -==== `[.contract-item-name]#++constructor++#++(name: felt252, symbol: felt252)++` [.item-kind]#constructor# +==== `[.contract-item-name]#++constructor++#++(ref self: ContractState, name: felt252, symbol: felt252)++` [.item-kind]#constructor# Initializes the state of the ERC721 contract by setting the token name and symbol. The constructor also registers the IERC721_ID and IERC721_METADATA_ID interface ids according to SRC-5. +[.contract-item] +[[ERC721-initializer]] +==== `[.contract-item-name]#++initializer++#++(ref self: ContractState, name_: felt252, symbol_: felt252)++` [.item-kind]#internal# + +Initializes the contract by setting the token name and symbol. +This should be used inside the contract's constructor. + +[.contract-item] +[[ERC721-_owner_of]] +==== `[.contract-item-name]#++_owner_of++#++(self: @ContractState, token_id: felt252) -> ContractAddress++` [.item-kind]#internal# + +Internal function that returns the owner address of `token_id`. +This function will panic if the token does not exist. + +[.contract-item] +[[ERC721-_exists]] +==== `[.contract-item-name]#++_exists++#++(self: @ContractState, token_id: u256) -> bool++` [.item-kind]#internal# + +Internal function that returns whether `token_id` exists. + +Tokens start existing when they are minted (<>), and stop existing when they are burned (<>). + +[.contract-item] +[[ERC721-_is_approved_or_owner]] +==== `[.contract-item-name]#++_is_approved_or_owner++#++(ref self: ContractState, spender: ContractAddress, token_id: u256) -> bool++` [.item-kind]#internal# + +Internal function that returns whether `spender` is allowed to manage `token_id`. + +This function panics if: + +- `token_id` does not exist. + +[.contract-item] +[[ERC721-_approve]] +==== `[.contract-item-name]#++_approve++#++(ref self: ContractState, to: ContractAddress, token_id: u256)++` [.item-kind]#internal# + +Internal function that changes or reaffirms the approved address for an NFT. + +Emits an <> event. + +This function panics if: + +- `token_id` does not exist. +- `to` is the current token owner. + +[.contract-item] +[[ERC721-_set_approval_for_all]] +==== `[.contract-item-name]#++_set_approval_for_all++#++(ref self: ContractState, owner: ContractAddress, operator: ContractAddress, approved: bool)++` [.item-kind]#internal# + +Internal function that enables or disables approval for `operator` to manage all of the +`owner` assets. + +Emits an <> event. + +This function panics if: + +-`owner` is the `operator`. + +[.contract-item] +[[ERC721-_mint]] +==== `[.contract-item-name]#++_mint++#++(ref self: ContractState, to: ContractAddress, token_id: u256)++` [.item-kind]#internal# + +WARNING: Usage of this method is discouraged, use <> whenever possible. + +Internal function that mints `token_id` and transfers it to `to`. + +Emits an <> event. + +This function panics if: + +- `to` is the zero address. +- `token_id` already exists. + +[.contract-item] +[[ERC721-_transfer]] +==== `[.contract-item-name]#++_transfer++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256)++` [.item-kind]#internal# + +Internal function that transfers `token_id` from `from` to `to`. + +Emits an <> event. + +This function panics if: + +- `to` is the zero address. +- `from` is not the token owner. +- `token_id` does not exist. + +[.contract-item] +[[ERC721-_burn]] +==== `[.contract-item-name]#++_burn++#++(ref self: ContractState, token_id: u256)++` [.item-kind]#internal# + +Internal function that destroys `token_id`. +The approval is cleared when the token is burned. +This internal function does not check if the sender is authorized to operate on the token. + +Emits an <> event. + +This function panics if: + +- `token_id` does not exist. + +[.contract-item] +[[ERC721-_safe_mint]] +==== `[.contract-item-name]#++_safe_mint++#++(ref self: ContractState, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#internal# + +Internal function that safely mints `token_id` and transfers it to `to`. +If `to` is not an account contract, `to` must support IERC721Receiver; otherwise, the transaction will fail. + +Emits an <> event. + +This function panics if: + +- `token_id` does not exist. +- `to` neither is an account contract nor supports the IERC721Receiver interface. + +[.contract-item] +[[ERC721-_safe_transfer]] +==== `[.contract-item-name]#++_safe_transfer++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#internal# + +Internal function that safely transfers `token_id` token from `from` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. + +`data` is additional data, it has no specified format and it is sent in call to `to`. + +This function is equivalent to `safe_transfer_from`, and can be used to e.g. implement alternative mechanisms to perform signature-based token transfers. + +Emits an <> event. + +This function panics if: + +- `to` is the zero address. +- `from` is not the token owner. +- `token_id` does not exist. +- `to` neither is an account contract nor supports the IERC721Receiver interface. + +* xref:#ERC721-_set_token_uri[`++_set_token_uri(self, token_id, token_uri)++`] + +[.contract-item] +[[ERC721-_set_token_uri]] +==== `[.contract-item-name]#++_set_token_uri++#++(ref self: ContractState, token_id: u256, token_uri: felt252)++` [.item-kind]#internal# + +Sets the `token_uri` of `token_id`. + +This function panics if: + +- `token_id` does not exist. + [.contract] [[IERC721Receiver]] === `++IERC721Receiver++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/cairo-2/src/token/erc721/interface.cairo#L70-L79[{github-icon},role=heading-link] From 7bcae474a1cb0f11b26d508400f3b00cb771ac93 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 10 Sep 2023 16:15:19 -0400 Subject: [PATCH 116/246] fix link --- docs/modules/ROOT/pages/api/erc721.adoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 677f78723..7d6f9b816 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -1,5 +1,6 @@ :github-icon: pass:[] :eip721: https://eips.ethereum.org/EIPS/eip-721[EIP721] +:erc721-receiver: xref:/erc721.adoc#receiving_tokens[ERC721Receiver] = ERC721 @@ -86,7 +87,7 @@ This function panics if: Safely transfer ownership of `token_id` from `from` to `to`, checking first that `to` is aware of the ERC721 protocol to prevent tokens being locked forever. For information regarding how contracts communicate their -awareness of the ERC721 protocol, see <>(TODO!). +awareness of the ERC721 protocol, see {erc721-receiver}. Emits a <> event. From ca8052dc11669b543debfb2bc05c14a4b3927123 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 10 Sep 2023 16:32:38 -0400 Subject: [PATCH 117/246] fix links, add function headers --- docs/modules/ROOT/pages/api/erc721.adoc | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 7d6f9b816..55d37e3f3 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -46,6 +46,8 @@ Interface of the IERC721 standard as defined in {eip721}. * xref:#IERC721-Transfer[`++Transfer(from, to, token_id)++`] -- +===== Functions + [.contract-item] [[IERC721-balance_of]] ==== `[.contract-item-name]#++balance_of++#++(account: ContractAddress) → u256++` [.item-kind]#external# @@ -141,6 +143,8 @@ This function panics if: Query if `operator` is an authorized operator for `owner`. +==== Events + [.contract-item] [[IERC721-Approval]] ==== `[.contract-item-name]#++Approval++#++(owner: ContractAddress, approved: ContractAddress, token_id: u256)++` [.item-kind]#event# @@ -206,6 +210,8 @@ See {eip721}. * xref:#IERC721-Transfer[`++Transfer(from, to, token_id)++`] -- +==== Functions + [.contract-item] [[IERC721Metadata-name]] ==== `[.contract-item-name]#++name++#++() -> felt252++` [.item-kind]#external# @@ -293,6 +299,8 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi * xref:#IERC721-Transfer[`++Transfer(from, to, token_id)++`] -- +==== Functions + [.contract-item] [[ERC721-constructor]] ==== `[.contract-item-name]#++constructor++#++(ref self: ContractState, name: felt252, symbol: felt252)++` [.item-kind]#constructor# @@ -406,18 +414,18 @@ This function panics if: ==== `[.contract-item-name]#++_safe_mint++#++(ref self: ContractState, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#internal# Internal function that safely mints `token_id` and transfers it to `to`. -If `to` is not an account contract, `to` must support IERC721Receiver; otherwise, the transaction will fail. +If `to` is not an account contract, `to` must support <>; otherwise, the transaction will fail. Emits an <> event. This function panics if: - `token_id` does not exist. -- `to` neither is an account contract nor supports the IERC721Receiver interface. +- `to` neither is an account contract nor supports the <> interface. [.contract-item] [[ERC721-_safe_transfer]] -==== `[.contract-item-name]#++_safe_transfer++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#internal# +==== `[.contract-item-name]#++_safe_mint++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#internal# Internal function that safely transfers `token_id` token from `from` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. @@ -432,9 +440,7 @@ This function panics if: - `to` is the zero address. - `from` is not the token owner. - `token_id` does not exist. -- `to` neither is an account contract nor supports the IERC721Receiver interface. - -* xref:#ERC721-_set_token_uri[`++_set_token_uri(self, token_id, token_uri)++`] +- `to` neither is an account contract nor supports the <> interface. [.contract-item] [[ERC721-_set_token_uri]] @@ -458,11 +464,13 @@ use openzeppelin::token::erc721::interface::IERC721Receiver; [.contract-index] .Functions -- -* xref:#ERC721Receiver-on_erc721_received[`++on_erc721_received(operator, from, token_id, data)++`] +* xref:#IERC721Receiver-on_erc721_received[`++on_erc721_received(operator, from, token_id, data)++`] -- +==== Functions + [.contract-item] -[[ERC721Receiver-on_erc721_received]] +[[IERC721Receiver-on_erc721_received]] ==== `[.contract-item-name]#++on_erc721_received++#++(operator: ContractAddress, from: ContractAddress, token_id: u256, data Span)++` [.item-kind]#external# Whenever an IERC721 `token_id` token is transferred to this non-account contract via <> by `operator` from `from`, this function is called. From 89c8536456594bbb645cd5e295eb37c0ef2de489 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 10 Sep 2023 17:16:39 -0400 Subject: [PATCH 118/246] fix links, tidy up --- docs/modules/ROOT/pages/api/erc721.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 55d37e3f3..6f39f3260 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -306,7 +306,7 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi ==== `[.contract-item-name]#++constructor++#++(ref self: ContractState, name: felt252, symbol: felt252)++` [.item-kind]#constructor# Initializes the state of the ERC721 contract by setting the token name and symbol. -The constructor also registers the IERC721_ID and IERC721_METADATA_ID interface ids according to SRC-5. +The constructor also registers the IERC721 and IERC721_METADATA interface ids according to SRC-5. [.contract-item] [[ERC721-initializer]] @@ -425,13 +425,13 @@ This function panics if: [.contract-item] [[ERC721-_safe_transfer]] -==== `[.contract-item-name]#++_safe_mint++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#internal# +==== `[.contract-item-name]#++_safe_transfer++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#internal# Internal function that safely transfers `token_id` token from `from` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. `data` is additional data, it has no specified format and it is sent in call to `to`. -This function is equivalent to `safe_transfer_from`, and can be used to e.g. implement alternative mechanisms to perform signature-based token transfers. +This function is equivalent to <>, and can be used to e.g. implement alternative mechanisms to perform signature-based token transfers. Emits an <> event. From 5fde8b109aa5d1437925848e32a09b63d51d8279 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 11 Sep 2023 10:37:58 -0400 Subject: [PATCH 119/246] fix link --- docs/modules/ROOT/pages/erc721.adoc | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 8859d625e..21f83dfa7 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -17,7 +17,6 @@ TIP: For detailed information on the usage and implementation check the {erc721- ** <> ** <> ** <> - ** <> * <> * <> ** <> @@ -106,12 +105,13 @@ See <>. :erc165-discussion: https://github.com/OpenZeppelin/cairo-contracts/discussions/100[this discussion] :src-6: https://community.starknet.io/t/snip-starknet-standard-account/95665[SRC-6 in Starknet Shamans] +:src-5: xref:introspection.adoc#src5[SRC-5] In order to be sure a contract can safely accept ERC721 tokens, said contract must implement the `IERC721Receiver` interface (as expressed in the EIP721 specification). Methods such as `safe_transfer_from` and `_safe_mint` call the recipient contract's `on_erc721_received` method. If the contract fails to return the correct magic value, the transaction fails. -Starknet contracts that support safe transfers, however, must also support xref:introspection.adoc#src5[SRC5] and include `supports_interface` as proposed (originally as ERC165) in {erc165-discussion}. +Starknet contracts that support safe transfers, however, must also support {src-5} and include `supports_interface` as proposed (originally as ERC165) in {erc165-discussion}. `safe_transfer_from` requires a means of differentiating between account and non-account contracts. Account contracts must support the Starknet standard account interface in order to communicate the contract's ability to receive safe NFT transfers. The standard account interface is drafted and defined as {src-6}. @@ -145,10 +145,6 @@ NOTE: Storing the URI as an array of felts was considered to accommodate larger While this approach is more flexible regarding URIs, a returned array further deviates from the standard set in https://eips.ethereum.org/EIPS/eip-721[EIP721]. Therefore, this library's ERC721 implementation sets URIs as a single field element. -=== Supporting Interfaces - -TODO - == Presets ERC721 presets have been created to allow for quick deployments as-is whic are a great option for testing and prototyping. From 90be39f764863864b49f30763eb22f78ed137746 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 14 Sep 2023 07:05:11 +0200 Subject: [PATCH 120/246] fix: naming convention (#732) --- src/tests/token/test_dual20.cairo | 23 +++++++++-------- src/token/erc20/dual20.cairo | 42 +++++++++++++++---------------- 2 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/tests/token/test_dual20.cairo b/src/tests/token/test_dual20.cairo index 08eaf82c7..aa0bd2306 100644 --- a/src/tests/token/test_dual20.cairo +++ b/src/tests/token/test_dual20.cairo @@ -5,8 +5,8 @@ use openzeppelin::tests::mocks::erc20_panic::SnakeERC20Panic; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::mocks::snake20_mock::SnakeERC20Mock; use openzeppelin::tests::utils; -use openzeppelin::token::erc20::dual20::DualERC20; -use openzeppelin::token::erc20::dual20::DualERC20Trait; +use openzeppelin::token::erc20::dual20::DualCaseERC20; +use openzeppelin::token::erc20::dual20::DualCaseERC20Trait; use openzeppelin::token::erc20::interface::IERC20CamelDispatcher; use openzeppelin::token::erc20::interface::IERC20CamelDispatcherTrait; use openzeppelin::token::erc20::interface::IERC20Dispatcher; @@ -43,36 +43,39 @@ fn OPERATOR() -> ContractAddress { // Setup // -fn setup_snake() -> (DualERC20, IERC20Dispatcher) { +fn setup_snake() -> (DualCaseERC20, IERC20Dispatcher) { let mut calldata = array![]; calldata.append_serde(NAME); calldata.append_serde(SYMBOL); calldata.append_serde(SUPPLY); calldata.append_serde(OWNER()); let target = utils::deploy(SnakeERC20Mock::TEST_CLASS_HASH, calldata); - (DualERC20 { contract_address: target }, IERC20Dispatcher { contract_address: target }) + (DualCaseERC20 { contract_address: target }, IERC20Dispatcher { contract_address: target }) } -fn setup_camel() -> (DualERC20, IERC20CamelDispatcher) { +fn setup_camel() -> (DualCaseERC20, IERC20CamelDispatcher) { let mut calldata = array![]; calldata.append_serde(NAME); calldata.append_serde(SYMBOL); calldata.append_serde(SUPPLY); calldata.append_serde(OWNER()); let target = utils::deploy(CamelERC20Mock::TEST_CLASS_HASH, calldata); - (DualERC20 { contract_address: target }, IERC20CamelDispatcher { contract_address: target }) + (DualCaseERC20 { contract_address: target }, IERC20CamelDispatcher { contract_address: target }) } -fn setup_non_erc20() -> DualERC20 { +fn setup_non_erc20() -> DualCaseERC20 { let calldata = array![]; let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); - DualERC20 { contract_address: target } + DualCaseERC20 { contract_address: target } } -fn setup_erc20_panic() -> (DualERC20, DualERC20) { +fn setup_erc20_panic() -> (DualCaseERC20, DualCaseERC20) { let snake_target = utils::deploy(SnakeERC20Panic::TEST_CLASS_HASH, array![]); let camel_target = utils::deploy(CamelERC20Panic::TEST_CLASS_HASH, array![]); - (DualERC20 { contract_address: snake_target }, DualERC20 { contract_address: camel_target }) + ( + DualCaseERC20 { contract_address: snake_target }, + DualCaseERC20 { contract_address: camel_target } + ) } // diff --git a/src/token/erc20/dual20.cairo b/src/token/erc20/dual20.cairo index 28897c6f5..dfb54584b 100644 --- a/src/token/erc20/dual20.cairo +++ b/src/token/erc20/dual20.cairo @@ -11,44 +11,44 @@ use starknet::SyscallResultTrait; use starknet::call_contract_syscall; #[derive(Copy, Drop)] -struct DualERC20 { +struct DualCaseERC20 { contract_address: ContractAddress } -trait DualERC20Trait { - fn name(self: @DualERC20) -> felt252; - fn symbol(self: @DualERC20) -> felt252; - fn decimals(self: @DualERC20) -> u8; - fn total_supply(self: @DualERC20) -> u256; - fn balance_of(self: @DualERC20, account: ContractAddress) -> u256; - fn allowance(self: @DualERC20, owner: ContractAddress, spender: ContractAddress) -> u256; - fn transfer(self: @DualERC20, recipient: ContractAddress, amount: u256) -> bool; +trait DualCaseERC20Trait { + fn name(self: @DualCaseERC20) -> felt252; + fn symbol(self: @DualCaseERC20) -> felt252; + fn decimals(self: @DualCaseERC20) -> u8; + fn total_supply(self: @DualCaseERC20) -> u256; + fn balance_of(self: @DualCaseERC20, account: ContractAddress) -> u256; + fn allowance(self: @DualCaseERC20, owner: ContractAddress, spender: ContractAddress) -> u256; + fn transfer(self: @DualCaseERC20, recipient: ContractAddress, amount: u256) -> bool; fn transfer_from( - self: @DualERC20, sender: ContractAddress, recipient: ContractAddress, amount: u256 + self: @DualCaseERC20, sender: ContractAddress, recipient: ContractAddress, amount: u256 ) -> bool; - fn approve(self: @DualERC20, spender: ContractAddress, amount: u256) -> bool; + fn approve(self: @DualCaseERC20, spender: ContractAddress, amount: u256) -> bool; } -impl DualERC20Impl of DualERC20Trait { - fn name(self: @DualERC20) -> felt252 { +impl DualCaseERC20Impl of DualCaseERC20Trait { + fn name(self: @DualCaseERC20) -> felt252 { let args = array![]; call_contract_syscall(*self.contract_address, selectors::name, args.span()) .unwrap_and_cast() } - fn symbol(self: @DualERC20) -> felt252 { + fn symbol(self: @DualCaseERC20) -> felt252 { let args = array![]; call_contract_syscall(*self.contract_address, selectors::symbol, args.span()) .unwrap_and_cast() } - fn decimals(self: @DualERC20) -> u8 { + fn decimals(self: @DualCaseERC20) -> u8 { let args = array![]; call_contract_syscall(*self.contract_address, selectors::decimals, args.span()) .unwrap_and_cast() } - fn total_supply(self: @DualERC20) -> u256 { + fn total_supply(self: @DualCaseERC20) -> u256 { let mut args = array![]; try_selector_with_fallback( *self.contract_address, selectors::total_supply, selectors::totalSupply, args.span() @@ -56,7 +56,7 @@ impl DualERC20Impl of DualERC20Trait { .unwrap_and_cast() } - fn balance_of(self: @DualERC20, account: ContractAddress) -> u256 { + fn balance_of(self: @DualCaseERC20, account: ContractAddress) -> u256 { let mut args = array![]; args.append_serde(account); @@ -66,7 +66,7 @@ impl DualERC20Impl of DualERC20Trait { .unwrap_and_cast() } - fn allowance(self: @DualERC20, owner: ContractAddress, spender: ContractAddress) -> u256 { + fn allowance(self: @DualCaseERC20, owner: ContractAddress, spender: ContractAddress) -> u256 { let mut args = array![]; args.append_serde(owner); args.append_serde(spender); @@ -75,7 +75,7 @@ impl DualERC20Impl of DualERC20Trait { .unwrap_and_cast() } - fn transfer(self: @DualERC20, recipient: ContractAddress, amount: u256) -> bool { + fn transfer(self: @DualCaseERC20, recipient: ContractAddress, amount: u256) -> bool { let mut args = array![]; args.append_serde(recipient); args.append_serde(amount); @@ -85,7 +85,7 @@ impl DualERC20Impl of DualERC20Trait { } fn transfer_from( - self: @DualERC20, sender: ContractAddress, recipient: ContractAddress, amount: u256 + self: @DualCaseERC20, sender: ContractAddress, recipient: ContractAddress, amount: u256 ) -> bool { let mut args = array![]; args.append_serde(sender); @@ -98,7 +98,7 @@ impl DualERC20Impl of DualERC20Trait { .unwrap_and_cast() } - fn approve(self: @DualERC20, spender: ContractAddress, amount: u256) -> bool { + fn approve(self: @DualCaseERC20, spender: ContractAddress, amount: u256) -> bool { let mut args = array![]; args.append_serde(spender); args.append_serde(amount); From fcdd6d1b02306f18f1089656f15b87d1701ffde3 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 14 Sep 2023 07:08:21 +0200 Subject: [PATCH 121/246] feat: add Errors modules (#691) --- src/access/accesscontrol/accesscontrol.cairo | 9 +++- src/access/ownable/ownable.cairo | 12 +++-- src/account/account.cairo | 15 ++++-- src/introspection/src5.cairo | 6 ++- src/security/initializable.cairo | 6 ++- src/security/pausable.cairo | 11 ++++- src/security/reentrancyguard.cairo | 6 ++- src/token/erc20/erc20.cairo | 21 ++++++--- src/token/erc721/erc721.cairo | 49 ++++++++++++-------- 9 files changed, 96 insertions(+), 39 deletions(-) diff --git a/src/access/accesscontrol/accesscontrol.cairo b/src/access/accesscontrol/accesscontrol.cairo index 718137ecf..90dfe421c 100644 --- a/src/access/accesscontrol/accesscontrol.cairo +++ b/src/access/accesscontrol/accesscontrol.cairo @@ -58,6 +58,11 @@ mod AccessControl { new_admin_role: felt252 } + mod Errors { + const INVALID_CALLER: felt252 = 'Can only renounce role for self'; + const MISSING_ROLE: felt252 = 'Caller is missing role'; + } + #[external(v0)] impl SRC5Impl of ISRC5 { fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { @@ -98,7 +103,7 @@ mod AccessControl { fn renounce_role(ref self: ContractState, role: felt252, account: ContractAddress) { let caller: ContractAddress = get_caller_address(); - assert(caller == account, 'Can only renounce role for self'); + assert(caller == account, Errors::INVALID_CALLER); self._revoke_role(role, account); } } @@ -140,7 +145,7 @@ mod AccessControl { fn assert_only_role(self: @ContractState, role: felt252) { let caller: ContractAddress = get_caller_address(); let authorized: bool = AccessControlImpl::has_role(self, role, caller); - assert(authorized, 'Caller is missing role'); + assert(authorized, Errors::MISSING_ROLE); } fn _grant_role(ref self: ContractState, role: felt252, account: ContractAddress) { diff --git a/src/access/ownable/ownable.cairo b/src/access/ownable/ownable.cairo index b249d7528..ee621afa4 100644 --- a/src/access/ownable/ownable.cairo +++ b/src/access/ownable/ownable.cairo @@ -25,6 +25,12 @@ mod Ownable { new_owner: ContractAddress, } + mod Errors { + const NOT_OWNER: felt252 = 'Caller is not the owner'; + const ZERO_ADDRESS_CALLER: felt252 = 'Caller is the zero address'; + const ZERO_ADDRESS_OWNER: felt252 = 'New owner is the zero address'; + } + #[generate_trait] impl InternalImpl of InternalTrait { fn initializer(ref self: ContractState, owner: ContractAddress) { @@ -34,8 +40,8 @@ mod Ownable { fn assert_only_owner(self: @ContractState) { let owner: ContractAddress = self._owner.read(); let caller: ContractAddress = get_caller_address(); - assert(!caller.is_zero(), 'Caller is the zero address'); - assert(caller == owner, 'Caller is not the owner'); + assert(!caller.is_zero(), Errors::ZERO_ADDRESS_CALLER); + assert(caller == owner, Errors::NOT_OWNER); } fn _transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { @@ -55,7 +61,7 @@ mod Ownable { } fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { - assert(!new_owner.is_zero(), 'New owner is the zero address'); + assert(!new_owner.is_zero(), Errors::ZERO_ADDRESS_OWNER); self.assert_only_owner(); self._transfer_ownership(new_owner); } diff --git a/src/account/account.cairo b/src/account/account.cairo index 3bda0e34a..ce2eb8c2f 100644 --- a/src/account/account.cairo +++ b/src/account/account.cairo @@ -66,6 +66,13 @@ mod Account { removed_owner_guid: felt252 } + mod Errors { + const INVALID_CALLER: felt252 = 'Account: invalid caller'; + const INVALID_SIGNATURE: felt252 = 'Account: invalid signature'; + const INVALID_TX_VERSION: felt252 = 'Account: invalid tx version'; + const UNAUTHORIZED: felt252 = 'Account: unauthorized'; + } + #[constructor] fn constructor(ref self: ContractState, _public_key: felt252) { self.initializer(_public_key); @@ -81,13 +88,13 @@ mod Account { // Avoid calls from other contracts // https://github.com/OpenZeppelin/cairo-contracts/issues/344 let sender = get_caller_address(); - assert(sender.is_zero(), 'Account: invalid caller'); + assert(sender.is_zero(), Errors::INVALID_CALLER); // Check tx version let tx_info = get_tx_info().unbox(); let version = tx_info.version; if version != TRANSACTION_VERSION { - assert(version == QUERY_VERSION, 'Account: invalid tx version'); + assert(version == QUERY_VERSION, Errors::INVALID_TX_VERSION); } _execute_calls(calls) @@ -190,7 +197,7 @@ mod Account { let tx_info = get_tx_info().unbox(); let tx_hash = tx_info.transaction_hash; let signature = tx_info.signature; - assert(self._is_valid_signature(tx_hash, signature), 'Account: invalid signature'); + assert(self._is_valid_signature(tx_hash, signature), Errors::INVALID_SIGNATURE); starknet::VALIDATED } @@ -218,7 +225,7 @@ mod Account { fn assert_only_self() { let caller = get_caller_address(); let self = get_contract_address(); - assert(self == caller, 'Account: unauthorized'); + assert(self == caller, Errors::UNAUTHORIZED); } #[internal] diff --git a/src/introspection/src5.cairo b/src/introspection/src5.cairo index 75ad1d3b2..51a4e6720 100644 --- a/src/introspection/src5.cairo +++ b/src/introspection/src5.cairo @@ -10,6 +10,10 @@ mod SRC5 { supported_interfaces: LegacyMap } + mod Errors { + const INVALID_ID: felt252 = 'SRC5: invalid id'; + } + #[external(v0)] impl SRC5Impl of interface::ISRC5 { fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { @@ -34,7 +38,7 @@ mod SRC5 { } fn deregister_interface(ref self: ContractState, interface_id: felt252) { - assert(interface_id != interface::ISRC5_ID, 'SRC5: invalid id'); + assert(interface_id != interface::ISRC5_ID, Errors::INVALID_ID); self.supported_interfaces.write(interface_id, false); } } diff --git a/src/security/initializable.cairo b/src/security/initializable.cairo index e0e7e658f..934813276 100644 --- a/src/security/initializable.cairo +++ b/src/security/initializable.cairo @@ -8,6 +8,10 @@ mod Initializable { initialized: bool } + mod Errors { + const INITIALIZED: felt252 = 'Initializable: is initialized'; + } + #[generate_trait] impl InternalImpl of InternalTrait { fn is_initialized(self: @ContractState) -> bool { @@ -15,7 +19,7 @@ mod Initializable { } fn initialize(ref self: ContractState) { - assert(!self.is_initialized(), 'Initializable: is initialized'); + assert(!self.is_initialized(), Errors::INITIALIZED); self.initialized.write(true); } } diff --git a/src/security/pausable.cairo b/src/security/pausable.cairo index 73480add7..7441891a8 100644 --- a/src/security/pausable.cairo +++ b/src/security/pausable.cairo @@ -22,15 +22,22 @@ mod Pausable { Paused: Paused, Unpaused: Unpaused, } + #[derive(Drop, starknet::Event)] struct Paused { account: ContractAddress } + #[derive(Drop, starknet::Event)] struct Unpaused { account: ContractAddress } + mod Errors { + const PAUSED: felt252 = 'Pausable: paused'; + const NOT_PAUSED: felt252 = 'Pausable: not paused'; + } + #[external(v0)] impl PausableImpl of super::IPausable { fn is_paused(self: @ContractState) -> bool { @@ -41,11 +48,11 @@ mod Pausable { #[generate_trait] impl InternalImpl of InternalTrait { fn assert_not_paused(self: @ContractState) { - assert(!self.paused.read(), 'Pausable: paused'); + assert(!self.paused.read(), Errors::PAUSED); } fn assert_paused(self: @ContractState) { - assert(self.paused.read(), 'Pausable: not paused'); + assert(self.paused.read(), Errors::NOT_PAUSED); } fn _pause(ref self: ContractState) { diff --git a/src/security/reentrancyguard.cairo b/src/security/reentrancyguard.cairo index f08713776..d6a218c62 100644 --- a/src/security/reentrancyguard.cairo +++ b/src/security/reentrancyguard.cairo @@ -10,10 +10,14 @@ mod ReentrancyGuard { entered: bool } + mod Errors { + const REENTRANT_CALL: felt252 = 'ReentrancyGuard: reentrant call'; + } + #[generate_trait] impl InternalImpl of InternalTrait { fn start(ref self: ContractState) { - assert(!self.entered.read(), 'ReentrancyGuard: reentrant call'); + assert(!self.entered.read(), Errors::REENTRANT_CALL); self.entered.write(true); } diff --git a/src/token/erc20/erc20.cairo b/src/token/erc20/erc20.cairo index 9448b8ef3..df5d1fd25 100644 --- a/src/token/erc20/erc20.cairo +++ b/src/token/erc20/erc20.cairo @@ -40,6 +40,15 @@ mod ERC20 { value: u256 } + mod Errors { + const APPROVE_FROM_ZERO: felt252 = 'ERC20: approve from 0'; + const APPROVE_TO_ZERO: felt252 = 'ERC20: approve to 0'; + const TRANSFER_FROM_ZERO: felt252 = 'ERC20: transfer from 0'; + const TRANSFER_TO_ZERO: felt252 = 'ERC20: transfer to 0'; + const BURN_FROM_ZERO: felt252 = 'ERC20: burn from 0'; + const MINT_TO_ZERO: felt252 = 'ERC20: mint to 0'; + } + #[constructor] fn constructor( ref self: ContractState, @@ -188,14 +197,14 @@ mod ERC20 { } fn _mint(ref self: ContractState, recipient: ContractAddress, amount: u256) { - assert(!recipient.is_zero(), 'ERC20: mint to 0'); + assert(!recipient.is_zero(), Errors::MINT_TO_ZERO); self._total_supply.write(self._total_supply.read() + amount); self._balances.write(recipient, self._balances.read(recipient) + amount); self.emit(Transfer { from: Zeroable::zero(), to: recipient, value: amount }); } fn _burn(ref self: ContractState, account: ContractAddress, amount: u256) { - assert(!account.is_zero(), 'ERC20: burn from 0'); + assert(!account.is_zero(), Errors::BURN_FROM_ZERO); self._total_supply.write(self._total_supply.read() - amount); self._balances.write(account, self._balances.read(account) - amount); self.emit(Transfer { from: account, to: Zeroable::zero(), value: amount }); @@ -204,8 +213,8 @@ mod ERC20 { fn _approve( ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256 ) { - assert(!owner.is_zero(), 'ERC20: approve from 0'); - assert(!spender.is_zero(), 'ERC20: approve to 0'); + assert(!owner.is_zero(), Errors::APPROVE_FROM_ZERO); + assert(!spender.is_zero(), Errors::APPROVE_TO_ZERO); self._allowances.write((owner, spender), amount); self.emit(Approval { owner, spender, value: amount }); } @@ -216,8 +225,8 @@ mod ERC20 { recipient: ContractAddress, amount: u256 ) { - assert(!sender.is_zero(), 'ERC20: transfer from 0'); - assert(!recipient.is_zero(), 'ERC20: transfer to 0'); + assert(!sender.is_zero(), Errors::TRANSFER_FROM_ZERO); + assert(!recipient.is_zero(), Errors::TRANSFER_TO_ZERO); self._balances.write(sender, self._balances.read(sender) - amount); self._balances.write(recipient, self._balances.read(recipient) + amount); self.emit(Transfer { from: sender, to: recipient, value: amount }); diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 0abffb4d1..559b57c9c 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -60,6 +60,19 @@ mod ERC721 { approved: bool } + mod Errors { + const INVALID_TOKEN_ID: felt252 = 'ERC721: invalid token ID'; + const INVALID_ACCOUNT: felt252 = 'ERC721: invalid account'; + const UNAUTHORIZED: felt252 = 'ERC721: unauthorized caller'; + const APPROVAL_TO_OWNER: felt252 = 'ERC721: approval to owner'; + const SELF_APPROVAL: felt252 = 'ERC721: self approval'; + const INVALID_RECEIVER: felt252 = 'ERC721: invalid receiver'; + const ALREADY_MINTED: felt252 = 'ERC721: token already minted'; + const WRONG_SENDER: felt252 = 'ERC721: wrong sender'; + const SAFE_MINT_FAILED: felt252 = 'ERC721: safe mint failed'; + const SAFE_TRANSFER_FAILED: felt252 = 'ERC721: safe transfer failed'; + } + #[constructor] fn constructor( ref self: ContractState, @@ -103,7 +116,7 @@ mod ERC721 { } fn token_uri(self: @ContractState, token_id: u256) -> felt252 { - assert(self._exists(token_id), 'ERC721: invalid token ID'); + assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); self._token_uri.read(token_id) } } @@ -111,7 +124,7 @@ mod ERC721 { #[external(v0)] impl ERC721MetadataCamelOnlyImpl of interface::IERC721MetadataCamelOnly { fn tokenURI(self: @ContractState, tokenId: u256) -> felt252 { - assert(self._exists(tokenId), 'ERC721: invalid token ID'); + assert(self._exists(tokenId), Errors::INVALID_TOKEN_ID); self._token_uri.read(tokenId) } } @@ -119,7 +132,7 @@ mod ERC721 { #[external(v0)] impl ERC721Impl of interface::IERC721 { fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { - assert(!account.is_zero(), 'ERC721: invalid account'); + assert(!account.is_zero(), Errors::INVALID_ACCOUNT); self._balances.read(account) } @@ -128,7 +141,7 @@ mod ERC721 { } fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { - assert(self._exists(token_id), 'ERC721: invalid token ID'); + assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); self._token_approvals.read(token_id) } @@ -144,7 +157,7 @@ mod ERC721 { let caller = get_caller_address(); assert( owner == caller || ERC721Impl::is_approved_for_all(@self, owner, caller), - 'ERC721: unauthorized caller' + Errors::UNAUTHORIZED ); self._approve(to, token_id); } @@ -159,8 +172,7 @@ mod ERC721 { ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 ) { assert( - self._is_approved_or_owner(get_caller_address(), token_id), - 'ERC721: unauthorized caller' + self._is_approved_or_owner(get_caller_address(), token_id), Errors::UNAUTHORIZED ); self._transfer(from, to, token_id); } @@ -173,8 +185,7 @@ mod ERC721 { data: Span ) { assert( - self._is_approved_or_owner(get_caller_address(), token_id), - 'ERC721: unauthorized caller' + self._is_approved_or_owner(get_caller_address(), token_id), Errors::UNAUTHORIZED ); self._safe_transfer(from, to, token_id, data); } @@ -242,7 +253,7 @@ mod ERC721 { let owner = self._owners.read(token_id); match owner.is_zero() { bool::False(()) => owner, - bool::True(()) => panic_with_felt252('ERC721: invalid token ID') + bool::True(()) => panic_with_felt252(Errors::INVALID_TOKEN_ID) } } @@ -262,7 +273,7 @@ mod ERC721 { fn _approve(ref self: ContractState, to: ContractAddress, token_id: u256) { let owner = self._owner_of(token_id); - assert(owner != to, 'ERC721: approval to owner'); + assert(owner != to, Errors::APPROVAL_TO_OWNER); self._token_approvals.write(token_id, to); self.emit(Approval { owner, approved: to, token_id }); @@ -274,14 +285,14 @@ mod ERC721 { operator: ContractAddress, approved: bool ) { - assert(owner != operator, 'ERC721: self approval'); + assert(owner != operator, Errors::SELF_APPROVAL); self._operator_approvals.write((owner, operator), approved); self.emit(ApprovalForAll { owner, operator, approved }); } fn _mint(ref self: ContractState, to: ContractAddress, token_id: u256) { - assert(!to.is_zero(), 'ERC721: invalid receiver'); - assert(!self._exists(token_id), 'ERC721: token already minted'); + assert(!to.is_zero(), Errors::INVALID_RECEIVER); + assert(!self._exists(token_id), Errors::ALREADY_MINTED); self._balances.write(to, self._balances.read(to) + 1); self._owners.write(token_id, to); @@ -292,9 +303,9 @@ mod ERC721 { fn _transfer( ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 ) { - assert(!to.is_zero(), 'ERC721: invalid receiver'); + assert(!to.is_zero(), Errors::INVALID_RECEIVER); let owner = self._owner_of(token_id); - assert(from == owner, 'ERC721: wrong sender'); + assert(from == owner, Errors::WRONG_SENDER); // Implicit clear approvals, no need to emit an event self._token_approvals.write(token_id, Zeroable::zero()); @@ -324,7 +335,7 @@ mod ERC721 { self._mint(to, token_id); assert( _check_on_erc721_received(Zeroable::zero(), to, token_id, data), - 'ERC721: safe mint failed' + Errors::SAFE_MINT_FAILED ); } @@ -337,12 +348,12 @@ mod ERC721 { ) { self._transfer(from, to, token_id); assert( - _check_on_erc721_received(from, to, token_id, data), 'ERC721: safe transfer failed' + _check_on_erc721_received(from, to, token_id, data), Errors::SAFE_TRANSFER_FAILED ); } fn _set_token_uri(ref self: ContractState, token_id: u256, token_uri: felt252) { - assert(self._exists(token_id), 'ERC721: invalid token ID'); + assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); self._token_uri.write(token_id, token_uri) } } From 79e5127dc918a33b4973157393cd16b9bb0941b2 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 14 Sep 2023 20:37:20 -0400 Subject: [PATCH 122/246] change to dark theme --- docs/modules/ROOT/pages/api/erc721.adoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 6f39f3260..162a4b64e 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -14,7 +14,7 @@ TIP: For an overview of ERC721, read our xref:erc721.adoc[ERC721 guide]. [[IERC721]] === `++IERC721++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/cairo-2/src/token/erc721/interface.cairo#L13-L31[{github-icon},role=heading-link] -[.hljs-theme-light.nopadding] +[.hljs-theme-dark] ```javascript use openzeppelin::token::erc721::interface::IERC721; ``` @@ -167,7 +167,7 @@ Emitted when `token_id` token is transferred from `from` to `to`. [[IERC721Metadata]] === `++IERC721Metadata++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/cairo-2/src/token/erc721/interface.cairo#L54-L59[{github-icon},role=heading-link] -[.hljs-theme-light.nopadding] +[.hljs-theme-dark] ```javascript use openzeppelin::token::erc721::interface::IERC721Metadata; ``` @@ -235,7 +235,7 @@ If the URI is not set for `token_id`, the return value will be `0`. [[ERC721]] === `++ERC721++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/cairo-2/src/token/erc721/erc721.cairo#L7[{github-icon},role=heading-link] -[.hljs-theme-light.nopadding] +[.hljs-theme-dark] ```javascript use openzeppelin::token::erc721::ERC721; ``` @@ -456,7 +456,7 @@ This function panics if: [[IERC721Receiver]] === `++IERC721Receiver++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/cairo-2/src/token/erc721/interface.cairo#L70-L79[{github-icon},role=heading-link] -[.hljs-theme-light.nopadding] +[.hljs-theme-dark] ```javascript use openzeppelin::token::erc721::interface::IERC721Receiver; ``` From a93462b9a6c1aa60e33facfb210b80293e086d70 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 14 Sep 2023 21:01:58 -0400 Subject: [PATCH 123/246] fix functions heading --- docs/modules/ROOT/pages/api/erc721.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 162a4b64e..79b6b6c0e 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -46,7 +46,7 @@ Interface of the IERC721 standard as defined in {eip721}. * xref:#IERC721-Transfer[`++Transfer(from, to, token_id)++`] -- -===== Functions +==== Functions [.contract-item] [[IERC721-balance_of]] From 643ddc341c7765edab502e07b84dceb157e41e1d Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 14 Sep 2023 21:14:48 -0400 Subject: [PATCH 124/246] change panics to requirements --- docs/modules/ROOT/pages/api/erc721.adoc | 99 +++++++++++++------------ 1 file changed, 52 insertions(+), 47 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 79b6b6c0e..92eb7e265 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -60,9 +60,9 @@ Returns the number of NFTs owned by `account`. Returns the owner address of `token_id`. -This function panics if: +Requirements: -- `token_id` does not exist. +- `token_id` exists. [.contract-item] [[IERC721-transfer_from]] @@ -75,12 +75,12 @@ Usage of <> prevents loss, though Emits a <> event. -This function panics if: +Requirements: -- Caller is neither approved nor the `token_id` owner. -- `to` is the zero address. -- `from` is not the token owner. -- `token_id` does not exist. +- Caller either approved or the `token_id` owner. +- `to` is not the zero address. +- `from` is not the zero address. +- `token_id` exists. [.contract-item] [[IERC721-safe_transfer_from]] @@ -93,13 +93,13 @@ awareness of the ERC721 protocol, see {erc721-receiver}. Emits a <> event. -This function panics if: +Requirements: -- Caller is neither approved nor the `token_id` owner. -- `to` is the zero address. -- `from` is not the token owner. -- `token_id` does not exist. -- `to` neither is an account contract nor supports the IERC721Receiver interface. +- Caller is either approved or the `token_id` owner. +- `to` is not the zero address. +- `from` is not the zero address. +- `token_id` exists. +- `to` is either an account contract or supports the <> interface. [.contract-item] [[IERC721-approve]] @@ -109,11 +109,11 @@ Change or reaffirm the approved address for an NFT. Emits an <> event. -This function panics if: +Requirements: -- Caller is neither an approved operator nor the `token_id` owner. -- `to` is either the `owner` or the zero address. -- `token_id` does not exist. +- The caller is either an approved operator or the `token_id` owner. +- `to` cannot be the token owner or the zero address. +- `token_id` exists. [.contract-item] [[IERC721-set_approval_for_all]] @@ -123,9 +123,9 @@ Enable or disable approval for `operator` to manage all of the caller's assets. Emits an <> event. -This function panics if: +Requirements: -- `owner` is the `operator`. +- `operator` cannot be the caller. [.contract-item] [[IERC721-get_approved]] @@ -133,9 +133,9 @@ This function panics if: Returns the address approved for `token_id`. -This function panics if: +Requirements: -- `token_id` does not exist. +- `token_id` exists. [.contract-item] [[IERC721-is_approved_for_all]] @@ -336,9 +336,9 @@ Tokens start existing when they are minted (<>), and stop ex Internal function that returns whether `spender` is allowed to manage `token_id`. -This function panics if: +Requirements: -- `token_id` does not exist. +- `token_id` exists. [.contract-item] [[ERC721-_approve]] @@ -348,10 +348,10 @@ Internal function that changes or reaffirms the approved address for an NFT. Emits an <> event. -This function panics if: +Requirements: -- `token_id` does not exist. -- `to` is the current token owner. +- `token_id` exists. +`to` is not the current token owner. [.contract-item] [[ERC721-_set_approval_for_all]] @@ -362,9 +362,9 @@ Internal function that enables or disables approval for `operator` to manage all Emits an <> event. -This function panics if: +Requirements: --`owner` is the `operator`. +- `operator` cannot be the caller. [.contract-item] [[ERC721-_mint]] @@ -376,10 +376,10 @@ Internal function that mints `token_id` and transfers it to `to`. Emits an <> event. -This function panics if: +Requirements: -- `to` is the zero address. -- `token_id` already exists. +- `to` is not the zero address. +- `token_id` does not already exist. [.contract-item] [[ERC721-_transfer]] @@ -389,11 +389,11 @@ Internal function that transfers `token_id` from `from` to `to`. Emits an <> event. -This function panics if: +Requirements: -- `to` is the zero address. -- `from` is not the token owner. -- `token_id` does not exist. +- `to` is not the zero address. +- `from` is the token owner. +- `token_id` exists. [.contract-item] [[ERC721-_burn]] @@ -405,9 +405,9 @@ This internal function does not check if the sender is authorized to operate on Emits an <> event. -This function panics if: +Requirements: -- `token_id` does not exist. +`token_id` exists. [.contract-item] [[ERC721-_safe_mint]] @@ -418,10 +418,15 @@ If `to` is not an account contract, `to` must support <> event. -This function panics if: +Requirements: -- `token_id` does not exist. -- `to` neither is an account contract nor supports the <> interface. +`token_id` exists. +- `to` is either an account contract or supports the <> interface. + +Requirements: + +- `token_id` exists. +- `to` either is an account contract or supports the <> interface. [.contract-item] [[ERC721-_safe_transfer]] @@ -435,12 +440,12 @@ This function is equivalent to <> Emits an <> event. -This function panics if: +Requirements: -- `to` is the zero address. -- `from` is not the token owner. -- `token_id` does not exist. -- `to` neither is an account contract nor supports the <> interface. +- `to` cannot be the zero address. +- `from` must be the token owner. +- `token_id` exists. +- `to` either is an account contract or supports the <> interface. [.contract-item] [[ERC721-_set_token_uri]] @@ -448,9 +453,9 @@ This function panics if: Sets the `token_uri` of `token_id`. -This function panics if: +Requirements: -- `token_id` does not exist. +- `token_id` exists. [.contract] [[IERC721Receiver]] From 7373daa47cb0442be59e723cf58784e4527b4e90 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 15 Sep 2023 20:55:21 +0200 Subject: [PATCH 125/246] Update account docs (#709) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: update format and add api * fix: typo * feat: add counterfactual deployment doc * feat: add API entries * feat: add events * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Andrew Fleming * feat: update from reviews * feat: apply review updates * feat: update docs * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * feat: apply review updates * fix: account casing * feat: add headers * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * feat: add link * feat: move API * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * refactor: update wording * Update docs/antora.yml Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * feat: apply update reviews * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * refactor: UI * fix: UI * feat: focus on SRC6 * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * feat: apply review updates --------- Co-authored-by: Andrew Fleming Co-authored-by: Martín Triay --- docs/antora.yml | 3 + docs/modules/ROOT/nav.adoc | 5 +- docs/modules/ROOT/pages/accounts.adoc | 655 ++---------------- docs/modules/ROOT/pages/api/account.adoc | 246 +++++++ .../modules/ROOT/pages/guides/deployment.adoc | 42 ++ src/account/account.cairo | 4 +- 6 files changed, 359 insertions(+), 596 deletions(-) create mode 100644 docs/modules/ROOT/pages/api/account.adoc create mode 100644 docs/modules/ROOT/pages/guides/deployment.adoc diff --git a/docs/antora.yml b/docs/antora.yml index ceea1e622..011b0fdf6 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -3,3 +3,6 @@ title: Contracts for Cairo version: 0.7.0 nav: - modules/ROOT/nav.adoc +asciidoc: + attributes: + page-sidebar-collapse-default: true diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 3c84b7d03..d78f8a2bd 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -4,6 +4,9 @@ * xref:proxies.adoc[Proxies and Upgrades] * xref:accounts.adoc[Accounts] +** xref:/guides/deployment.adoc[Counterfactual deployments] +** xref:/api/account.adoc[API Reference] + * xref:access.adoc[Access Control] * Tokens @@ -16,4 +19,4 @@ * xref:udc.adoc[Universal Deployer Contract] * xref:utilities.adoc[Utilities] -* xref:contracts::index.adoc[Contracts for Solidity] \ No newline at end of file +* xref:contracts::index.adoc[Contracts for Solidity] diff --git a/docs/modules/ROOT/pages/accounts.adoc b/docs/modules/ROOT/pages/accounts.adoc index 55cbbc5e4..101f71f9c 100644 --- a/docs/modules/ROOT/pages/accounts.adoc +++ b/docs/modules/ROOT/pages/accounts.adoc @@ -1,632 +1,101 @@ :test-signers: https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.6.1/tests/signers.py +:snip-5: https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md +:snip-6: https://github.com/ericnordelo/SNIPs/blob/feat/standard-account/SNIPS/snip-6.md +:counterfactual: xref:/guides/deployment.adoc[Counterfactual Deployments] = Accounts -Unlike Ethereum where accounts are directly derived from a private key, there's no native account concept on StarkNet. +Unlike Ethereum where accounts are derived from a private key, all Starknet accounts are contracts. This means there's no Externally Owned Account (EOA) +concept on Starknet. -Instead, signature validation has to be done at the contract level. -To relieve smart contract applications such as ERC20 tokens or exchanges from this responsibility, we make use of Account contracts to deal with transaction authentication. +Instead, the network features native account abstraction and signature validation happens at the contract level. -For a general overview of the account abstraction, see StarkWare's https://medium.com/starkware/starknet-alpha-0-10-0-923007290470[StarkNet Alpha 0.10]. -A more detailed discussion on the topic can be found in https://community.starknet.io/t/starknet-account-abstraction-model-part-1/781[StarkNet Account Abstraction Part 1]. +For a general overview of account abstraction, see +https://docs.starknet.io/documentation/architecture_and_concepts/Accounts/introduction/[Starknet's documentation]. +A more detailed discussion on the topic can be found in +https://community.starknet.io/t/starknet-account-abstraction-model-part-1/781[Starknet Shaman's forum]. -== Table of Contents +TIP: For detailed information on the usage and implementation check the xref:/api/account.adoc[API Reference] section. -* <> -* <> - ** <> -* <> -* <> -** <> - ** <> - ** <> - ** <> -* <> - ** <> - ** <> -* <> -* <> - ** <> - ** <> - ** <> - ** <> - ** <> - ** <> - ** <> - ** <> - ** <> -* <> - ** <> - ** <> -* <> -* <> -* <> -* <> +== Standard Account Interface -== Quickstart +Accounts in Starknet are smart contracts, and so they can be deployed and interacted +with like any other contract, and can be extended to implement any custom logic. However, an account is a special type +of contract that is used to validate and execute transactions. For this reason, it must implement a set of entrypoints +that the protocol uses for this execution flow. The {snip-6}[SNIP-6] proposal defines a standard interface for accounts, +supporting this execution flow and interoperability with DApps in the ecosystem. -The general workflow is: +=== ISRC6 Interface -. Account contract is deployed to StarkNet. -. Signed transactions can now be sent to the Account contract which validates and executes them. - -In Python, this would look as follows: - -[,python] ----- -from starkware.starknet.testing.starknet import Starknet -from utils import get_contract_class -from signers import MockSigner - -signer = MockSigner(123456789987654321) - -starknet = await Starknet.empty() - -# 1. Deploy Account -account = await starknet.deploy( - get_contract_class("Account"), - constructor_calldata=[signer.public_key] -) - -# 2. Send transaction through Account -await signer.send_transaction(account, some_contract_address, 'some_function', [some_parameter]) ----- - -== Account entrypoints - -Account contracts have only three entry points for all user interactions: - -1. <> validates the declaration signature prior to the declaration. -As of Cairo v0.10.0, contract classes should be declared from an Account contract. - -2. <> verifies the transaction signature before executing the transaction with `\\__execute__`. - -3. <> acts as the state-changing entry point for all user interaction with any contract, including managing the account contract itself. -That's why if you want to change the public key controlling the Account, you would send a transaction targeting the very Account contract: - -[,python] ----- -await signer.send_transaction( - account, - account.contract_address, - 'set_public_key', - [NEW_KEY] -) ----- - -Or if you want to update the Account's L1 address on the `AccountRegistry` contract, you would - -[,python] ----- -await signer.send_transaction(account, registry.contract_address, 'set_L1_address', [NEW_ADDRESS]) ----- - -NOTE: You can read more about how messages are structured and hashed in the https://github.com/OpenZeppelin/cairo-contracts/discussions/24[Account message scheme discussion]. -For more information on the design choices and implementation of multicall, you can read the https://github.com/OpenZeppelin/cairo-contracts/discussions/27[How should Account multicall work discussion]. - -The `\\__validate__` and `\\__execute__` methods accept the same arguments; however, `\\__execute__` returns a transaction response: - -[,cairo] ----- -func __validate__( - call_array_len: felt, call_array: AccountCallArray*, calldata_len: felt, calldata: felt*) { -} - -func __execute__( - call_array_len: felt, call_array: AccountCallArray*, calldata_len: felt, calldata: felt* -) -> (response_len: felt, response: felt*) { -} ----- - -Where: - -* `call_array_len` is the number of calls. -* `call_array` is an array representing each `Call`. -* `calldata_len` is the number of calldata parameters. -* `calldata` is an array representing the function parameters. - -NOTE: The scheme of building multicall transactions within the `\\__execute__` method will change once StarkNet allows for pointers in struct arrays. -In which case, multiple transactions can be passed to (as opposed to built within) `\\__execute__`. - -There's a fourth canonical entrypoint for accounts, the `\\__validate_deploy__` method. It is **only callable by the protocol** during the execution of a `DeployAccount` type of transaction, but not by any other contract. This entrypoint is for counterfactual deployments. - -=== Counterfactual Deployments - -Counterfactual means something that hasn't happened. - -A deployment is said to be counterfactual when the deployed contract pays for it. It's called like this because we need to send the funds to the address before deployment. A deployment that hasn't happened. - -The steps are the following: - -1. Precompute the `address` given a `class_hash`, `salt`, and constructor `calldata`. -2. Send funds to `address`. -3. Send a `DeployAccount` type transaction. -4. The protocol will then validate with `\\__validate_deploy__`. -5. If successful, the protocol deploys the contract and the contract itself pays for the transaction. - -Since `address` will ultimately depend on the `class_hash` and `calldata`, it's safe for the protocol to validate the signature and spend the funds on that address. - -== Standard interface - -The https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.6.1/src/openzeppelin/account/IAccount.cairo[`IAccount.cairo`] contract interface contains the standard account interface proposed in https://github.com/OpenZeppelin/cairo-contracts/discussions/41[#41] and adopted by OpenZeppelin and Argent. -It implements https://eips.ethereum.org/EIPS/eip-1271[EIP-1271] and it is agnostic of signature validation. Further, nonce management is handled on the protocol level. - -NOTE: `\\__validate_deploy__` is not part of the interface since it's only callable by the protocol. Also contracts don't need to implement it to be considered accounts. - -[,cairo] +[,javascript] ---- +/// Represents a call to a target contract function. struct Call { - to: felt, - selector: felt, - calldata_len: felt, - calldata: felt*, -} - -// Tmp struct introduced while we wait for Cairo to support passing `[Call]` to __execute__ -struct CallArray { - to: felt, - selector: felt, - data_offset: felt, - data_len: felt, + to: ContractAddress, + selector: felt252, + calldata: Array } +/// Standard Account Interface +trait ISRC6 { + /// Executes a transaction through the account. + fn __execute__(calls: Array) -> Array>; -@contract_interface -namespace IAccount { - func supportsInterface(interfaceId: felt) -> (success: felt) { - } - - func isValidSignature(hash: felt, signature_len: felt, signature: felt*) -> (isValid: felt) { - } - - func __validate__( - call_array_len: felt, call_array: AccountCallArray*, calldata_len: felt, calldata: felt* - ) { - } - - func __validate_declare__(class_hash: felt) { - } - - func __execute__( - call_array_len: felt, call_array: AccountCallArray*, calldata_len: felt, calldata: felt* - ) -> (response_len: felt, response: felt*) { - } -} - ----- - -== Keys, signatures and signers - -While the interface is agnostic of signature validation schemes, this implementation assumes there's a public-private key pair controlling the Account. -That's why the `constructor` function expects a `public_key` parameter to set it. -Since there's also a `setPublicKey()` method, accounts can be effectively transferred. + /// Asserts whether the transaction is valid to be executed. + fn __validate__(calls: Array) -> felt252; -=== Signature validation - -Signature validation occurs separately from execution as of Cairo v0.10. -Upon receiving transactions, an account contract first calls `\\__validate__`. -An account will only execute a transaction if, and only if, the signature proves valid. -This decoupling allows for a protocol-level distinction between invalid and reverted transactions. -See <>. - -=== Signer - -The signer is responsible for creating a transaction signature with the user's private key for a given transaction. -This implementation utilizes https://github.com/OpenZeppelin/nile/blob/main/src/nile/signer.py[Nile's Signer] class to create transaction signatures through the `Signer` method `sign_transaction`. - -`sign_transaction` expects the following parameters per transaction: - -* `sender` the contract address invoking the tx. -* `calls` a list containing a sublist of each call to be sent. -Each sublist must consist of: - .. `to` the address of the target contract of the message. - .. `selector` the function to be called on the target contract. - .. `calldata` the parameters for the given `selector`. -* `nonce` an unique identifier of this message to prevent transaction replays. -* `max_fee` the maximum fee a user will pay. - -Which returns: - -* `calldata` a list of arguments for each call. -* `sig_r` the transaction signature. -* `sig_s` the transaction signature. - -While the `Signer` class performs much of the work for a transaction to be sent, it neither manages nonces nor invokes the actual transaction on the Account contract. -To simplify Account management, most of this is abstracted away with `MockSigner`. - -=== MockSigner utility - -The `MockSigner` class in {test-signers}[signers.py] is used to perform transactions on a given Account, crafting the transaction and managing nonces. - -NOTE: StarkNet's testing framework does not currently support transaction invocations from account contracts. `MockSigner` therefore utilizes StarkNet's API gateway to manually execute the `InvokeFunction` for testing. - -A `MockSigner` instance exposes the following methods: - -* `send_transaction(account, to, selector_name, calldata, nonce=None, max_fee=0)` returns a link:https://docs.python.org/3/library/asyncio-future.html[future] of a signed transaction, ready to be sent. -* `send_transactions(account, calls, nonce=None, max_fee=0)` returns a future of batched signed transactions, ready to be sent. -* `declare_class(account, contract_name, nonce=None, max_fee=0)` returns a future of a declaration transaction. -* `deploy_account(state, calldata, salt=0, nonce=0, max_fee=0)`: returns a future of a counterfactual deployment. - -To use `MockSigner`, pass a private key when instantiating the class: - -[,python] ----- -from utils import MockSigner - -PRIVATE_KEY = 123456789987654321 -signer = MockSigner(PRIVATE_KEY) ----- - -Then send single transactions with the `send_transaction` method. - -[,python] ----- -await signer.send_transaction(account, contract_address, 'method_name', []) ----- - -If utilizing multicall, send multiple transactions with the `send_transactions` method. - -[,python] ----- -await signer.send_transactions( - account, - [ - (contract_address, 'method_name', [param1, param2]), - (contract_address, 'another_method', []) - ] -) ----- - -Use `declare_class` to declare a contract: - -[,python] ----- -await signer.declare_class(account, "MyToken") ----- - -And `deploy_account` to <> deploy an account: - -[,python] ----- -await signer.deploy_account(state, [signer.public_key]) ----- - - -=== MockEthSigner utility - -The `MockEthSigner` class in {test-signers}[signers.py] is used to perform transactions on a given Account with a secp256k1 curve key pair, crafting the transaction and managing nonces. -It differs from the `MockSigner` implementation by: - -* Not using the public key but its derived address instead (the last 20 bytes of the keccak256 hash of the public key and adding `0x` to the beginning). -* Signing the message with a secp256k1 curve address. - -== `Call` and `AccountCallArray` format - -The idea is for all user intent to be encoded into a `Call` representing a smart contract call. -Users can also pack multiple messages into a single transaction (creating a multicall transaction). -Cairo currently does not support arrays of structs with pointers which means the `\\__execute__` function cannot properly iterate through multiple ``Call``s. -Instead, this implementation utilizes a workaround with the `AccountCallArray` struct. -See <>. - -=== `Call` - -A single `Call` is structured as follows: - -[,cairo] ----- -struct Call { - to: felt - selector: felt - calldata_len: felt - calldata: felt* + /// Asserts whether a given signature for a given hash is valid. + fn is_valid_signature(hash: felt252, signature: Array) -> felt252; } ---- -Where: - -* `to` is the address of the target contract of the message. -* `selector` is the selector of the function to be called on the target contract. -* `calldata_len` is the number of calldata parameters. -* `calldata` is an array representing the function parameters. +{snip-6}[SNIP-6] adds the `is_valid_signature` method. This method is not used by the protocol, but it's useful for +DApps to verify the validity of signatures, supporting features like Sign In with Starknet. -=== `AccountCallArray` +SNIP-6 also defines that compliant accounts must implement the SRC5 interface following {snip-5}[SNIP-5], as +a mechanism for detecting whether a contract is an account or not through introspection. -`AccountCallArray` is structured as: +=== ISRC5 Interface -[,cairo] +[,javascript] ---- -struct AccountCallArray { - to: felt - selector: felt - data_offset: felt - data_len: felt +/// Standard Interface Detection +trait ISRC5 { + /// Queries if a contract implements a given interface. + fn supports_interface(interface_id: felt252) -> bool; } ---- -Where: - -* `to` is the address of the target contract of the message. -* `selector` is the selector of the function to be called on the target contract. -* `data_offset` is the starting position of the calldata array that holds the ``Call``'s calldata. -* `data_len` is the number of calldata elements in the `Call`. - -== Multicall transactions - -A multicall transaction packs the `to`, `selector`, `calldata_offset`, and `calldata_len` of each call into the `AccountCallArray` struct and keeps the cumulative calldata for every call in a separate array. -The `\\__execute__` function rebuilds each message by combining the `AccountCallArray` with its calldata (demarcated by the offset and calldata length specified for that particular call). -The rebuilding logic is set in the internal `_from_call_array_to_call`. - -This is the basic flow: - -First, the user sends the messages for the transaction through a Signer instantiation which looks like this: - -[,python] ----- -await signer.send_transaction( - account, [ - (contract_address, 'contract_method', [arg_1]), - (contract_address, 'another_method', [arg_1, arg_2]) - ] -) ----- - -Then the `from_call_to_call_array` method in link:https://github.com/OpenZeppelin/nile/blob/main/src/nile/signer.py[Nile's signer] converts each call into the `AccountCallArray` format and cumulatively stores the calldata of every call into a single array. -Next, both arrays (as well as the `sender`, `nonce`, and `max_fee`) are used to create the transaction hash. -The Signer then invokes `\__execute__` with the signature and passes `AccountCallArray`, calldata, and nonce as arguments. - -Finally, the `\\__execute__` method takes the `AccountCallArray` and calldata and builds an array of ``Call``s (MultiCall). - -NOTE: Every transaction utilizes `AccountCallArray`. -A single `Call` is treated as a bundle with one message. - -== API Specification - -This in a nutshell is the Account contract public API: - -[,cairo] ----- -namespace Account { - func constructor(publicKey: felt) { - } - - func getPublicKey() -> (publicKey: felt) { - } - - func supportsInterface(interfaceId: felt) -> (success: felt) { - } - - func setPublicKey(newPublicKey: felt) { - } - - func isValidSignature(hash: felt, signature_len: felt, signature: felt*) -> (isValid: felt) { - } - - func __validate__( - call_array_len: felt, call_array: AccountCallArray*, calldata_len: felt, calldata: felt* - ) -> (response_len: felt, response: felt*) { - } - - func __validate_declare__( - call_array_len: felt, call_array: AccountCallArray*, calldata_len: felt, calldata: felt* - ) -> (response_len: felt, response: felt*) { - } - - func __execute__( - call_array_len: felt, call_array: AccountCallArray*, calldata_len: felt, calldata: felt* - ) -> (response_len: felt, response: felt*) { -} ----- - -=== `constructor` - -Initializes and sets the public key for the Account contract. - -Parameters: - -[,cairo] ----- -publicKey: felt ----- - -Returns: None. - -=== `getPublicKey` - -Returns the public key associated with the Account. - -Parameters: None. - -Returns: - -[,cairo] ----- -publicKey: felt ----- - -=== `supportsInterface` - -Returns `TRUE` if this contract implements the interface defined by `interfaceId`. -Account contracts now implement ERC165 through static support (see <>). - -Parameters: - -[,cairo] ----- -interfaceId: felt ----- - -Returns: - -[,cairo] ----- -success: felt ----- - -=== `setPublicKey` - -Sets the public key that will control this Account. -It can be used to rotate keys for security, change them in case of compromised keys or even transferring ownership of the account. - -Parameters: - -[,cairo] ----- -newPublicKey: felt ----- - -Returns: None. - -=== `isValidSignature` - -This function is inspired by https://eips.ethereum.org/EIPS/eip-1271[EIP-1271] and returns `TRUE` if a given signature is valid, otherwise it reverts. -In the future it will return `FALSE` if a given signature is invalid (for more info please check https://github.com/OpenZeppelin/cairo-contracts/issues/327[this issue]). - -Parameters: - -[,cairo] ----- -hash: felt -signature_len: felt -signature: felt* ----- - -Returns: - -[,cairo] ----- -isValid: felt ----- - -NOTE: It may return `FALSE` in the future if a given signature is invalid (follow the discussion on https://github.com/OpenZeppelin/cairo-contracts/issues/327[this issue]). - -=== `\\__validate__` - -Validates the transaction signature and is called prior to `\\__execute__`. - -Parameters: - -[,cairo] ----- -call_array_len: felt -call_array: AccountCallArray* -calldata_len: felt -calldata: felt* ----- - -Returns: None. - -=== `\\__validate_declare__` - -Validates the signature for declaration transactions. - -Parameters: - -[,cairo] ----- -class_hash: felt ----- - -Returns: None. - -=== `\\__validate_deploy__` - -Validates the signature for counterfactual deployment transactions. - -It takes the `class_hash` of the account being deployed along with the `salt` and `calldata`, the latter being expanded. For example if the account is deployed with calldata `[arg_1, ..., arg_n]`: - -Parameters: - -[,cairo] ----- -class_hash: felt -salt: felt -arg_1: felt -... -arg_n: felt ----- - -Returns: None. - -=== `\\__execute__` - -This is the only external entrypoint to interact with the Account contract. -It: - -. Calls the target contract with the intended function selector and calldata parameters. -. Forwards the contract call response data as return value. - -Parameters: - -[,cairo] ----- -call_array_len: felt -call_array: AccountCallArray* -calldata_len: felt -calldata: felt* ----- - -NOTE: The current signature scheme expects a 2-element array like `[sig_r, sig_s]`. - -Returns: - -[,cairo] ----- -response_len: felt -response: felt* ----- - -== Presets - -The following contract presets are ready to deploy and can be used as-is for quick prototyping and testing. -Each preset differs on the signature type being used by the Account. - -=== Account - -The https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.6.1/src/openzeppelin/account/presets/Account.cairo[`Account`] preset uses StarkNet keys to validate transactions. - -=== Eth Account - -The https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.6.1/src/openzeppelin/account/presets/EthAccount.cairo[`EthAccount`] preset supports Ethereum addresses, validating transactions with secp256k1 keys. - -== Account introspection with ERC165 - -Certain contracts like ERC721 or ERC1155 require a means to differentiate between account contracts and non-account contracts. -For a contract to declare itself as an account, it should implement https://eips.ethereum.org/EIPS/eip-165[ERC165] as proposed in https://github.com/OpenZeppelin/cairo-contracts/discussions/100[#100]. - -To be in compliance with ERC165 specifications, we calculate the account contract ID as the XOR of ``IAccount``'s equivalent EVM selectors (not StarkNet selectors). -This magic value has been tracking the changes of the still evolving Account interface standard, and **its current value is `0xa66bd575`**. - -Our ERC165 integration on StarkNet is inspired by OpenZeppelin's Solidity implementation of https://docs.openzeppelin.com/contracts/4.x/api/utils#ERC165Storage[ERC165Storage] which stores the interfaces that the implementing contract supports. -In the case of account contracts, querying `supportsInterface` of an account's address with the `IAccount` magic value should return `TRUE`. - -NOTE: For Account contracts, ERC165 support is static and does not require Account contracts to register. - -== Extending the Account contract +{snip-6}[SNIP-6] compliant accounts must return `true` when queried for the ISRC6 interface Id. -Account contracts can be extended by following the xref:extensibility.adoc#the_pattern[extensibility pattern]. +Even though these interfaces are not enforced by the protocol, it's recommended to implement them for enabling +interoperability with the ecosystem. -To implement custom account contracts, it's required by the StarkNet compiler that they include the three entrypoint functions `\\__validate__`, `\\__validate_declare__`, and `\\__execute__`. -`\\__validate__` and `\\__validate_declare__` should include the same signature validation method; whereas, `\\__execute__` should only handle the actual transaction. Incorporating a new validation scheme necessitates only that it's invoked by both `\\__validate__` and `\\__validate_declare__`. +== Protocol-level methods -This is why the Account library comes with different flavors of signature validation methods like `is_valid_eth_signature` and the vanilla `is_valid_signature`. +In this section we will describe the methods that the protocol uses for abstracting the accounts. The first two +are required for enabling accounts to be used for executing transactions. The rest are optional: -Account contract developers are encouraged to implement the https://github.com/OpenZeppelin/cairo-contracts/discussions/41[standard Account interface] and incorporate the custom logic thereafter. +1. `\\__validate__` verifies the validity of the transaction to be executed. This is usually used to validate signatures, +but the entrypoint implementation can be customized to feature any validation mechanism https://docs.starknet.io/documentation/architecture_and_concepts/Accounts/validate_and_execute/#validate_limitations[with some limitations]. -IMPORTANT: Due to current inconsistencies between the testing framework and the actual StarkNet network, extreme caution should be used when integrating new Account contracts. -Instances have occurred where account functionality tests pass and transactions execute correctly on the local node; yet, they fail on public networks. -For this reason, it's highly encouraged that new account contracts are also deployed and tested on the public testnet. -See https://github.com/OpenZeppelin/cairo-contracts/issues/386[issue #386] for more information. +2. `\\__execute__` executes the transaction if the validation is successful. -Some other validation schemes to look out for in the future: +3. `\\__validate_declare__` optional entrypoint similar to `\\__validate__` but for transactions +meant to declare other contracts. -* Multisig. -* Guardian logic like in https://github.com/argentlabs/argent-contracts-starknet/blob/de5654555309fa76160ba3d7393d32d2b12e7349/contracts/ArgentAccount.cairo[Argent's account]. +4. `\\__validate_deploy__` optional entrypoint similar to `\\__validate__` but meant for {counterfactual}. -== L1 escape hatch mechanism +NOTE: Although these entrypoints are available to the protocol for its regular transaction flow, they can also be called like any other method. -[unknown, to be defined] +== Deploying an account -== Paying for gas +In Starknet there are two ways of deploying smart contracts: using the `deploy_syscall` and doing +counterfactual deployments. +The former can be easily done with the xref:udc.adoc[Universal Deployer Contract (UDC)], a contract that +wraps and exposes the `deploy_syscall` to provide arbitrary deployments through regular contract calls. +But if you don't have an account to invoke it, you will probably want to use the latter. -[unknown, to be defined] +To do counterfactual deployments, you need to implement another protocol-level entrypoint named +`\\__validate_deploy__`. You can check the {counterfactual} guide to learn how. diff --git a/docs/modules/ROOT/pages/api/account.adoc b/docs/modules/ROOT/pages/api/account.adoc new file mode 100644 index 000000000..52ed25191 --- /dev/null +++ b/docs/modules/ROOT/pages/api/account.adoc @@ -0,0 +1,246 @@ +:github-icon: pass:[] +:snip6: https://github.com/ericnordelo/SNIPs/blob/feat/standard-account/SNIPS/snip-6.md[SNIP-6] + += Account + +Reference of interfaces, presets, and utilities related to account contracts. + +== Core + +[.contract] +[[ISRC6]] +=== `++ISRC6++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/cairo-2/src/account/interface.cairo#L12[{github-icon},role=heading-link] + +```javascript +use openzeppelin::account::interface::ISRC6; +``` + +Interface of the SRC6 Standard Account as defined in the {snip6}. + +[.contract-index] +.Functions +-- +* xref:#ISRC6-\\__execute__[`++__execute__(calls)++`] +* xref:#ISRC6-\\__validate__[`++__validate__(calls)++`] +* xref:#ISRC6-is_valid_signature[`++is_valid_signature(hash, signature)++`] +-- + +[#ISRC6-Functions] +==== Functions + +[.contract-item] +[[ISRC6-__execute__]] +==== `[.contract-item-name]#++__execute__++#++(calls: Array) → Array>++` [.item-kind]#external# + +Executes the list of calls as a transaction after validation. + +Returns an array with each call's output. + +NOTE: The `Call` struct is defined in https://github.com/starkware-libs/cairo/blob/main/corelib/src/starknet/account.cairo#L3[corelib]. + +[.contract-item] +[[ISRC6-__validate__]] +==== `[.contract-item-name]#++__validate__++#++(calls: Array) → felt252++` [.item-kind]#external# + +Validates a transaction before execution. + +Returns the short string `'VALID'` if valid, otherwise it reverts. + +[.contract-item] +[[ISRC6-is_valid_signature]] +==== `[.contract-item-name]#++is_valid_signature++#++(hash: felt252, signature: Array) → felt252++` [.item-kind]#external# + +Validates whether a signature is valid or not for the given message hash. + +Returns the short string `'VALID'` if valid, otherwise it reverts. + +[.contract] +[[Account]] +=== `++Account++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/cairo-2/src/account/account.cairo#L27[{github-icon},role=heading-link] + +:OwnerAdded: xref:Account-OwnerAdded[OwnerAdded] +:OwnerRemoved: xref:Account-OwnerRemoved[OwnerRemoved] + +```javascript +use openzeppelin::account::Account; +``` +Account contract implementation extending xref:ISRC6[`ISRC6`]. + +[.contract-index] +.Utilities +-- +* xref:#Account-assert_only_self[`++InternalImpl::assert_only_self(self)++`] +-- + +[.contract-index] +.External Functions +-- +* xref:#Account-\\__validate_deploy__[`++__validate_deploy__(self, hash, signature)++`] + +[.contract-subindex-inherited] +.SRC6Impl + +* xref:#Account-\\__execute__[`++__execute__(self, calls)++`] +* xref:#Account-\\__validate__[`++__validate__(self, calls)++`] +* xref:#Account-is_valid_signature[`++is_valid_signature(self, hash, signature)++`] + +[.contract-subindex-inherited] +.SRC5Impl + +* xref:#Account-supports_interface[`++supports_interface(self, interface_id)++`] + +[.contract-subindex-inherited] +.DeclarerImpl + +* xref:#Account-\\__validate_declare__[`++__validate_declare__(self, class_hash)++`] + +[.contract-subindex-inherited] +.PublicKeyImpl + +* xref:#Account-set_public_key[`++set_public_key(self, new_public_key)++`] +* xref:#Account-get_public_key[`++get_public_key(self)++`] +-- + +[.contract-index] +.Internal Functions +-- +* xref:#Account-constructor[`++constructor(self, _public_key)++`] + +[.contract-subindex-inherited] +.InternalImpl + +* xref:#Account-initializer[`++initializer(self, _public_key)++`] +* xref:#Account-validate_transaction[`++validate_transaction(self)++`] +* xref:#Account-_set_public_key[`++_set_public_key(self, new_public_key)++`] +* xref:#Account-_is_valid_signature[`++_is_valid_signature(self, hash, signature)++`] +-- + +[.contract-index] +.Events +-- +* xref:#Account-OwnerAdded[`++OwnerAdded(new_owner_guid)++`] +* xref:#Account-OwnerRemoved[`++OwnerRemoved(removed_owner_guid)++`] +-- + +[#Account-Utilities] +==== Utilities + +[.contract-item] +[[Account-assert_only_self]] +==== `[.contract-item-name]#++assert_only_self++#++(self: @ContractState)++` [.item-kind]#internal# + +Validates that the caller is the account itself. Otherwise it reverts. + +[#Account-Functions] +==== Functions + +[.contract-item] +[[Account-constructor]] +==== `[.contract-item-name]#++constructor++#++(ref self: ContractState, _public_key: felt252)++` [.item-kind]#constructor# + +Initializes the account with the given public key, and registers the ISRC6 interface ID. + +Emits an {OwnerAdded} event. + +[.contract-item] +[[Account-__validate_deploy__]] +==== `[.contract-item-name]#++__validate_deploy__++#++(self: @ContractState, class_hash: felt252, contract_address_salt: felt252, _public_key: felt252) → felt252++` [.item-kind]#external# + +Validates a https://docs.starknet.io/documentation/architecture_and_concepts/Network_Architecture/Blocks/transactions/#deploy_account_transaction[`DeployAccount` transaction]. +See xref:/guides/deployment.adoc[Counterfactual Deployments]. + +Returns the short string `'VALID'` if valid, otherwise it reverts. + +[.contract-item] +[[Account-__execute__]] +==== `[.contract-item-name]#++__execute__++#++(ref self: ContractState, calls: Array) → Array>++` [.item-kind]#external# + +See xref:ISRC6-\\__execute__[ISRC6::\\__execute__]. + +[.contract-item] +[[Account-__validate__]] +==== `[.contract-item-name]#++__validate__++#++(self: @ContractState, calls: Array) → felt252++` [.item-kind]#external# + +See xref:ISRC6-\\__validate__[ISRC6::\\__validate__]. + +[.contract-item] +[[Account-is_valid_signature]] +==== `[.contract-item-name]#++is_valid_signature++#++(self: @ContractState, hash: felt252, signature: Array) → felt252++` [.item-kind]#external# + +See xref:ISRC6-is_valid_signature[ISRC6::is_valid_signature]. + +[.contract-item] +[[Account-supports_interface]] +==== `[.contract-item-name]#++supports_interface++#++(self: @ContractState, interface_id: felt252) → bool++` [.item-kind]#external# + +Returns whether a contract implements a given interface or not. + +[.contract-item] +[[Account-__validate_declare__]] +==== `[.contract-item-name]#++__validate_declare__++#++(self: @ContractState, class_hash: felt252) → felt252++` [.item-kind]#external# + +Validates a https://docs.starknet.io/documentation/architecture_and_concepts/Network_Architecture/Blocks/transactions/#declare-transaction[`Declare` transaction]. + +Returns the short string `'VALID'` if valid, otherwise it reverts. + +[.contract-item] +[[Account-set_public_key]] +==== `[.contract-item-name]#++set_public_key++#++(ref self: ContractState, new_public_key: felt252)++` [.item-kind]#external# + +Sets a new public key for the account. Only accesible by the account calling itself through `\\__execute__`. + +Emits both an {OwnerRemoved} and an {OwnerAdded} event. + +[.contract-item] +[[Account-get_public_key]] +==== `[.contract-item-name]#++get_public_key++#++(self: @ContractState)++ → felt252` [.item-kind]#external# + +Returns the current public key of the account. + +[.contract-item] +[[Account-initializer]] +==== `[.contract-item-name]#++initializer++#++(ref self: ContractState, _public_key: felt252)++` [.item-kind]#internal# + +Initializes the account with the given public key, and registers the ISRC6 interface ID. + +Emits an {OwnerAdded} event. + +[.contract-item] +[[Account-validate_transaction]] +==== `[.contract-item-name]#++validate_transaction++#++(self: @ContractState)++ → felt252` [.item-kind]#internal# + +Validates a transaction signature from the +https://github.com/starkware-libs/cairo/blob/main/corelib/src/starknet/info.cairo#L61[global context]. + +Returns the short string `'VALID'` if valid, otherwise it reverts. + +[.contract-item] +[[Account-_set_public_key]] +==== `[.contract-item-name]#++_set_public_key++#++(ref self: ContractState, new_public_key: felt252)++` [.item-kind]#internal# + +Set the public key without validating the caller. + +Emits an {OwnerAdded} event. + +CAUTION: The usage of this method outside the `set_public_key` function is discouraged. + +[.contract-item] +[[Account-_is_valid_signature]] +==== `[.contract-item-name]#++_is_valid_signature++#++(self: @ContractState, hash: felt252, signature: Span)++ → bool` [.item-kind]#internal# + +Validates the provided `signature` for the `hash`, using the account current public key. + +[#Account-Events] +==== Events + +[.contract-item] +[[Account-OwnerAdded]] +==== `[.contract-item-name]#++OwnerAdded++#++(new_owner_guid: felt252)++` [.item-kind]#event# + +Emitted when a `public_key` is added. + +[.contract-item] +[[Account-OwnerRemoved]] +==== `[.contract-item-name]#++OwnerRemoved++#++(removed_owner_guid: felt252)++` [.item-kind]#event# + +Emitted when a `public_key` is removed. diff --git a/docs/modules/ROOT/pages/guides/deployment.adoc b/docs/modules/ROOT/pages/guides/deployment.adoc new file mode 100644 index 000000000..92f6a1bd2 --- /dev/null +++ b/docs/modules/ROOT/pages/guides/deployment.adoc @@ -0,0 +1,42 @@ +:foundry: https://foundry-rs.github.io/starknet-foundry/starknet/account.html[Starknet Foundry] +:starkli: https://book.starkli.rs/accounts#account-deployment[Starkli] + += Counterfactual deployments + +A counterfactual contract is a contract we can interact with even before actually deploying it on-chain. +For example, we can send funds or assign privileges to a contract that doesn't yet exist. +Why? Because deployments in Starknet are deterministic, allowing us to predict the address where our contract will be deployed. +We can leverage this property to make a contract pay for its own deployment by simply sending funds in advance. We call this a counterfactual deployment. + +This process can be described with the following steps: + +TIP: For testing this flow you can check the {foundry} or the {starkli} guides for deploying accounts. + +1. Deterministically precompute the `contract_address` given a `class_hash`, `salt`, and constructor `calldata`. +Note that the `class_hash` must be previously declared for the deployment to succeed. + +2. Send funds to the `contract_address`. Usually you will estimate the fee of the transaction first. Existing +tools usually do this for you. + +3. Send a `DeployAccount` type transaction to the network. + +4. The protocol will then validate the transaction with the `\\__validate_deploy__` entrypoint of the contract to be deployed. + +5. If the validation succeeds, the protocol will charge the fee and then register the contract as deployed. + +NOTE: Although this method is very popular to deploy accounts, this works for any kind of contract. + +== Deployment validation + +To be counterfactually deployed, the deploying contract must implement the `\\__validate_deploy__` entrypoint, +called by the protocol when a `DeployAccount` transaction is sent to the network. + +[,javascript] +---- +trait IDeployable { + /// Must return 'VALID' when the validation is successful. + fn __validate_deploy__( + class_hash: felt252, contract_address_salt: felt252, _public_key: felt252 + ) -> felt252; +} +---- diff --git a/src/account/account.cairo b/src/account/account.cairo index ce2eb8c2f..13b7e8d0e 100644 --- a/src/account/account.cairo +++ b/src/account/account.cairo @@ -228,7 +228,7 @@ mod Account { assert(self == caller, Errors::UNAUTHORIZED); } - #[internal] + #[private] fn _execute_calls(mut calls: Array) -> Array> { let mut res = ArrayTrait::new(); loop { @@ -245,7 +245,7 @@ mod Account { res } - #[internal] + #[private] fn _execute_single_call(call: Call) -> Span { let Call{to, selector, calldata } = call; starknet::call_contract_syscall(to, selector, calldata.span()).unwrap() From 0b451c3a9902103fbe6d6e62b97d873c904c6488 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 17 Sep 2023 22:00:20 -0400 Subject: [PATCH 126/246] refactor usage section --- docs/modules/ROOT/pages/erc721.adoc | 207 +++++++++++++++++++++++----- 1 file changed, 170 insertions(+), 37 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 21f83dfa7..3eac58ef3 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -3,24 +3,13 @@ :token-types: https://docs.openzeppelin.com/contracts/4.x/tokens#different-kinds-of-tokens[non-fungible tokens] :eip721: https://eips.ethereum.org/EIPS/eip-721[EIP-721] :erc721-api: xref:/api/erc721.adoc[API Reference] +:introspection: xref:/introspection.adoc[Introspection] The ERC721 token standard is a specification for {token-types}, or more colloquially: NFTs. The `erc721.cairo` contract implements an approximation of {eip721} in Cairo for StarkNet. TIP: For detailed information on the usage and implementation check the {erc721-api} section. -== Table of Contents - -* <> -** <> -* <> - ** <> - ** <> - ** <> -* <> -* <> - ** <> - == IERC721 [,javascript] @@ -70,10 +59,10 @@ But some differences can still be found, such as: The EIP721 standard, however, states that the return value should be of type string. If a token's URI is not set, the returned value is `0`. Note that URIs cannot exceed 31 characters at this time. -See <>. +See <>. * ``interface_id``s are hardcoded and initialized by the constructor. The hardcoded values derive from Starknet's selector calculcations. -See <>. +See the {introspection} docs. * `safe_transfer_from` can only be expressed as a single function in Cairo as opposed to the two functions declared in EIP721. The difference between both functions consists of accepting `data` as an argument. Because function overloading is currently not possible in Cairo, `safe_transfer_from` by default accepts the `data` argument. @@ -88,18 +77,110 @@ In doing so, recipient contracts (both accounts and non-accounts) can be verifie == Usage +:components: https://community.starknet.io/t/cairo-1-contract-syntax-is-evolving/94794#extensibility-and-components-11[Components] + +WARNING: The following example uses a contract's `unsafe_new_contract_state` to access another contract's state. +This is currently unsafe, because storage members could clash among used contracts if not reviewed carefully. +Extensibility will be revisited after {components} are introduced. + +Using Contracts for Cairo, constructing an ERC721 contract requires setting up the constructor and exposing the ERC721 interface. +Here’s what that looks like: + +[,javascript] +---- +#[starknet::contract] +mod MyNFT { + use starknet::ContractAddress; + use openzeppelin::token::erc721::ERC721; + + #[storage] + struct Storage {} + + #[constructor] + fn constructor( + self: @ContractState, + recipient: ContractAddress + ) { + let name = 'MyNFT'; + let symbol = 'NFT'; + let token_id = 1; + let token_uri = 'NFT_URI'; + + let mut unsafe_state = ERC721::unsafe_new_contract_state(); + // Initialize ERC721 + ERC721::InternalImpl::initializer(ref unsafe_state, name, symbol); + // Mint NFT + ERC721::InternalImpl::_mint(ref unsafe_state, recipient, token_id); + // Set the token's URI + ERC721::InternalImpl::_set_token_uri(ref unsafe_state, token_id, token_uri); + } + + // Implement the standard IERC721 interface + #[external(v0)] + impl MyTokenImpl of ERC721ABI { + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { + let unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721::ERC721Impl::balance_of(@unsafe_state, account) + } + + ... + } + + // Implement the IERC721Metadata interface + #[external(v0)] + impl MyTokenMetadataImpl of IERC721Metadata { + fn name(self: @ContractState) -> felt252 { + let unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721::ERC721Impl::name(@unsafe_state) + } + + ... + } +} +---- + +In order for the `MyNFT` contract to extend the ERC721 contract, it utilizes the `unsafe_new_contract_state`. +The unsafe contract state allows access to ERC721's implementations. +With this access, the constructor first calls the initializer to set the NFT name and symbol. +The constructor then calls `_mint` to create a one-of-one NFT. Finally, the constructor sets the token URI. + +Below the constructor, this contract includes two implementations: `IERC721` and `IERC721Metadata`. +`IERC721Metadata` isn't technically required to create an ERC721 contract; however, most contracts include the metadata interface which exposes `name`, `symbol`, and `token_uri`. + === Token Transfers +:src-5: xref:introspection.adoc#src5[SRC-5] + This library includes `transfer_from` and `safe_transfer_from` to transfer NFTs. If using `transfer_from`, *the caller is responsible to confirm that the recipient is capable of receiving NFTs or else they may be permanently lost.* +The `safe_transfer_from` method mitigates this risk by querying the recipient contract's interface support. + -The `safe_transfer_from` method incorporates the following conditional logic: +The safe mechanism first queries if the recipient contract supports the IERC721Receiver interface through introspection ({src-5}). +If the recipient contract does _not_ support the receiver interface, then the safe mechanism checks if the recipient contract supports the ISRC-6 interface, which is the standard account contract interface. +If either case is true, then the token transfer behaves as if `transfer_from` was called. +Otherwise, the transfer will fail. -. If the calling address is an account contract, the token transfer will behave as if `transfer_from` was called. -. If the calling address is not an account contract, the safe function will check that the contract supports ERC721 tokens. +To better visualize the process, see the snippet below. -The current implementation of `safe_transfer_from` checks for `on_erc721_received` and requires that the recipient contract supports SRC5 and exposes the `supports_interface` method. -See <>. +[,javascript] +---- +fn _check_on_erc721_received( + from: ContractAddress, to: ContractAddress, token_id: u256, data: Span +) -> bool { + // Check if `to` has declared support for IERC721Receiver + if (DualCaseSRC5 { contract_address: to } + .supports_interface(interface::IERC721_RECEIVER_ID)) { + DualCaseERC721Receiver { contract_address: to } + .on_erc721_received( + get_caller_address(), from, token_id, data + ) == interface::IERC721_RECEIVER_ID + } else { + // Check if `to` is an account contract + DualCaseSRC5 { contract_address: to }.supports_interface(account::interface::ISRC6_ID) + } +} +---- === Receiving tokens @@ -107,23 +188,10 @@ See <>. :src-6: https://community.starknet.io/t/snip-starknet-standard-account/95665[SRC-6 in Starknet Shamans] :src-5: xref:introspection.adoc#src5[SRC-5] -In order to be sure a contract can safely accept ERC721 tokens, said contract must implement the `IERC721Receiver` interface (as expressed in the EIP721 specification). -Methods such as `safe_transfer_from` and `_safe_mint` call the recipient contract's `on_erc721_received` method. -If the contract fails to return the correct magic value, the transaction fails. - -Starknet contracts that support safe transfers, however, must also support {src-5} and include `supports_interface` as proposed (originally as ERC165) in {erc165-discussion}. -`safe_transfer_from` requires a means of differentiating between account and non-account contracts. -Account contracts must support the Starknet standard account interface in order to communicate the contract's ability to receive safe NFT transfers. -The standard account interface is drafted and defined as {src-6}. -`on_erc721_received` will call `supports_interface` with the SRC6 magic value _0x2ceccef7f994940b3962a6c67e0ba4fcd37df7d131417c604f91e03caecc1cd_ on the recipient address. -SRC6-compliant account contracts will return `true` thus communicating that the recipient is an account contract. -Non-account contracts, however, _must_ register support for ERC721 safe transfers. -Otherwise, the safe transfer will fail. +In order to be sure a non-account contract can safely accept ERC721 tokens, said contract must implement both the `IERC721Receiver` interface (as expressed in the EIP721 specification) and the `ISRC5` interface which supports introspection. ==== IERC721Receiver -The IERC721Receiver interface must be implemented in any non-account contract that wants to support safe transfers from ERC721 asset contracts. - [,javascript] ---- trait IERC721Receiver { @@ -136,14 +204,79 @@ trait IERC721Receiver { } ---- -=== Interpreting ERC721 URIs +Implementing the `IERC721Receiver` interface exposes the `on_erc721_received` method. +When safe methods such as `safe_transfer_from` and `_safe_mint` are called, they invoke the recipient contract's `on_erc721_received` method which *must* return the IERC721Receiver interface ID. +Otherwise, the transaction will fail. + +==== ISRC-5 + +[,javascript] +---- +trait ISRC5 { + fn supports_interface(interface_id: felt252) -> bool; +} +---- + +The `ISRC5` interface allows the safe methods to query if the recipient supports the `IERC721Receiver` interface ID. +See {introspection} for more information. + +==== Creating a token receiver contract + +[,javascript] +---- +#[starknet::contract] +mod ERC721Receiver { + use starknet::ContractAddress; + use openzeppelin::token::erc721::ERC721; + use openzeppelin::token::erc721::interface; + use openzeppelin::introspection::interface::ISRC5; + use openzeppelin::introspection::src5::SRC5; + + #[storage] + struct Storage {} + + #[constructor] + fn constructor(ref self: ContractState) { + // Register the token receiver interface + let mut unsafe_state = SRC5::unsafe_new_contract_state(); + SRC5::InternalImpl::register_interface(ref unsafe_state, interface::IERC721_RECEIVER_ID); + } + + /// Implement the ISRC-5 interface so the sender contract can query + /// if the recipient supports the token receiver interface ID + #[external(v0)] + impl ISRC5Impl of ISRC5 { + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { + let unsafe_state = SRC5::unsafe_new_contract_state(); + SRC5::SRC5Impl::supports_interface(@unsafe_state, interface_id) + } + } + + /// Implement the token receiver interface + #[external(v0)] + impl ERC721ReceiverImpl of interface::IERC721Receiver { + fn on_erc721_received( + self: @ContractState, + operator: ContractAddress, + from: ContractAddress, + token_id: u256, + data: Span + ) -> felt252 { + interface::IERC721_RECEIVER_ID + } + } +} +---- + +=== Storing ERC721 URIs + +:string-roadmap: https://github.com/orgs/starkware-libs/projects/1/views/1?pane=issue&itemId=28823165[here] Token URIs in Cairo are stored as single field elements. Each field element equates to 252-bits (or 31.5 bytes) which means that a token's URI can be no longer than 31 characters. -NOTE: Storing the URI as an array of felts was considered to accommodate larger strings. -While this approach is more flexible regarding URIs, a returned array further deviates from the standard set in https://eips.ethereum.org/EIPS/eip-721[EIP721]. -Therefore, this library's ERC721 implementation sets URIs as a single field element. +NOTE: Native string support in Cairo is currently in progress and tracked {string-roadmap}. +Once Cairo offers full string support, this will be revisited. == Presets From bd5a36eb58de420b5db4dc31901871ad5fb2c4b9 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 18 Sep 2023 02:41:31 -0400 Subject: [PATCH 127/246] add camel methods, link to introspection --- docs/modules/ROOT/pages/api/erc721.adoc | 90 ++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 3 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 92eb7e265..1c6710e00 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -1,6 +1,7 @@ :github-icon: pass:[] :eip721: https://eips.ethereum.org/EIPS/eip-721[EIP721] :erc721-receiver: xref:/erc721.adoc#receiving_tokens[ERC721Receiver] +:casing-discussion: https://github.com/OpenZeppelin/cairo-contracts/discussions/34[here] = ERC721 @@ -35,7 +36,7 @@ Interface of the IERC721 standard as defined in {eip721}. [.contract-subindex-inherited] .ISRC5 -* xref:#IERC721-supports_interface[`++supports_interface(interface_id)++`] +* xref:/api/introspection.adoc#ISRC5-supports_interface[`++supports_interface(interface_id)++`] -- [.contract-index] @@ -196,7 +197,7 @@ See {eip721}. [.contract-subindex-inherited] .ISRC5 -* xref:#IERC721-supports_interface[`++supports_interface(interface_id)++`] +* xref:/api/introspection.adoc#ISRC5-supports_interface[`++supports_interface(interface_id)++`] -- [.contract-index] @@ -266,10 +267,27 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi * xref:#IERC721-get_approved[`++get_approved(self, token_id)++`] * xref:#IERC721-is_approved_for_all[`++is_approved_for_all(self, owner, operator)++`] +[.contract-subindex-inherited] +.ER721Camel + +* xref:#ERC721-tokenURI[`++tokenURI(self, tokenId)++`] +* xref:#ERC721-balanceOf[`++balanceOf(self, account)++`] +* xref:#ERC721-ownerOf[`++ownerOf(self, tokenId)++`] +* xref:#ERC721-transferFrom[`++transferFrom(self, from, to, tokenId)++`] +* xref:#ERC721-safeTransferFrom[`++safeTransferFrom(self, from, to, tokenId, data)++`] +* xref:#ERC721-setApprovalForAll[`++setApprovalForAll(self, operator, approved)++`] +* xref:#ERC721-getApproved[`++getApproved(self, tokenId)++`] +* xref:#ERC721-isApprovedForAll[`++isApprovedForAll(self, owner, operator)++`] + [.contract-subindex-inherited] .ISRC5 -* xref:#ISRC5-supports_interface[`++supports_interface(self, interface_id)++`] +* xref:/api/introspection.adoc#ISRC5-supports_interface[`++supports_interface(self, interface_id)++`] + +[.contract-subindex-inherited] +.SRC5Camel + +* xref:/api/introspection.adoc#ISRC5-supports_interface[`++supportsInterface(self, interfaceId)++`] [.contract-subindex-inherited] .InternalImpl @@ -308,6 +326,72 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi Initializes the state of the ERC721 contract by setting the token name and symbol. The constructor also registers the IERC721 and IERC721_METADATA interface ids according to SRC-5. +[.contract-item] +[[ERC721-tokenURI]] +==== `[.contract-item-name]#++tokenURI++#++(self: @ContractState, tokenId: u256) -> felt252++` [.item-kind]#external# + +See <>. + +Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. + +[.contract-item] +[[ERC721-balanceOf]] +==== `[.contract-item-name]#++balanceOf++#++(self: @ContractState, account: ContractAddress) -> u256++` [.item-kind]#external# + +See <>. + +Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. + +[.contract-item] +[[ERC721-ownerOf]] +==== `[.contract-item-name]#++ownerOf++#++(self: @ContractState, tokenId: u256) -> ContractAddress++` [.item-kind]#external# + +See <>. + +Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. + +[.contract-item] +[[ERC721-transferFrom]] +==== `[.contract-item-name]#++transferFrom++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256)++` [.item-kind]#external# + +See <>. + +Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. + +[.contract-item] +[[ERC721-safeTransferFrom]] +==== `[.contract-item-name]#++safeTransferFrom++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256, data: Span)++` [.item-kind]#external# + +See <>. + +Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. + +* xref:#IERC721-setApprovalForAll[`++setApprovalForAll(operator, approved)++`] + +[.contract-item] +[[ERC721-setApprovalForAll]] +==== `[.contract-item-name]#++setApprovalForAll++#++(ref self: ContractState, operator: ContractAddress, approved: bool)++` [.item-kind]#external# + +See <>. + +Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. + +[.contract-item] +[[ERC721-getApproved]] +==== `[.contract-item-name]#++getApproved++#++(self: @ContractState, tokenId: u256) -> ContractAddress++` [.item-kind]#external# + +See <>. + +Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. + +[.contract-item] +[[ERC721-isApprovedForAll]] +==== `[.contract-item-name]#++isApprovedForAll++#++(self: @ContractState, owner: ContractAddress, operator: ContractAddress) -> bool++` [.item-kind]#external# + +See <>. + +Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. + [.contract-item] [[ERC721-initializer]] ==== `[.contract-item-name]#++initializer++#++(ref self: ContractState, name_: felt252, symbol_: felt252)++` [.item-kind]#internal# From 299ccddedc9242ab43007f1ea482987d9445ae33 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 18 Sep 2023 02:47:01 -0400 Subject: [PATCH 128/246] fix last paragraph --- docs/modules/ROOT/pages/erc721.adoc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 3eac58ef3..77dfd5bf7 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -297,8 +297,6 @@ trait IERC721Metadata { } ---- -The `ERC721Metadata` extension allows your smart contract to be interrogated for its name and for details about the assets which your NFTs represent. +The `ERC721Metadata` extension allows a smart contract to be interrogated for its name and for details about the assets which the NFTs represent. -We follow OpenZeppelin's Solidity approach of integrating the Metadata methods `name`, `symbol`, and `token_uri` (`tokenURI` in Solidity) into all ERC721 implementations. -If preferred, a contract can be created that does not import the Metadata methods from the `ERC721` library. -Note that the `IERC721Metadata` interface id should be removed from the constructor as well. +Contracts for Cairo follows the Solidity Contracts approach of integrating the metadata methods `name`, `symbol`, and `token_uri` (`tokenURI` in Solidity) into all ERC721 implementations. From f407d82614d81efee08ad1a953e9cc8407a4faef Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 18 Sep 2023 02:51:25 -0400 Subject: [PATCH 129/246] fix constructor comment --- src/token/erc721/erc721.cairo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index a5f1cde0d..61e9c1833 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -66,8 +66,6 @@ mod ERC721 { approved: bool } - /// Initializes the state of the ERC721 contract. This includes setting the - /// NFT name and symbol. mod Errors { const INVALID_TOKEN_ID: felt252 = 'ERC721: invalid token ID'; const INVALID_ACCOUNT: felt252 = 'ERC721: invalid account'; @@ -81,6 +79,8 @@ mod ERC721 { const SAFE_TRANSFER_FAILED: felt252 = 'ERC721: safe transfer failed'; } + /// Initializes the state of the ERC721 contract. This includes setting both the + /// NFT name and symbol as well as minting `token_id` to `recipient`. #[constructor] fn constructor( ref self: ContractState, From aeeb0decaada4909a0da711e4d1c9beee828f7fe Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 18 Sep 2023 15:41:41 -0400 Subject: [PATCH 130/246] add external/internal headings in func list --- docs/modules/ROOT/pages/api/erc721.adoc | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 1c6710e00..35d8fc7a1 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -244,10 +244,8 @@ use openzeppelin::token::erc721::ERC721; Implementation of ERC721 which includes the IERC721Metadata extension as specified in https://eips.ethereum.org/EIPS/eip-721[EIP-721]. [.contract-index] -.Functions +.External Functions -- -* xref:#ERC721-constructor[`++constructor(self, name, symbol)++`] - [.contract-subindex-inherited] .IERC721Metadata @@ -288,6 +286,13 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi .SRC5Camel * xref:/api/introspection.adoc#ISRC5-supports_interface[`++supportsInterface(self, interfaceId)++`] +-- + +[.contract-index] +.Internal Functions +-- + +* xref:#ERC721-constructor[`++constructor(self, name, symbol)++`] [.contract-subindex-inherited] .InternalImpl From 2c84e03b77387582fd1c59039ff61b41708b15f9 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 18 Sep 2023 15:54:41 -0400 Subject: [PATCH 131/246] clean up comments --- src/token/erc721/erc721.cairo | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 61e9c1833..7648e0dd4 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -57,7 +57,7 @@ mod ERC721 { token_id: u256 } - /// Emitted when `owner` enables or disables (approved) `operator` to manage + /// Emitted when `owner` enables or disables (`approved`) `operator` to manage /// all of its assets. #[derive(Drop, starknet::Event)] struct ApprovalForAll { @@ -207,9 +207,9 @@ mod ERC721 { self._transfer(from, to, token_id); } - /// Safely transfer ownership of `token_id` from `from` to `to`, checking first - /// that `to` is aware of the ERC721 protocol to prevent tokens being locked - /// forever. + /// Safely transfer ownership of `token_id` from `from` to `to`. + /// If `to` is not an account contract, `to` must support IERC721Receiver; + /// otherwise, the transaction will fail. /// Emits a [Transfer](Transfer) event. fn safe_transfer_from( ref self: ContractState, @@ -411,8 +411,8 @@ mod ERC721 { } /// Internal function that safely transfers `token_id` token from `from` to `to`, - /// checking first that contract recipients are aware of the ERC721 protocol to - /// prevent tokens from being forever locked. + /// If `to` is not an account contract, `to` must support IERC721Receiver; + /// otherwise, the transaction will fail. /// Emits a [Transfer](Transfer) event. fn _safe_transfer( ref self: ContractState, @@ -437,7 +437,7 @@ mod ERC721 { #[internal] /// Internal function that checks if `to` either is an account contract or /// has registered support for the ERC721 interface through SRC-5. - /// The transaction does not execute if both cases are false. + /// The transaction will fail if both cases are false. fn _check_on_erc721_received( from: ContractAddress, to: ContractAddress, token_id: u256, data: Span ) -> bool { From 2550b6ada63dfd9643247c1b6d2a393a7c0b4c9d Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 18 Sep 2023 16:00:27 -0400 Subject: [PATCH 132/246] remove unnecessary list items --- docs/modules/ROOT/pages/erc721.adoc | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 77dfd5bf7..affbf4317 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -50,7 +50,6 @@ trait ISRC5 { Although StarkNet is not EVM compatible, this implementation aims to be as close as possible to the ERC721 standard in the following ways: -* It uses Cairo's `u256` instead of `felt252`. * It makes use of Cairo's short strings to simulate `name` and `symbol`. But some differences can still be found, such as: From bf3df1bccf125a21ad0410dab45b2b14ea538b74 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 18 Sep 2023 17:52:39 -0400 Subject: [PATCH 133/246] remove list item --- docs/modules/ROOT/pages/erc721.adoc | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index affbf4317..b93e5eab9 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -72,7 +72,6 @@ To be as close as possible to the standard, it accepts a dynamic array of felts. * `SRC5.register_interface` allows contracts to set and communicate which interfaces they support. This is similar to OpenZeppelin's {erc165-storage}. * `IERC721Receiver` compliant contracts return a hardcoded selector id according to Starknet selectors (as opposed to selector calculation in Solidity). -In doing so, recipient contracts (both accounts and non-accounts) can be verified that they support ERC721 transfers. == Usage From db2099430b4289c26cc94be2a1a7afe3eab39e66 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 18 Sep 2023 19:27:29 -0400 Subject: [PATCH 134/246] add parenthesis --- docs/modules/ROOT/pages/erc721.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index b93e5eab9..e3806e0ae 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -121,7 +121,7 @@ mod MyNFT { ERC721::ERC721Impl::balance_of(@unsafe_state, account) } - ... + (...) } // Implement the IERC721Metadata interface @@ -132,7 +132,7 @@ mod MyNFT { ERC721::ERC721Impl::name(@unsafe_state) } - ... + (...) } } ---- From 681ef1f98a5ea4291ae6003e94b6274e58fd0880 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 19 Sep 2023 10:06:44 -0400 Subject: [PATCH 135/246] add isrc5 to example --- docs/modules/ROOT/pages/erc721.adoc | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index e3806e0ae..117392649 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -90,6 +90,7 @@ Here’s what that looks like: mod MyNFT { use starknet::ContractAddress; use openzeppelin::token::erc721::ERC721; + use openzeppelin::introspection::interface::ISRC5; #[storage] struct Storage {} @@ -113,9 +114,18 @@ mod MyNFT { ERC721::InternalImpl::_set_token_uri(ref unsafe_state, token_id, token_uri); } - // Implement the standard IERC721 interface + /// Implement the ISRC5 interface. + #[external(v0)] + impl SRC5Impl of ISRC5 { + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { + let unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721::SRC5Impl::supports_interface(@unsafe_state, interface_id) + } + } + + /// Implement the standard IERC721 interface. #[external(v0)] - impl MyTokenImpl of ERC721ABI { + impl MyTokenImpl of IERC721 { fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { let unsafe_state = ERC721::unsafe_new_contract_state(); ERC721::ERC721Impl::balance_of(@unsafe_state, account) @@ -124,7 +134,7 @@ mod MyNFT { (...) } - // Implement the IERC721Metadata interface + /// Implement the IERC721Metadata interface. #[external(v0)] impl MyTokenMetadataImpl of IERC721Metadata { fn name(self: @ContractState) -> felt252 { From 15217df29cf7471a36889c22fec13a044f02c838 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 19 Sep 2023 10:08:09 -0400 Subject: [PATCH 136/246] fix impls --- docs/modules/ROOT/pages/erc721.adoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 117392649..eaa01bbb0 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -90,6 +90,7 @@ Here’s what that looks like: mod MyNFT { use starknet::ContractAddress; use openzeppelin::token::erc721::ERC721; + use openzeppelin::token::erc721::interface; use openzeppelin::introspection::interface::ISRC5; #[storage] @@ -125,7 +126,7 @@ mod MyNFT { /// Implement the standard IERC721 interface. #[external(v0)] - impl MyTokenImpl of IERC721 { + impl MyTokenImpl of interface::IERC721 { fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { let unsafe_state = ERC721::unsafe_new_contract_state(); ERC721::ERC721Impl::balance_of(@unsafe_state, account) @@ -136,7 +137,7 @@ mod MyNFT { /// Implement the IERC721Metadata interface. #[external(v0)] - impl MyTokenMetadataImpl of IERC721Metadata { + impl MyTokenMetadataImpl of interface::IERC721Metadata { fn name(self: @ContractState) -> felt252 { let unsafe_state = ERC721::unsafe_new_contract_state(); ERC721::ERC721Impl::name(@unsafe_state) From 9c0c314b727396f2998b1332f23f9ee8ff8356ae Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Tue, 19 Sep 2023 10:14:43 -0400 Subject: [PATCH 137/246] Apply suggestions from code review Co-authored-by: Eric Nordelo --- docs/modules/ROOT/pages/erc721.adoc | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index eaa01bbb0..62628eddf 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -6,11 +6,11 @@ :introspection: xref:/introspection.adoc[Introspection] The ERC721 token standard is a specification for {token-types}, or more colloquially: NFTs. -The `erc721.cairo` contract implements an approximation of {eip721} in Cairo for StarkNet. +The `erc721.cairo` contract implements an approximation of {eip721} in Cairo for Starknet. TIP: For detailed information on the usage and implementation check the {erc721-api} section. -== IERC721 +== IERC721 Interface [,javascript] ---- @@ -48,7 +48,7 @@ trait ISRC5 { :erc165-storage: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v0.6.1/contracts/utils/introspection/ERC165Storage.sol[ERC165Storage] -Although StarkNet is not EVM compatible, this implementation aims to be as close as possible to the ERC721 standard in the following ways: +Although Starknet is not EVM compatible, this implementation aims to be as close as possible to the ERC721 standard in the following ways: * It makes use of Cairo's short strings to simulate `name` and `symbol`. @@ -62,9 +62,9 @@ See <>. * ``interface_id``s are hardcoded and initialized by the constructor. The hardcoded values derive from Starknet's selector calculcations. See the {introspection} docs. -* `safe_transfer_from` can only be expressed as a single function in Cairo as opposed to the two functions declared in EIP721. +* `safe_transfer_from` can only be expressed as a single function in Cairo as opposed to the two functions declared in EIP721, because function overloading is currently not possible in Cairo. The difference between both functions consists of accepting `data` as an argument. -Because function overloading is currently not possible in Cairo, `safe_transfer_from` by default accepts the `data` argument. +`safe_transfer_from` by default accepts the `data` argument. If `data` is not used, simply pass an empty array. * `safe_transfer_from` is specified such that the optional `data` argument should be of type bytes. In Solidity, this means a dynamically-sized array. @@ -107,9 +107,9 @@ mod MyNFT { let token_uri = 'NFT_URI'; let mut unsafe_state = ERC721::unsafe_new_contract_state(); - // Initialize ERC721 + // Initialize the ERC721 storage ERC721::InternalImpl::initializer(ref unsafe_state, name, symbol); - // Mint NFT + // Mint the NFT to recipient ERC721::InternalImpl::_mint(ref unsafe_state, recipient, token_id); // Set the token's URI ERC721::InternalImpl::_set_token_uri(ref unsafe_state, token_id, token_uri); @@ -149,7 +149,7 @@ mod MyNFT { ---- In order for the `MyNFT` contract to extend the ERC721 contract, it utilizes the `unsafe_new_contract_state`. -The unsafe contract state allows access to ERC721's implementations. +The unsafe contract state allows access to ERC721's storage. With this access, the constructor first calls the initializer to set the NFT name and symbol. The constructor then calls `_mint` to create a one-of-one NFT. Finally, the constructor sets the token URI. @@ -165,7 +165,7 @@ If using `transfer_from`, *the caller is responsible to confirm that the recipie The `safe_transfer_from` method mitigates this risk by querying the recipient contract's interface support. -The safe mechanism first queries if the recipient contract supports the IERC721Receiver interface through introspection ({src-5}). +The safe mechanism first queries if the recipient contract supports the `IERC721Receiver` interface through introspection ({src-5}). If the recipient contract does _not_ support the receiver interface, then the safe mechanism checks if the recipient contract supports the ISRC-6 interface, which is the standard account contract interface. If either case is true, then the token transfer behaves as if `transfer_from` was called. Otherwise, the transfer will fail. @@ -252,7 +252,7 @@ mod ERC721Receiver { } /// Implement the ISRC-5 interface so the sender contract can query - /// if the recipient supports the token receiver interface ID + /// if the recipient supports the token receiver interface ID. #[external(v0)] impl ISRC5Impl of ISRC5 { fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { @@ -261,7 +261,7 @@ mod ERC721Receiver { } } - /// Implement the token receiver interface + /// Implement the token receiver interface. #[external(v0)] impl ERC721ReceiverImpl of interface::IERC721Receiver { fn on_erc721_received( @@ -289,7 +289,7 @@ Once Cairo offers full string support, this will be revisited. == Presets -ERC721 presets have been created to allow for quick deployments as-is whic are a great option for testing and prototyping. +ERC721 presets have been created to allow for quick deployments as-is which are a great option for testing and prototyping. == Extensions From 17c0276fa95ba073b651bff2c4c1d1ae2a9c44f7 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 19 Sep 2023 15:19:17 -0400 Subject: [PATCH 138/246] add interface sections near top --- docs/modules/ROOT/pages/erc721.adoc | 49 +++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 62628eddf..d239cccbb 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -10,7 +10,24 @@ The `erc721.cairo` contract implements an approximation of {eip721} in Cairo for TIP: For detailed information on the usage and implementation check the {erc721-api} section. -== IERC721 Interface +== Interfaces + +:compatibility: xref:/erc721.adoc#erc721_compatibility[ERC721 Compatibility] +:ierc721-interface: xref:/erc721.adoc#ierc721[IERC721] +:ierc721metadata-interface: xref:/erc721.adoc#ierc721metadata[IERC721Metadata] +:isrc5-interface: xref:/erc721.adoc#isrc5[ISRC5] + +Contracts must implement both the {ierc721-interface} and {isrc5-interface} interfaces in order to have an ERC721 compliant contract. +Additionally, ERC721 contracts often include {ierc721metadata-interface} interface as well. +Contracts for Cairo's implementation includes all three interfaces. + +WARNING: The {ierc721-interface} and {ierc721metadata-interface} implementations are approximations of their Ethereum counterparts. +These approximations include a few notable differences. +See {compatibility} for more information. + +=== IERC721 + +The `IERC721` interface provides basic functionality to track, transfer, and consign NFTs. [,javascript] ---- @@ -38,15 +55,41 @@ trait IERC721 { owner: ContractAddress, operator: ContractAddress ) -> bool; } +---- +=== ISRC5 + +:snip5: https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md[SNIP-5] +:eip165: https://eips.ethereum.org/EIPS/eip-165[EIP-165] + +`ISRC5`, from the {snip5} standard, allows for other contracts to query if the implementing contract supports a specific interface. +This is especially important with NFT contracts because if an NFT is transferred to a contract that does not support the `IERC721` interface, the NFT is essentially lost forever. +To mitigate this risk, the `ISRC5` interface allows for interface introspection which, in turn, supports safe Token Transfers. + +TIP: `ISRC5` is very similar to Ethereum's {eip165}. +It's imperative to understand the introspection mechanism to avoid making drastic errors. +See {introspection}. + +[,javascript] +---- trait ISRC5 { fn supports_interface(interface_id: felt252) -> bool; } ---- -=== ERC721 Compatibility +=== IERC721Metadata + +The `ERC721Metadata` extension allows a smart contract to be interrogated for its name and for details about the assets which the NFTs represent. +The vast majority of NFT contracts include this interface. -:erc165-storage: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v0.6.1/contracts/utils/introspection/ERC165Storage.sol[ERC165Storage] +[,javascript] +---- +trait IERC721Metadata { + fn name() -> felt252; + fn symbol() -> felt252; + fn token_uri(token_id: u256) -> felt252; +} +---- Although Starknet is not EVM compatible, this implementation aims to be as close as possible to the ERC721 standard in the following ways: From a5367e763fedb1c6c1c0a841b88223b2f01fd21b Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 19 Sep 2023 15:22:44 -0400 Subject: [PATCH 139/246] fix erc165 storage link and compatibility section --- docs/modules/ROOT/pages/erc721.adoc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index d239cccbb..ef78c93a5 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -91,11 +91,12 @@ trait IERC721Metadata { } ---- -Although Starknet is not EVM compatible, this implementation aims to be as close as possible to the ERC721 standard in the following ways: +== ERC721 Compatibility -* It makes use of Cairo's short strings to simulate `name` and `symbol`. +:erc165-storage: https://docs.openzeppelin.com/contracts/4.x/api/utils#ERC165Storage[ERC165Storage] -But some differences can still be found, such as: +Although Starknet is not EVM compatible, this implementation aims to be as close as possible to the ERC721 standard by utilizing Cairo's short strings to simulate `name` and `symbol`. +This implementation does, however, include a few notable differences such as: * `token_uri` returns a felt252 representation of the queried token's URI. The EIP721 standard, however, states that the return value should be of type string. From ad6d1a1085e7827f86e0c98762fb35320183a948 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 19 Sep 2023 16:23:42 -0400 Subject: [PATCH 140/246] clean up compatibility section --- docs/modules/ROOT/pages/erc721.adoc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index ef78c93a5..b0c08a16e 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -94,6 +94,8 @@ trait IERC721Metadata { == ERC721 Compatibility :erc165-storage: https://docs.openzeppelin.com/contracts/4.x/api/utils#ERC165Storage[ERC165Storage] +:src5-api: xref:introspection.adoc#src5[SRC-5] +:eip165: https://eips.ethereum.org/EIPS/eip-165[EIP165] Although Starknet is not EVM compatible, this implementation aims to be as close as possible to the ERC721 standard by utilizing Cairo's short strings to simulate `name` and `symbol`. This implementation does, however, include a few notable differences such as: @@ -104,7 +106,7 @@ If a token's URI is not set, the returned value is `0`. Note that URIs cannot exceed 31 characters at this time. See <>. * ``interface_id``s are hardcoded and initialized by the constructor. -The hardcoded values derive from Starknet's selector calculcations. +The hardcoded values derive from Starknet's selector calculations. See the {introspection} docs. * `safe_transfer_from` can only be expressed as a single function in Cairo as opposed to the two functions declared in EIP721, because function overloading is currently not possible in Cairo. The difference between both functions consists of accepting `data` as an argument. @@ -113,8 +115,8 @@ If `data` is not used, simply pass an empty array. * `safe_transfer_from` is specified such that the optional `data` argument should be of type bytes. In Solidity, this means a dynamically-sized array. To be as close as possible to the standard, it accepts a dynamic array of felts. -* `SRC5.register_interface` allows contracts to set and communicate which interfaces they support. -This is similar to OpenZeppelin's {erc165-storage}. +* ERC721 utilizes {src5-api} to declare and query interface support on Starknet as opposed to Ethereum's {eip165}. +The design for `SRC-5` is similar to OpenZeppelin's {erc165-storage}. * `IERC721Receiver` compliant contracts return a hardcoded selector id according to Starknet selectors (as opposed to selector calculation in Solidity). == Usage From 920ea79ff08868529dd696217b93698961d59140 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 19 Sep 2023 19:58:31 -0400 Subject: [PATCH 141/246] add api links, remove dash from SRCs --- docs/modules/ROOT/pages/erc721.adoc | 44 ++++++++++++++--------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index b0c08a16e..012112695 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -64,7 +64,7 @@ trait IERC721 { `ISRC5`, from the {snip5} standard, allows for other contracts to query if the implementing contract supports a specific interface. This is especially important with NFT contracts because if an NFT is transferred to a contract that does not support the `IERC721` interface, the NFT is essentially lost forever. -To mitigate this risk, the `ISRC5` interface allows for interface introspection which, in turn, supports safe Token Transfers. +To mitigate this risk, the `ISRC5` interface allows for interface introspection which helps facilitate safe token transfers. TIP: `ISRC5` is very similar to Ethereum's {eip165}. It's imperative to understand the introspection mechanism to avoid making drastic errors. @@ -94,7 +94,7 @@ trait IERC721Metadata { == ERC721 Compatibility :erc165-storage: https://docs.openzeppelin.com/contracts/4.x/api/utils#ERC165Storage[ERC165Storage] -:src5-api: xref:introspection.adoc#src5[SRC-5] +:src5-api: xref:introspection.adoc#src5[SRC5] :eip165: https://eips.ethereum.org/EIPS/eip-165[EIP165] Although Starknet is not EVM compatible, this implementation aims to be as close as possible to the ERC721 standard by utilizing Cairo's short strings to simulate `name` and `symbol`. @@ -116,12 +116,15 @@ If `data` is not used, simply pass an empty array. In Solidity, this means a dynamically-sized array. To be as close as possible to the standard, it accepts a dynamic array of felts. * ERC721 utilizes {src5-api} to declare and query interface support on Starknet as opposed to Ethereum's {eip165}. -The design for `SRC-5` is similar to OpenZeppelin's {erc165-storage}. +The design for `SRC5` is similar to OpenZeppelin's {erc165-storage}. * `IERC721Receiver` compliant contracts return a hardcoded selector id according to Starknet selectors (as opposed to selector calculation in Solidity). == Usage :components: https://community.starknet.io/t/cairo-1-contract-syntax-is-evolving/94794#extensibility-and-components-11[Components] +:name-api: xref:api/erc721.adoc#IERC721Metadata-name[name] +:symbol-api: xref:api/erc721.adoc#IERC721Metadata-symbol[symbol] +:token_uri-api: xref:api/erc721.adoc#IERC721Metadata-token_uri[token_uri] WARNING: The following example uses a contract's `unsafe_new_contract_state` to access another contract's state. This is currently unsafe, because storage members could clash among used contracts if not reviewed carefully. @@ -200,19 +203,22 @@ With this access, the constructor first calls the initializer to set the NFT nam The constructor then calls `_mint` to create a one-of-one NFT. Finally, the constructor sets the token URI. Below the constructor, this contract includes two implementations: `IERC721` and `IERC721Metadata`. -`IERC721Metadata` isn't technically required to create an ERC721 contract; however, most contracts include the metadata interface which exposes `name`, `symbol`, and `token_uri`. +`IERC721Metadata` isn't technically required to create an ERC721 contract; however, most contracts include the metadata interface which exposes {name-api}, {symbol-api}, and {token_uri-api}. === Token Transfers -:src-5: xref:introspection.adoc#src5[SRC-5] +:src-5: xref:introspection.adoc#src5[SRC5] +:transfer_from-api: xref:api/erc721.adoc#IERC721-transfer_from[transfer_from] +:safe_transfer_from-api: xref:api/erc721.adoc#IERC721-safe_transfer_from[safe_transfer_from] +:isrc6: xref:accounts.adoc#isrc6_interface[ISRC6] -This library includes `transfer_from` and `safe_transfer_from` to transfer NFTs. +This library includes {transfer_from-api} and {safe_transfer_from-api} to transfer NFTs. If using `transfer_from`, *the caller is responsible to confirm that the recipient is capable of receiving NFTs or else they may be permanently lost.* The `safe_transfer_from` method mitigates this risk by querying the recipient contract's interface support. The safe mechanism first queries if the recipient contract supports the `IERC721Receiver` interface through introspection ({src-5}). -If the recipient contract does _not_ support the receiver interface, then the safe mechanism checks if the recipient contract supports the ISRC-6 interface, which is the standard account contract interface. +If the recipient contract does _not_ support the receiver interface, then the safe mechanism checks if the recipient contract supports the {isrc6} interface, which is the standard account contract interface. If either case is true, then the token transfer behaves as if `transfer_from` was called. Otherwise, the transfer will fail. @@ -240,10 +246,12 @@ fn _check_on_erc721_received( === Receiving tokens :erc165-discussion: https://github.com/OpenZeppelin/cairo-contracts/discussions/100[this discussion] -:src-6: https://community.starknet.io/t/snip-starknet-standard-account/95665[SRC-6 in Starknet Shamans] -:src-5: xref:introspection.adoc#src5[SRC-5] +:src5: xref:introspection.adoc#src5[SRC5] +:on_erc721_received-api: xref:api/erc721.adoc#IERC721Receiver-on_erc721_received[on_erc721_received] +:computing-interface-id: xref:introspection.adoc#computing_the_interface_id[Computing the interface ID] -In order to be sure a non-account contract can safely accept ERC721 tokens, said contract must implement both the `IERC721Receiver` interface (as expressed in the EIP721 specification) and the `ISRC5` interface which supports introspection. +In order to be sure a non-account contract can safely accept ERC721 tokens, said contract must implement the `IERC721Receiver` interface. +The recipient contract must also implement the {src5} interface which, as described earlier, supports interface introspection. ==== IERC721Receiver @@ -259,21 +267,11 @@ trait IERC721Receiver { } ---- -Implementing the `IERC721Receiver` interface exposes the `on_erc721_received` method. +Implementing the `IERC721Receiver` interface exposes the {on_erc721_received-api} method. When safe methods such as `safe_transfer_from` and `_safe_mint` are called, they invoke the recipient contract's `on_erc721_received` method which *must* return the IERC721Receiver interface ID. Otherwise, the transaction will fail. -==== ISRC-5 - -[,javascript] ----- -trait ISRC5 { - fn supports_interface(interface_id: felt252) -> bool; -} ----- - -The `ISRC5` interface allows the safe methods to query if the recipient supports the `IERC721Receiver` interface ID. -See {introspection} for more information. +TIP: For information on how to calculate interface IDs, see {computing-interface-id}. ==== Creating a token receiver contract @@ -297,7 +295,7 @@ mod ERC721Receiver { SRC5::InternalImpl::register_interface(ref unsafe_state, interface::IERC721_RECEIVER_ID); } - /// Implement the ISRC-5 interface so the sender contract can query + /// Implement the ISRC5 interface so the sender contract can query /// if the recipient supports the token receiver interface ID. #[external(v0)] impl ISRC5Impl of ISRC5 { From 386a20490fc70a9e78f90a5633d87d634af314f7 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 19 Sep 2023 19:59:04 -0400 Subject: [PATCH 142/246] remove presets and extensions --- docs/modules/ROOT/pages/erc721.adoc | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 012112695..09e01541a 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -330,26 +330,3 @@ Each field element equates to 252-bits (or 31.5 bytes) which means that a token' NOTE: Native string support in Cairo is currently in progress and tracked {string-roadmap}. Once Cairo offers full string support, this will be revisited. - -== Presets - -ERC721 presets have been created to allow for quick deployments as-is which are a great option for testing and prototyping. - -== Extensions - -ERC721 includes the optional <> extension as well as other forthcoming extensions. - -=== ERC721Metadata - -[,javascript] ----- -trait IERC721Metadata { - fn name() -> felt252; - fn symbol() -> felt252; - fn token_uri(token_id: u256) -> felt252; -} ----- - -The `ERC721Metadata` extension allows a smart contract to be interrogated for its name and for details about the assets which the NFTs represent. - -Contracts for Cairo follows the Solidity Contracts approach of integrating the metadata methods `name`, `symbol`, and `token_uri` (`tokenURI` in Solidity) into all ERC721 implementations. From e70ba71354c7fbc3df3d749aa122f66047755e0e Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 19 Sep 2023 20:12:18 -0400 Subject: [PATCH 143/246] tidy up usage --- docs/modules/ROOT/pages/erc721.adoc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 09e01541a..77f634248 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -122,9 +122,6 @@ The design for `SRC5` is similar to OpenZeppelin's {erc165-storage}. == Usage :components: https://community.starknet.io/t/cairo-1-contract-syntax-is-evolving/94794#extensibility-and-components-11[Components] -:name-api: xref:api/erc721.adoc#IERC721Metadata-name[name] -:symbol-api: xref:api/erc721.adoc#IERC721Metadata-symbol[symbol] -:token_uri-api: xref:api/erc721.adoc#IERC721Metadata-token_uri[token_uri] WARNING: The following example uses a contract's `unsafe_new_contract_state` to access another contract's state. This is currently unsafe, because storage members could clash among used contracts if not reviewed carefully. @@ -203,7 +200,7 @@ With this access, the constructor first calls the initializer to set the NFT nam The constructor then calls `_mint` to create a one-of-one NFT. Finally, the constructor sets the token URI. Below the constructor, this contract includes two implementations: `IERC721` and `IERC721Metadata`. -`IERC721Metadata` isn't technically required to create an ERC721 contract; however, most contracts include the metadata interface which exposes {name-api}, {symbol-api}, and {token_uri-api}. +`IERC721Metadata` isn't technically required to create an ERC721 contract; however, most contracts include the metadata interface. === Token Transfers From 1f6b1a15338418ff833ffdf0aaa9c46c5dd4a279 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 19 Sep 2023 20:37:14 -0400 Subject: [PATCH 144/246] fix heading --- docs/modules/ROOT/pages/erc721.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 77f634248..faa6560bd 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -10,7 +10,7 @@ The `erc721.cairo` contract implements an approximation of {eip721} in Cairo for TIP: For detailed information on the usage and implementation check the {erc721-api} section. -== Interfaces +== Interface :compatibility: xref:/erc721.adoc#erc721_compatibility[ERC721 Compatibility] :ierc721-interface: xref:/erc721.adoc#ierc721[IERC721] @@ -63,7 +63,7 @@ trait IERC721 { :eip165: https://eips.ethereum.org/EIPS/eip-165[EIP-165] `ISRC5`, from the {snip5} standard, allows for other contracts to query if the implementing contract supports a specific interface. -This is especially important with NFT contracts because if an NFT is transferred to a contract that does not support the `IERC721` interface, the NFT is essentially lost forever. +This is especially important with NFT contracts because if an NFT is transferred to a contract that does not support the `IERC721` interface, the NFT may be lost forever. To mitigate this risk, the `ISRC5` interface allows for interface introspection which helps facilitate safe token transfers. TIP: `ISRC5` is very similar to Ethereum's {eip165}. From ee1efbd5981cbcc7c44564ded074022df04c9f05 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 19 Sep 2023 20:46:40 -0400 Subject: [PATCH 145/246] add more api links --- docs/modules/ROOT/pages/erc721.adoc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index faa6560bd..5da6acc37 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -122,6 +122,7 @@ The design for `SRC5` is similar to OpenZeppelin's {erc165-storage}. == Usage :components: https://community.starknet.io/t/cairo-1-contract-syntax-is-evolving/94794#extensibility-and-components-11[Components] +:mint-api: xref:api/erc721.adoc#ERC721-_mint[_mint] WARNING: The following example uses a contract's `unsafe_new_contract_state` to access another contract's state. This is currently unsafe, because storage members could clash among used contracts if not reviewed carefully. @@ -197,7 +198,7 @@ mod MyNFT { In order for the `MyNFT` contract to extend the ERC721 contract, it utilizes the `unsafe_new_contract_state`. The unsafe contract state allows access to ERC721's storage. With this access, the constructor first calls the initializer to set the NFT name and symbol. -The constructor then calls `_mint` to create a one-of-one NFT. Finally, the constructor sets the token URI. +The constructor then calls {mint-api} to create a one-of-one NFT. Finally, the constructor sets the token URI. Below the constructor, this contract includes two implementations: `IERC721` and `IERC721Metadata`. `IERC721Metadata` isn't technically required to create an ERC721 contract; however, most contracts include the metadata interface. @@ -246,6 +247,9 @@ fn _check_on_erc721_received( :src5: xref:introspection.adoc#src5[SRC5] :on_erc721_received-api: xref:api/erc721.adoc#IERC721Receiver-on_erc721_received[on_erc721_received] :computing-interface-id: xref:introspection.adoc#computing_the_interface_id[Computing the interface ID] +:safe_transfer_from-api: xref:api/erc721.adoc#IERC721-safe_transfer_from[safe_transfer_from] +:safe_mint-api: xref:api/erc721.adoc#ERC721-_safe_mint[_safe_mint] + In order to be sure a non-account contract can safely accept ERC721 tokens, said contract must implement the `IERC721Receiver` interface. The recipient contract must also implement the {src5} interface which, as described earlier, supports interface introspection. @@ -265,7 +269,7 @@ trait IERC721Receiver { ---- Implementing the `IERC721Receiver` interface exposes the {on_erc721_received-api} method. -When safe methods such as `safe_transfer_from` and `_safe_mint` are called, they invoke the recipient contract's `on_erc721_received` method which *must* return the IERC721Receiver interface ID. +When safe methods such as {safe_transfer_from-api} and {safe_mint-api} are called, they invoke the recipient contract's `on_erc721_received` method which *must* return the IERC721Receiver interface ID. Otherwise, the transaction will fail. TIP: For information on how to calculate interface IDs, see {computing-interface-id}. From d389d0e45e0855432fb81fff4edb191c4686cd83 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 20 Sep 2023 13:15:01 -0400 Subject: [PATCH 146/246] add src5 link --- docs/modules/ROOT/pages/api/erc721.adoc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 35d8fc7a1..55720c269 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -324,12 +324,14 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi ==== Functions +:src5: xref:introspection.adoc#src5[SRC5] + [.contract-item] [[ERC721-constructor]] ==== `[.contract-item-name]#++constructor++#++(ref self: ContractState, name: felt252, symbol: felt252)++` [.item-kind]#constructor# Initializes the state of the ERC721 contract by setting the token name and symbol. -The constructor also registers the IERC721 and IERC721_METADATA interface ids according to SRC-5. +The constructor also registers the IERC721 and IERC721_METADATA interface ids according to {src5}. [.contract-item] [[ERC721-tokenURI]] From cbb5bf55631c8aafecb8bbefbf92a289e6b6bb77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Wed, 20 Sep 2023 16:21:23 -0300 Subject: [PATCH 147/246] Add Interface & Dispatchers docs (#730) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: update format and add api * fix: typo * feat: add counterfactual deployment doc * feat: add API entries * feat: add events * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Andrew Fleming * feat: update from reviews * feat: apply review updates * feat: update docs * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * feat: apply review updates * fix: account casing * feat: add headers * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * feat: add link * feat: move API * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * refactor: update wording * Update docs/antora.yml Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * feat: apply update reviews * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * refactor: UI * fix: UI * feat: focus on SRC6 * add interface & dispatchers docs * apply review feedback * address feedback comments --------- Co-authored-by: Eric Nordelo Co-authored-by: Andrew Fleming --- docs/modules/ROOT/nav.adoc | 25 ++-- docs/modules/ROOT/pages/interfaces.adoc | 185 ++++++++++++++++++++++++ 2 files changed, 199 insertions(+), 11 deletions(-) create mode 100644 docs/modules/ROOT/pages/interfaces.adoc diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index d78f8a2bd..380822723 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -1,7 +1,8 @@ * xref:index.adoc[Overview] -* xref:wizard.adoc[Wizard] -* xref:extensibility.adoc[Extensibility] -* xref:proxies.adoc[Proxies and Upgrades] +//* xref:wizard.adoc[Wizard] +//* xref:extensibility.adoc[Extensibility] +//* xref:proxies.adoc[Proxies and Upgrades] +* xref:interfaces.adoc[Interfaces and Dispatchers] * xref:accounts.adoc[Accounts] ** xref:/guides/deployment.adoc[Counterfactual deployments] @@ -9,14 +10,16 @@ * xref:access.adoc[Access Control] -* Tokens -** xref:erc20.adoc[ERC20] -** xref:erc721.adoc[ERC721] -** xref:erc1155.adoc[ERC1155] +//* xref:access.adoc[Access Control] -* xref:security.adoc[Security] -* xref:introspection.adoc[Introspection] -* xref:udc.adoc[Universal Deployer Contract] -* xref:utilities.adoc[Utilities] +//* Tokens +//** xref:erc20.adoc[ERC20] +//** xref:erc721.adoc[ERC721] +//** xref:erc1155.adoc[ERC1155] + +//* xref:security.adoc[Security] +//* xref:introspection.adoc[Introspection] +//* xref:udc.adoc[Universal Deployer Contract] +//* xref:utilities.adoc[Utilities] * xref:contracts::index.adoc[Contracts for Solidity] diff --git a/docs/modules/ROOT/pages/interfaces.adoc b/docs/modules/ROOT/pages/interfaces.adoc new file mode 100644 index 000000000..44125f15c --- /dev/null +++ b/docs/modules/ROOT/pages/interfaces.adoc @@ -0,0 +1,185 @@ +:great-interface-migration: link:https://community.starknet.io/t/the-great-interface-migration/92107[Great Interface Migration] + += Interfaces and Dispatchers + +This section describes the interfaces OpenZeppelin Contracts for Cairo offer, and explains the design choices behind them. + +Interfaces can be found in the module tree under the `interface` submodule, such as `token::erc20::interface`. For example: + +```javascript +use openzeppelin::token::erc20::interface::IERC20; +``` + +or + +```javascript +use openzeppelin::token::erc20::dual20::DualCaseERC20; +``` + +NOTE: For simplicity, we'll use ERC20 as example but the same concepts apply to other modules. + +== Interface traits +The library offers three types of traits to implement or interact with contracts: + +=== Standard traits + +These are associated with a predefined interface such as a standard. +This includes only the functions defined in the interface, and is the standard way to interact with a compliant contract. + +```javascript +#[starknet::interface] +trait IERC20 { + fn name(self: @TState) -> felt252; + fn symbol(self: @TState) -> felt252; + fn decimals(self: @TState) -> u8; + fn total_supply(self: @TState) -> u256; + fn balance_of(self: @TState, account: ContractAddress) -> u256; + fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256; + fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool; + fn transfer_from( + ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool; + fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool; +} +``` + +=== ABI traits + +They describe a contract's complete interface. This is useful to interface with a preset contract offered by this library, such as the ERC20 preset that includes non-standard functions like `increase_allowance`. + +```javascript +#[starknet::interface] +trait ERC20ABI { + fn name(self: @TState) -> felt252; + fn symbol(self: @TState) -> felt252; + fn decimals(self: @TState) -> u8; + fn total_supply(self: @TState) -> u256; + fn balance_of(self: @TState, account: ContractAddress) -> u256; + fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256; + fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool; + fn transfer_from( + ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool; + fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool; + fn increase_allowance(ref self: TState, spender: ContractAddress, added_value: u256) -> bool; + fn decrease_allowance( + ref self: TState, spender: ContractAddress, subtracted_value: u256 + ) -> bool; +} +``` + +=== Dispatcher traits +This is a utility trait to interface with contracts whose interface is unknown. Read more in the xref:#dualcase_dispatchers[DualCase Dispatchers] section. + +```javascript +#[derive(Copy, Drop)] +struct DualCaseERC20 { + contract_address: ContractAddress +} + +trait DualCaseERC20Trait { + fn name(self: @DualCaseERC20) -> felt252; + fn symbol(self: @DualCaseERC20) -> felt252; + fn decimals(self: @DualCaseERC20) -> u8; + fn total_supply(self: @DualCaseERC20) -> u256; + fn balance_of(self: @DualCaseERC20, account: ContractAddress) -> u256; + fn allowance(self: @DualCaseERC20, owner: ContractAddress, spender: ContractAddress) -> u256; + fn transfer(self: @DualCaseERC20, recipient: ContractAddress, amount: u256) -> bool; + fn transfer_from( + self: @DualCaseERC20, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool; + fn approve(self: @DualCaseERC20, spender: ContractAddress, amount: u256) -> bool; +} +``` + +== Dual interfaces + +Following the {great-interface-migration} plan, we added `snake_case` functions to all of our preexisting `camelCase` contracts with the goal of eventually dropping support for the latter. + +In short, we offer two types of interfaces and utilities to handle them: + +1. `camelCase` interfaces, which are the ones we've been using so far. +2. `snake_case` interfaces, which are the ones we're migrating to. + +This means that currently most of our contracts implement _dual interfaces_. For example, the ERC20 preset contract exposes `transferFrom`, `transfer_from`, `balanceOf`, `balance_of`, etc. + +NOTE: Dual interfaces are available for all external functions present in previous versions of OpenZeppelin Contracts for Cairo (https://github.com/OpenZeppelin/cairo-contracts/releases/tag/v0.6.1[v0.6.1] and below). + +=== `IERC20` + +The default version of the ERC20 interface trait exposes `snake_case` functions: + +```javascript +#[starknet::interface] +trait IERC20 { + fn name(self: @TState) -> felt252; + fn symbol(self: @TState) -> felt252; + fn decimals(self: @TState) -> u8; + fn total_supply(self: @TState) -> u256; + fn balance_of(self: @TState, account: ContractAddress) -> u256; + fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256; + fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool; + fn transfer_from( + ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool; + fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool; +} +``` + +=== `IERC20Camel` + +On top of that, we also offer a `camelCase` version of the same interface: + +```javascript +#[starknet::interface] +trait IERC20Camel { + fn name(self: @TState) -> felt252; + fn symbol(self: @TState) -> felt252; + fn decimals(self: @TState) -> u8; + fn totalSupply(self: @TState) -> u256; + fn balanceOf(self: @TState, account: ContractAddress) -> u256; + fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256; + fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool; + fn transferFrom( + ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool; + fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool; +} +``` + +== `DualCase` dispatchers + +WARNING: `DualCase` dispatchers won't work on live chains (`mainnet` or testnets) until they implement panic handling in their runtime. Dispatchers work fine in testing environments. + +In order to ease this transition, OpenZeppelin Contracts for Cairo offer what we call `DualCase` dispatchers such as `DualCaseERC721` or `DualCaseAccount`. + +These modules wrap a target contract with a compatibility layer to expose a `snake_case` interface no matter what casing the underlying contract uses. +This way, an AMM wouldn't have problems integrating tokens independently of their interface. + +For example: + +```javascript +let token = DualCaseERC20 { contract_address: target }; +token.transfer_from(OWNER(), RECIPIENT(), VALUE); +``` + +This is done by simply executing the `snake_case` version of the function (e.g. `transfer_from`) and falling back to the `camelCase` one (e.g. `transferFrom`) in case it reverts with `ENTRYPOINT_NOT_FOUND`, like this: + +```javascript +fn try_selector_with_fallback( + target: ContractAddress, snake_selector: felt252, camel_selector: felt252, args: Span +) -> SyscallResult> { + match call_contract_syscall(target, snake_selector, args) { + Result::Ok(ret) => Result::Ok(ret), + Result::Err(errors) => { + if *errors.at(0) == 'ENTRYPOINT_NOT_FOUND' { + return call_contract_syscall(target, camel_selector, args); + } else { + Result::Err(errors) + } + } + } +} +``` + +Trying the `snake_case` interface first renders `camelCase` calls a bit more expensive since a failed `snake_case` call will always happen before. This is a design choice to incentivize casing adoption/transition as per the {great-interface-migration}. From 4e388f57bb93e2be9ad7193255535c6a446a3e05 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 21 Sep 2023 00:44:38 +0200 Subject: [PATCH 148/246] Update overview docs (#735) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: update format and add api * fix: typo * feat: add counterfactual deployment doc * feat: add API entries * feat: add events * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Andrew Fleming * feat: update from reviews * feat: apply review updates * feat: update docs * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * feat: apply review updates * fix: account casing * feat: add headers * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * feat: add link * feat: move API * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * refactor: update wording * Update docs/antora.yml Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * feat: apply update reviews * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * refactor: UI * fix: UI * feat: focus on SRC6 * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * feat: update overview * feat: apply review updates * Update docs/modules/ROOT/pages/index.adoc Co-authored-by: Martín Triay * feat: apply review updates * Update docs/modules/ROOT/pages/index.adoc Co-authored-by: Andrew Fleming * feat: apply review updates * Update docs/modules/ROOT/pages/index.adoc Co-authored-by: Andrew Fleming --------- Co-authored-by: Andrew Fleming Co-authored-by: Martín Triay --- docs/modules/ROOT/nav.adoc | 20 ++-- docs/modules/ROOT/pages/index.adoc | 184 +++++++++++++++++------------ 2 files changed, 118 insertions(+), 86 deletions(-) diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 380822723..961aba815 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -8,18 +8,16 @@ ** xref:/guides/deployment.adoc[Counterfactual deployments] ** xref:/api/account.adoc[API Reference] -* xref:access.adoc[Access Control] +// * xref:access.adoc[Access Control] -//* xref:access.adoc[Access Control] +// * Tokens +// ** xref:erc20.adoc[ERC20] +// ** xref:erc721.adoc[ERC721] +// ** xref:erc1155.adoc[ERC1155] -//* Tokens -//** xref:erc20.adoc[ERC20] -//** xref:erc721.adoc[ERC721] -//** xref:erc1155.adoc[ERC1155] - -//* xref:security.adoc[Security] -//* xref:introspection.adoc[Introspection] -//* xref:udc.adoc[Universal Deployer Contract] -//* xref:utilities.adoc[Utilities] +// * xref:security.adoc[Security] +// * xref:introspection.adoc[Introspection] +// * xref:udc.adoc[Universal Deployer Contract] +// * xref:utilities.adoc[Utilities] * xref:contracts::index.adoc[Contracts for Solidity] diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index 648763e0c..aa01b689a 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -1,115 +1,149 @@ -= Contracts for Cairo +:starknet: https://starkware.co/product/starknet/[Starknet] +:scarb: https://docs.swmansion.com/scarb[Scarb] +:installation: https://docs.swmansion.com/scarb/download.html[this guide] -*A library for secure smart contract development* written in Cairo for https://starkware.co/product/starknet/[StarkNet], a decentralized ZK Rollup. += Contracts for Cairo -== Usage +*A library for secure smart contract development* written in Cairo for {starknet}, a decentralized ZK Rollup. WARNING: This repo contains highly experimental code. Expect rapid iteration. *Use at your own risk.* -=== First time? +== Installation -Before installing Cairo on your machine, you need to install `gmp`: +The library is available as a {scarb} package. Follow {installation} for installing Cairo and Scarb on your machine +before proceeding, and run the following command to check that the installation was successful: [,bash] ---- -sudo apt install -y libgmp3-dev # linux -brew install gmp # mac ----- +$ scarb --version -TIP: If you have any trouble installing gmp on your Apple M1 computer, https://github.com/OpenZeppelin/nile/issues/22[here's a list of potential solutions]. +scarb 0.7.0 (58cc88efb 2023-08-23) +cairo: 2.2.0 (https://crates.io/crates/cairo-lang-compiler/2.2.0) +sierra: 1.3.0 +---- === Set up your project -Create a directory for your project, then `cd` into it and create a Python virtual environment. +Create an empty directory, and `cd` into it: [,bash] ---- -mkdir my-project -cd my-project -python3 -m venv env -source env/bin/activate +mkdir my_project/ && cd my_project/ ---- -Install the https://github.com/OpenZeppelin/nile[Nile] development environment and then run `init` to kickstart a new project. -Nile will create the project directory structure and install https://www.cairo-lang.org/docs/quickstart.html[the Cairo language], a https://github.com/Shard-Labs/starknet-devnet/[local network], and a https://docs.pytest.org/en/6.2.x/[testing framework]. +Initialize a new Scarb project: [,bash] ---- -pip install cairo-nile -nile init +scarb init ---- -=== Install the library +The contents of `my_project/` should now look like this: [,bash] ---- -pip install openzeppelin-cairo-contracts ----- - -WARNING: Installing directly through GitHub may contain incomplete or breaking implementations. -While we aim not to introduce such changes, we still strongly recommend installing through https://github.com/OpenZeppelin/cairo-contracts/releases/[official releases]. - -=== Use a basic preset +$ ls -Presets are ready-to-use contracts that you can deploy right away. -They also serve as examples of how to use library modules. -xref:extensibility.adoc#presets[Read more about presets]. - -[,cairo] ----- -// contracts/MyToken.cairo - -%lang starknet - -from openzeppelin.token.erc20.presets.ERC20 import ( - constructor, - name, - symbol, - totalSupply, - decimals, - balanceOf, - allowance, - transfer, - transferFrom, - approve, - increaseAllowance, - decreaseAllowance -) +Scarb.toml src ---- -Compile and deploy it right away: +=== Install the library -[,bash] ----- -nile compile +Install the library by declaring it as a dependency in the project's `Scarb.toml` file: -nile deploy MyToken --alias my_token +[,text] +---- +[dependencies] +openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.7.0" } ---- -NOTE: `` is expected to be two integers i.e. -`1` `0`. -See xref:utilities.adoc#uint256[uint256] for more information. +WARNING: Make sure the tag matches the target release. -=== Write a custom contract using library modules +== Basic usage -xref:extensibility.adoc#libraries[Read more about libraries]. +This is how it looks to build an account contract using the xref:accounts.adoc[account module]. +Copy the code into `src/lib.cairo`. -[,cairo] +[,javascript] +---- +#[starknet::contract] +mod MyAccount { + use openzeppelin::account::Account; + use openzeppelin::account::account::PublicKeyTrait; + use openzeppelin::account::interface; + use openzeppelin::introspection::interface::ISRC5; + use starknet::account::Call; + + // Storage members used by this contract are defined in each imported + // module whose `unsafe_state` is used. This design will be improved + // with the addition of components in the future. + #[storage] + struct Storage {} + + #[constructor] + fn constructor(ref self: ContractState, public_key: felt252) { + let mut unsafe_state = _unsafe_state(); + Account::InternalImpl::initializer(ref unsafe_state, public_key); + } + + #[external(v0)] + impl SRC6Impl of interface::ISRC6 { + fn __execute__(self: @ContractState, mut calls: Array) -> Array> { + Account::SRC6Impl::__execute__(@_unsafe_state(), calls) + } + + fn __validate__(self: @ContractState, mut calls: Array) -> felt252 { + Account::SRC6Impl::__validate__(@_unsafe_state(), calls) + } + + fn is_valid_signature( + self: @ContractState, hash: felt252, signature: Array + ) -> felt252 { + Account::SRC6Impl::is_valid_signature(@_unsafe_state(), hash, signature) + } + } + + #[external(v0)] + impl SRC5Impl of ISRC5 { + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { + Account::SRC5Impl::supports_interface(@_unsafe_state(), interface_id) + } + } + + #[external(v0)] + impl PublicKeyImpl of PublicKeyTrait { + fn get_public_key(self: @ContractState) -> felt252 { + Account::PublicKeyImpl::get_public_key(@_unsafe_state()) + } + + fn set_public_key(ref self: ContractState, new_public_key: felt252) { + let mut unsafe_state = _unsafe_state(); + Account::PublicKeyImpl::set_public_key(ref unsafe_state, new_public_key); + } + } + + #[external(v0)] + fn __validate_deploy__( + self: @ContractState, + class_hash: felt252, + contract_address_salt: felt252, + _public_key: felt252 + ) -> felt252 { + Account::__validate_deploy__( + @_unsafe_state(), class_hash, contract_address_salt, _public_key + ) + } + + #[inline(always)] + fn _unsafe_state() -> Account::ContractState { + Account::unsafe_new_contract_state() + } +} ---- -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.uint256 import Uint256 -from openzeppelin.security.pausable.library import Pausable -from openzeppelin.token.erc20.library import ERC20 -(...) +You can now compile it: -@external -func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - recipient: felt, amount: Uint256 -) -> (success: felt) { - Pausable.assert_not_paused(); - return ERC20.transfer(recipient, amount); -} +[,bash] ---- +scarb build +---- \ No newline at end of file From 188b1aecd0d1b2bfc01ceac98a8b77437d53228b Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 20 Sep 2023 20:56:13 -0400 Subject: [PATCH 149/246] add _mint_with_uri to example --- docs/modules/ROOT/pages/erc721.adoc | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 5da6acc37..1bdeb77fe 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -156,10 +156,8 @@ mod MyNFT { let mut unsafe_state = ERC721::unsafe_new_contract_state(); // Initialize the ERC721 storage ERC721::InternalImpl::initializer(ref unsafe_state, name, symbol); - // Mint the NFT to recipient - ERC721::InternalImpl::_mint(ref unsafe_state, recipient, token_id); - // Set the token's URI - ERC721::InternalImpl::_set_token_uri(ref unsafe_state, token_id, token_uri); + // Mint the NFT to recipient and set the token's URI + _mint_with_uri(recipient, token_id, token_uri); } /// Implement the ISRC5 interface. @@ -192,16 +190,24 @@ mod MyNFT { (...) } + + #[internal] + fn _mint_with_uri( + recipient: ContractAddress, + token_id: u256, + token_uri: felt252 + ) { + let mut unsafe_state = ERC721::unsafe_new_contract_state(); + ERC721::InternalImpl::_mint(ref unsafe_state, recipient, token_id); + ERC721::InternalImpl::_set_token_uri(ref unsafe_state, token_id, token_uri); + } } ---- In order for the `MyNFT` contract to extend the ERC721 contract, it utilizes the `unsafe_new_contract_state`. The unsafe contract state allows access to ERC721's storage. With this access, the constructor first calls the initializer to set the NFT name and symbol. -The constructor then calls {mint-api} to create a one-of-one NFT. Finally, the constructor sets the token URI. - -Below the constructor, this contract includes two implementations: `IERC721` and `IERC721Metadata`. -`IERC721Metadata` isn't technically required to create an ERC721 contract; however, most contracts include the metadata interface. +Next, the constructor calls the custom internal function `_mint_with_uri` that mints a one-of-one NFT and sets the URI for the minted token ID. === Token Transfers From 60f632d3061877d9da71bd3970c7843ba2e9e1ff Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Wed, 20 Sep 2023 20:58:53 -0400 Subject: [PATCH 150/246] Apply suggestions from code review Co-authored-by: Eric Nordelo --- docs/modules/ROOT/pages/erc721.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 1bdeb77fe..6d1f4a1de 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -63,7 +63,7 @@ trait IERC721 { :eip165: https://eips.ethereum.org/EIPS/eip-165[EIP-165] `ISRC5`, from the {snip5} standard, allows for other contracts to query if the implementing contract supports a specific interface. -This is especially important with NFT contracts because if an NFT is transferred to a contract that does not support the `IERC721` interface, the NFT may be lost forever. +This is especially important with NFT contracts because if an NFT is transferred to a contract that does not support the `IERC721Receiver` interface, the NFT may be lost forever. To mitigate this risk, the `ISRC5` interface allows for interface introspection which helps facilitate safe token transfers. TIP: `ISRC5` is very similar to Ethereum's {eip165}. From 4b48d2afa399c4240f2014c362c241ada9daac84 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 20 Sep 2023 22:43:00 -0400 Subject: [PATCH 151/246] add headings --- docs/modules/ROOT/pages/api/erc721.adoc | 112 +++++++++++++++++++++--- 1 file changed, 100 insertions(+), 12 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 55720c269..fb49fe968 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -1,6 +1,6 @@ :github-icon: pass:[] :eip721: https://eips.ethereum.org/EIPS/eip-721[EIP721] -:erc721-receiver: xref:/erc721.adoc#receiving_tokens[ERC721Receiver] +:receiving-tokens: xref:/erc721.adoc#receiving_tokens[Receiving Tokens] :casing-discussion: https://github.com/OpenZeppelin/cairo-contracts/discussions/34[here] = ERC721 @@ -90,7 +90,7 @@ Requirements: Safely transfer ownership of `token_id` from `from` to `to`, checking first that `to` is aware of the ERC721 protocol to prevent tokens being locked forever. For information regarding how contracts communicate their -awareness of the ERC721 protocol, see {erc721-receiver}. +awareness of the ERC721 protocol, see {receiving-tokens}. Emits a <> event. @@ -243,6 +243,11 @@ use openzeppelin::token::erc721::ERC721; Implementation of ERC721 which includes the IERC721Metadata extension as specified in https://eips.ethereum.org/EIPS/eip-721[EIP-721]. +[.contract-index] +.Constructor + +* xref:#ERC721-constructor[`++constructor(self, name, symbol)++`] + [.contract-index] .External Functions -- @@ -292,8 +297,6 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi .Internal Functions -- -* xref:#ERC721-constructor[`++constructor(self, name, symbol)++`] - [.contract-subindex-inherited] .InternalImpl @@ -322,7 +325,7 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi * xref:#IERC721-Transfer[`++Transfer(from, to, token_id)++`] -- -==== Functions +==== Constructor :src5: xref:introspection.adoc#src5[SRC5] @@ -333,6 +336,74 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi Initializes the state of the ERC721 contract by setting the token name and symbol. The constructor also registers the IERC721 and IERC721_METADATA interface ids according to {src5}. +==== External functions + +[.contract-item] +[[ERC721-name]] +==== `[.contract-item-name]#++name++#++(self: @ContractState) -> felt252++` [.item-kind]#external# + +See <>. + +[.contract-item] +[[ERC721-symbol]] +==== `[.contract-item-name]#++symbol++#++(self: @ContractState) -> felt252++` [.item-kind]#external# + +See <>. + +[.contract-item] +[[ERC721-token_uri]] +==== `[.contract-item-name]#++token_uri++#++(self: @ContractState, token_id: u256) -> felt252++` [.item-kind]#external# + +See <>. + +[.contract-item] +[[ERC721-balance_of]] +==== `[.contract-item-name]#++balance_of++#++(self: @ContractState, account: ContractAddress) → u256++` [.item-kind]#external# + +See <>. + +[.contract-item] +[[ERC721-owner_of]] +==== `[.contract-item-name]#++owner_of++#++(self: @ContractState, token_id: u256) → ContractAddress++` [.item-kind]#external# + +See <>. + +[.contract-item] +[[ERC721-transfer_from]] +==== `[.contract-item-name]#++transfer_from++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256)++` [.item-kind]#external# + +See <>. + +[.contract-item] +[[ERC721-safe_transfer_from]] +==== `[.contract-item-name]#++safe_transfer_from++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#external# + +See <>. + +[.contract-item] +[[ERC721-approve]] +==== `[.contract-item-name]#++approve++#++(ref self: ContractState, to: ContractAddress, token_id: u256)++` [.item-kind]#external# + +See <>. + +[.contract-item] +[[ERC721-set_approval_for_all]] +==== `[.contract-item-name]#++set_approval_for_all++#++(ref self: ContractState, operator: ContractAddress, approved: bool)++` [.item-kind]#external# + +See <>. + +[.contract-item] +[[ERC721-get_approved]] +==== `[.contract-item-name]#++get_approved++#++(self: @ContractState, token_id: u256) -> u256++` [.item-kind]#external# + +See <>. + +[.contract-item] +[[ERC721-is_approved_for_all]] +==== `[.contract-item-name]#++is_approved_for_all++#++(self: @ContractState, owner: ContractAddress, operator: ContractAddress) -> bool++` [.item-kind]#external# + +See <>. + [.contract-item] [[ERC721-tokenURI]] ==== `[.contract-item-name]#++tokenURI++#++(self: @ContractState, tokenId: u256) -> felt252++` [.item-kind]#external# @@ -399,6 +470,8 @@ See <>. Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. +==== Internal functions + [.contract-item] [[ERC721-initializer]] ==== `[.contract-item-name]#++initializer++#++(ref self: ContractState, name_: felt252, symbol_: felt252)++` [.item-kind]#internal# @@ -442,7 +515,7 @@ Emits an <> event. Requirements: - `token_id` exists. -`to` is not the current token owner. +- `to` is not the current token owner. [.contract-item] [[ERC721-_set_approval_for_all]] @@ -511,13 +584,8 @@ Emits an <> event. Requirements: -`token_id` exists. -- `to` is either an account contract or supports the <> interface. - -Requirements: - - `token_id` exists. -- `to` either is an account contract or supports the <> interface. +- `to` is either an account contract or supports the <> interface. [.contract-item] [[ERC721-_safe_transfer]] @@ -548,6 +616,26 @@ Requirements: - `token_id` exists. +==== Events + +[.contract-item] +[[IERC721-Approval]] +==== `[.contract-item-name]#++Approval++#++(owner: ContractAddress, approved: ContractAddress, token_id: u256)++` [.item-kind]#event# + +See <>. + +[.contract-item] +[[IERC721-ApprovalForAll]] +==== `[.contract-item-name]#++ApprovalForAll++#++(owner: ContractAddress, operator: ContractAddress, approved: bool)++` [.item-kind]#event# + +See <>. + +[.contract-item] +[[IERC721-Transfer]] +==== `[.contract-item-name]#++Transfer++#++(from: ContractAddress, to: ContractAddress, token_id: u256)++` [.item-kind]#event# + +See <>. + [.contract] [[IERC721Receiver]] === `++IERC721Receiver++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/cairo-2/src/token/erc721/interface.cairo#L70-L79[{github-icon},role=heading-link] From a1b559c46eaa48d855b049134fd294ce43ee44e7 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Thu, 21 Sep 2023 19:05:33 -0400 Subject: [PATCH 152/246] remove underscore from name_ and symbol_ (#738) --- src/token/erc20/erc20.cairo | 6 +++--- src/token/erc721/erc721.cairo | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/token/erc20/erc20.cairo b/src/token/erc20/erc20.cairo index df5d1fd25..9a97cee58 100644 --- a/src/token/erc20/erc20.cairo +++ b/src/token/erc20/erc20.cairo @@ -172,9 +172,9 @@ mod ERC20 { #[generate_trait] impl InternalImpl of InternalTrait { - fn initializer(ref self: ContractState, name_: felt252, symbol_: felt252) { - self._name.write(name_); - self._symbol.write(symbol_); + fn initializer(ref self: ContractState, name: felt252, symbol: felt252) { + self._name.write(name); + self._symbol.write(symbol); } fn _increase_allowance( diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 559b57c9c..0373dad36 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -238,9 +238,9 @@ mod ERC721 { #[generate_trait] impl InternalImpl of InternalTrait { - fn initializer(ref self: ContractState, name_: felt252, symbol_: felt252) { - self._name.write(name_); - self._symbol.write(symbol_); + fn initializer(ref self: ContractState, name: felt252, symbol: felt252) { + self._name.write(name); + self._symbol.write(symbol); let mut unsafe_state = src5::SRC5::unsafe_new_contract_state(); src5::SRC5::InternalImpl::register_interface(ref unsafe_state, interface::IERC721_ID); From b18c9d6c5353dfd1fefa49fd9f32e4c09a7727bc Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Mon, 25 Sep 2023 10:15:04 -0400 Subject: [PATCH 153/246] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Martín Triay --- docs/modules/ROOT/pages/erc721.adoc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 6d1f4a1de..3d9c45f17 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -6,7 +6,7 @@ :introspection: xref:/introspection.adoc[Introspection] The ERC721 token standard is a specification for {token-types}, or more colloquially: NFTs. -The `erc721.cairo` contract implements an approximation of {eip721} in Cairo for Starknet. +The `token::erc721::ERC721` contract implements an approximation of {eip721} in Cairo for Starknet. TIP: For detailed information on the usage and implementation check the {erc721-api} section. @@ -21,7 +21,7 @@ Contracts must implement both the {ierc721-interface} and {isrc5-interface} inte Additionally, ERC721 contracts often include {ierc721metadata-interface} interface as well. Contracts for Cairo's implementation includes all three interfaces. -WARNING: The {ierc721-interface} and {ierc721metadata-interface} implementations are approximations of their Ethereum counterparts. +WARNING: The {ierc721-interface} and {ierc721metadata-interface} interfaces are approximations of their Ethereum counterparts. These approximations include a few notable differences. See {compatibility} for more information. @@ -112,7 +112,7 @@ See the {introspection} docs. The difference between both functions consists of accepting `data` as an argument. `safe_transfer_from` by default accepts the `data` argument. If `data` is not used, simply pass an empty array. -* `safe_transfer_from` is specified such that the optional `data` argument should be of type bytes. +* `safe_transfer_from` is implemented such that the optional `data` argument mimics `bytes`. In Solidity, this means a dynamically-sized array. To be as close as possible to the standard, it accepts a dynamic array of felts. * ERC721 utilizes {src5-api} to declare and query interface support on Starknet as opposed to Ethereum's {eip165}. @@ -204,8 +204,8 @@ mod MyNFT { } ---- -In order for the `MyNFT` contract to extend the ERC721 contract, it utilizes the `unsafe_new_contract_state`. -The unsafe contract state allows access to ERC721's storage. +In order for the `MyNFT` contract to extend the `ERC721` contract, it utilizes the `unsafe_new_contract_state`. +The unsafe contract state allows access to `ERC721`'s storage. With this access, the constructor first calls the initializer to set the NFT name and symbol. Next, the constructor calls the custom internal function `_mint_with_uri` that mints a one-of-one NFT and sets the URI for the minted token ID. @@ -332,7 +332,7 @@ mod ERC721Receiver { :string-roadmap: https://github.com/orgs/starkware-libs/projects/1/views/1?pane=issue&itemId=28823165[here] -Token URIs in Cairo are stored as single field elements. +Token URIs in Cairo are stored as single field elements (`felt252`). Each field element equates to 252-bits (or 31.5 bytes) which means that a token's URI can be no longer than 31 characters. NOTE: Native string support in Cairo is currently in progress and tracked {string-roadmap}. From 8e89139748f514f4258a5779d0c11bb78c4d3764 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 26 Sep 2023 03:10:04 -0400 Subject: [PATCH 154/246] normalize formatting --- docs/modules/ROOT/pages/api/erc721.adoc | 52 ++++++++++++------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index fb49fe968..14b446d8f 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -72,7 +72,7 @@ Requirements: Transfer ownership of `token_id` from `from` to `to`. Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 transfers or else they may be permanently lost. -Usage of <> prevents loss, though the caller must understand this adds an external call which potentially creates a reentrancy vulnerability. +Usage of <> prevents loss, though the caller must understand this adds an external call which potentially creates a reentrancy vulnerability. Emits a <> event. @@ -342,73 +342,73 @@ The constructor also registers the IERC721 and IERC721_METADATA interface ids ac [[ERC721-name]] ==== `[.contract-item-name]#++name++#++(self: @ContractState) -> felt252++` [.item-kind]#external# -See <>. +See <>. [.contract-item] [[ERC721-symbol]] ==== `[.contract-item-name]#++symbol++#++(self: @ContractState) -> felt252++` [.item-kind]#external# -See <>. +See <>. [.contract-item] [[ERC721-token_uri]] ==== `[.contract-item-name]#++token_uri++#++(self: @ContractState, token_id: u256) -> felt252++` [.item-kind]#external# -See <>. +See <>. [.contract-item] [[ERC721-balance_of]] ==== `[.contract-item-name]#++balance_of++#++(self: @ContractState, account: ContractAddress) → u256++` [.item-kind]#external# -See <>. +See <>. [.contract-item] [[ERC721-owner_of]] ==== `[.contract-item-name]#++owner_of++#++(self: @ContractState, token_id: u256) → ContractAddress++` [.item-kind]#external# -See <>. +See <>. [.contract-item] [[ERC721-transfer_from]] ==== `[.contract-item-name]#++transfer_from++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256)++` [.item-kind]#external# -See <>. +See <>. [.contract-item] [[ERC721-safe_transfer_from]] ==== `[.contract-item-name]#++safe_transfer_from++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#external# -See <>. +See <>. [.contract-item] [[ERC721-approve]] ==== `[.contract-item-name]#++approve++#++(ref self: ContractState, to: ContractAddress, token_id: u256)++` [.item-kind]#external# -See <>. +See <>. [.contract-item] [[ERC721-set_approval_for_all]] ==== `[.contract-item-name]#++set_approval_for_all++#++(ref self: ContractState, operator: ContractAddress, approved: bool)++` [.item-kind]#external# -See <>. +See <>. [.contract-item] [[ERC721-get_approved]] ==== `[.contract-item-name]#++get_approved++#++(self: @ContractState, token_id: u256) -> u256++` [.item-kind]#external# -See <>. +See <>. [.contract-item] [[ERC721-is_approved_for_all]] ==== `[.contract-item-name]#++is_approved_for_all++#++(self: @ContractState, owner: ContractAddress, operator: ContractAddress) -> bool++` [.item-kind]#external# -See <>. +See <>. [.contract-item] [[ERC721-tokenURI]] ==== `[.contract-item-name]#++tokenURI++#++(self: @ContractState, tokenId: u256) -> felt252++` [.item-kind]#external# -See <>. +See <>. Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. @@ -416,7 +416,7 @@ Supports the Cairo v0 convention of writing external methods in camelCase as dis [[ERC721-balanceOf]] ==== `[.contract-item-name]#++balanceOf++#++(self: @ContractState, account: ContractAddress) -> u256++` [.item-kind]#external# -See <>. +See <>. Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. @@ -424,7 +424,7 @@ Supports the Cairo v0 convention of writing external methods in camelCase as dis [[ERC721-ownerOf]] ==== `[.contract-item-name]#++ownerOf++#++(self: @ContractState, tokenId: u256) -> ContractAddress++` [.item-kind]#external# -See <>. +See <>. Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. @@ -432,7 +432,7 @@ Supports the Cairo v0 convention of writing external methods in camelCase as dis [[ERC721-transferFrom]] ==== `[.contract-item-name]#++transferFrom++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256)++` [.item-kind]#external# -See <>. +See <>. Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. @@ -440,17 +440,15 @@ Supports the Cairo v0 convention of writing external methods in camelCase as dis [[ERC721-safeTransferFrom]] ==== `[.contract-item-name]#++safeTransferFrom++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256, data: Span)++` [.item-kind]#external# -See <>. +See <>. Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. -* xref:#IERC721-setApprovalForAll[`++setApprovalForAll(operator, approved)++`] - [.contract-item] [[ERC721-setApprovalForAll]] ==== `[.contract-item-name]#++setApprovalForAll++#++(ref self: ContractState, operator: ContractAddress, approved: bool)++` [.item-kind]#external# -See <>. +See <>. Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. @@ -458,7 +456,7 @@ Supports the Cairo v0 convention of writing external methods in camelCase as dis [[ERC721-getApproved]] ==== `[.contract-item-name]#++getApproved++#++(self: @ContractState, tokenId: u256) -> ContractAddress++` [.item-kind]#external# -See <>. +See <>. Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. @@ -466,7 +464,7 @@ Supports the Cairo v0 convention of writing external methods in camelCase as dis [[ERC721-isApprovedForAll]] ==== `[.contract-item-name]#++isApprovedForAll++#++(self: @ContractState, owner: ContractAddress, operator: ContractAddress) -> bool++` [.item-kind]#external# -See <>. +See <>. Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. @@ -595,7 +593,7 @@ Internal function that safely transfers `token_id` token from `from` to `to`, ch `data` is additional data, it has no specified format and it is sent in call to `to`. -This function is equivalent to <>, and can be used to e.g. implement alternative mechanisms to perform signature-based token transfers. +This function is equivalent to <>, and can be used to e.g. implement alternative mechanisms to perform signature-based token transfers. Emits an <> event. @@ -622,19 +620,19 @@ Requirements: [[IERC721-Approval]] ==== `[.contract-item-name]#++Approval++#++(owner: ContractAddress, approved: ContractAddress, token_id: u256)++` [.item-kind]#event# -See <>. +See <>. [.contract-item] [[IERC721-ApprovalForAll]] ==== `[.contract-item-name]#++ApprovalForAll++#++(owner: ContractAddress, operator: ContractAddress, approved: bool)++` [.item-kind]#event# -See <>. +See <>. [.contract-item] [[IERC721-Transfer]] ==== `[.contract-item-name]#++Transfer++#++(from: ContractAddress, to: ContractAddress, token_id: u256)++` [.item-kind]#event# -See <>. +See <>. [.contract] [[IERC721Receiver]] @@ -657,5 +655,5 @@ use openzeppelin::token::erc721::interface::IERC721Receiver; [[IERC721Receiver-on_erc721_received]] ==== `[.contract-item-name]#++on_erc721_received++#++(operator: ContractAddress, from: ContractAddress, token_id: u256, data Span)++` [.item-kind]#external# -Whenever an IERC721 `token_id` token is transferred to this non-account contract via <> by `operator` from `from`, this function is called. +Whenever an IERC721 `token_id` token is transferred to this non-account contract via <> by `operator` from `from`, this function is called. From e674f3f126f366cc2c73e141e9232b03eb193460 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 26 Sep 2023 03:11:30 -0400 Subject: [PATCH 155/246] normalize warning --- docs/modules/ROOT/pages/erc721.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 3d9c45f17..18b27319a 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -125,7 +125,7 @@ The design for `SRC5` is similar to OpenZeppelin's {erc165-storage}. :mint-api: xref:api/erc721.adoc#ERC721-_mint[_mint] WARNING: The following example uses a contract's `unsafe_new_contract_state` to access another contract's state. -This is currently unsafe, because storage members could clash among used contracts if not reviewed carefully. +Although this is useful to use them as modules, it's considered unsafe because storage members could clash among used contracts if not reviewed carefully. Extensibility will be revisited after {components} are introduced. Using Contracts for Cairo, constructing an ERC721 contract requires setting up the constructor and exposing the ERC721 interface. From 2353e0ee3b7538e0f6c7541da18854b792395f9c Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 26 Sep 2023 03:31:56 -0400 Subject: [PATCH 156/246] simplify code block --- docs/modules/ROOT/pages/erc721.adoc | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 18b27319a..e0a1219ad 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -220,7 +220,6 @@ This library includes {transfer_from-api} and {safe_transfer_from-api} to transf If using `transfer_from`, *the caller is responsible to confirm that the recipient is capable of receiving NFTs or else they may be permanently lost.* The `safe_transfer_from` method mitigates this risk by querying the recipient contract's interface support. - The safe mechanism first queries if the recipient contract supports the `IERC721Receiver` interface through introspection ({src-5}). If the recipient contract does _not_ support the receiver interface, then the safe mechanism checks if the recipient contract supports the {isrc6} interface, which is the standard account contract interface. If either case is true, then the token transfer behaves as if `transfer_from` was called. @@ -233,16 +232,13 @@ To better visualize the process, see the snippet below. fn _check_on_erc721_received( from: ContractAddress, to: ContractAddress, token_id: u256, data: Span ) -> bool { - // Check if `to` has declared support for IERC721Receiver - if (DualCaseSRC5 { contract_address: to } - .supports_interface(interface::IERC721_RECEIVER_ID)) { - DualCaseERC721Receiver { contract_address: to } - .on_erc721_received( - get_caller_address(), from, token_id, data - ) == interface::IERC721_RECEIVER_ID + if { + // `to` supports ISRC5 && + // `to` has registered support for IERC721Receiver + // return true; } else { - // Check if `to` is an account contract - DualCaseSRC5 { contract_address: to }.supports_interface(account::interface::ISRC6_ID) + // `to` is an account contract + // return true; } } ---- From 74eb4e86702501ba9654eaf4b4330f5737bfe207 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Tue, 26 Sep 2023 20:27:20 +0200 Subject: [PATCH 157/246] Add prefixes to storage members (#743) * feat: add prefixes * Update src/access/accesscontrol/accesscontrol.cairo Co-authored-by: Andrew Fleming --------- Co-authored-by: Andrew Fleming --- src/access/accesscontrol/accesscontrol.cairo | 14 ++--- src/access/ownable/ownable.cairo | 10 ++-- src/account/account.cairo | 12 ++-- src/introspection/src5.cairo | 8 +-- src/security/initializable.cairo | 6 +- src/security/pausable.cairo | 12 ++-- src/security/reentrancyguard.cairo | 8 +-- src/tests/access/test_ownable.cairo | 8 +-- src/tests/security/test_reentrancyguard.cairo | 10 ++-- src/tests/token/test_erc721.cairo | 14 ++--- src/token/erc20/erc20.cairo | 49 ++++++++------- src/token/erc721/erc721.cairo | 60 +++++++++---------- 12 files changed, 108 insertions(+), 103 deletions(-) diff --git a/src/access/accesscontrol/accesscontrol.cairo b/src/access/accesscontrol/accesscontrol.cairo index 90dfe421c..0f3aa7535 100644 --- a/src/access/accesscontrol/accesscontrol.cairo +++ b/src/access/accesscontrol/accesscontrol.cairo @@ -12,8 +12,8 @@ mod AccessControl { #[storage] struct Storage { - role_admin: LegacyMap, - role_members: LegacyMap<(felt252, ContractAddress), bool>, + AccessControl_role_admin: LegacyMap, + AccessControl_role_member: LegacyMap<(felt252, ContractAddress), bool>, } #[event] @@ -82,11 +82,11 @@ mod AccessControl { #[external(v0)] impl AccessControlImpl of interface::IAccessControl { fn has_role(self: @ContractState, role: felt252, account: ContractAddress) -> bool { - self.role_members.read((role, account)) + self.AccessControl_role_member.read((role, account)) } fn get_role_admin(self: @ContractState, role: felt252) -> felt252 { - self.role_admin.read(role) + self.AccessControl_role_admin.read(role) } fn grant_role(ref self: ContractState, role: felt252, account: ContractAddress) { @@ -151,7 +151,7 @@ mod AccessControl { fn _grant_role(ref self: ContractState, role: felt252, account: ContractAddress) { if !AccessControlImpl::has_role(@self, role, account) { let caller: ContractAddress = get_caller_address(); - self.role_members.write((role, account), true); + self.AccessControl_role_member.write((role, account), true); self.emit(RoleGranted { role, account, sender: caller }); } } @@ -159,14 +159,14 @@ mod AccessControl { fn _revoke_role(ref self: ContractState, role: felt252, account: ContractAddress) { if AccessControlImpl::has_role(@self, role, account) { let caller: ContractAddress = get_caller_address(); - self.role_members.write((role, account), false); + self.AccessControl_role_member.write((role, account), false); self.emit(RoleRevoked { role, account, sender: caller }); } } fn _set_role_admin(ref self: ContractState, role: felt252, admin_role: felt252) { let previous_admin_role: felt252 = AccessControlImpl::get_role_admin(@self, role); - self.role_admin.write(role, admin_role); + self.AccessControl_role_admin.write(role, admin_role); self.emit(RoleAdminChanged { role, previous_admin_role, new_admin_role: admin_role }); } } diff --git a/src/access/ownable/ownable.cairo b/src/access/ownable/ownable.cairo index ee621afa4..856de8190 100644 --- a/src/access/ownable/ownable.cairo +++ b/src/access/ownable/ownable.cairo @@ -10,7 +10,7 @@ mod Ownable { #[storage] struct Storage { - _owner: ContractAddress + Ownable_owner: ContractAddress } #[event] @@ -38,15 +38,15 @@ mod Ownable { } fn assert_only_owner(self: @ContractState) { - let owner: ContractAddress = self._owner.read(); + let owner: ContractAddress = self.Ownable_owner.read(); let caller: ContractAddress = get_caller_address(); assert(!caller.is_zero(), Errors::ZERO_ADDRESS_CALLER); assert(caller == owner, Errors::NOT_OWNER); } fn _transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { - let previous_owner: ContractAddress = self._owner.read(); - self._owner.write(new_owner); + let previous_owner: ContractAddress = self.Ownable_owner.read(); + self.Ownable_owner.write(new_owner); self .emit( OwnershipTransferred { previous_owner: previous_owner, new_owner: new_owner } @@ -57,7 +57,7 @@ mod Ownable { #[external(v0)] impl OwnableImpl of interface::IOwnable { fn owner(self: @ContractState) -> ContractAddress { - self._owner.read() + self.Ownable_owner.read() } fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { diff --git a/src/account/account.cairo b/src/account/account.cairo index 13b7e8d0e..90408a30f 100644 --- a/src/account/account.cairo +++ b/src/account/account.cairo @@ -46,7 +46,7 @@ mod Account { #[storage] struct Storage { - public_key: felt252 + Account_public_key: felt252 } #[event] @@ -150,12 +150,12 @@ mod Account { #[external(v0)] impl PublicKeyImpl of super::PublicKeyTrait { fn get_public_key(self: @ContractState) -> felt252 { - self.public_key.read() + self.Account_public_key.read() } fn set_public_key(ref self: ContractState, new_public_key: felt252) { assert_only_self(); - self.emit(OwnerRemoved { removed_owner_guid: self.public_key.read() }); + self.emit(OwnerRemoved { removed_owner_guid: self.Account_public_key.read() }); self._set_public_key(new_public_key); } } @@ -163,7 +163,7 @@ mod Account { #[external(v0)] impl PublicKeyCamelImpl of super::PublicKeyCamelTrait { fn getPublicKey(self: @ContractState) -> felt252 { - self.public_key.read() + self.Account_public_key.read() } fn setPublicKey(ref self: ContractState, newPublicKey: felt252) { @@ -202,7 +202,7 @@ mod Account { } fn _set_public_key(ref self: ContractState, new_public_key: felt252) { - self.public_key.write(new_public_key); + self.Account_public_key.write(new_public_key); self.emit(OwnerAdded { new_owner_guid: new_public_key }); } @@ -213,7 +213,7 @@ mod Account { if valid_length { check_ecdsa_signature( - hash, self.public_key.read(), *signature.at(0_u32), *signature.at(1_u32) + hash, self.Account_public_key.read(), *signature.at(0_u32), *signature.at(1_u32) ) } else { false diff --git a/src/introspection/src5.cairo b/src/introspection/src5.cairo index 51a4e6720..3b94f4218 100644 --- a/src/introspection/src5.cairo +++ b/src/introspection/src5.cairo @@ -7,7 +7,7 @@ mod SRC5 { #[storage] struct Storage { - supported_interfaces: LegacyMap + SRC5_supported_interfaces: LegacyMap } mod Errors { @@ -20,7 +20,7 @@ mod SRC5 { if interface_id == interface::ISRC5_ID { return true; } - self.supported_interfaces.read(interface_id) + self.SRC5_supported_interfaces.read(interface_id) } } @@ -34,12 +34,12 @@ mod SRC5 { #[generate_trait] impl InternalImpl of InternalTrait { fn register_interface(ref self: ContractState, interface_id: felt252) { - self.supported_interfaces.write(interface_id, true); + self.SRC5_supported_interfaces.write(interface_id, true); } fn deregister_interface(ref self: ContractState, interface_id: felt252) { assert(interface_id != interface::ISRC5_ID, Errors::INVALID_ID); - self.supported_interfaces.write(interface_id, false); + self.SRC5_supported_interfaces.write(interface_id, false); } } } diff --git a/src/security/initializable.cairo b/src/security/initializable.cairo index 934813276..3ab4cda1f 100644 --- a/src/security/initializable.cairo +++ b/src/security/initializable.cairo @@ -5,7 +5,7 @@ mod Initializable { #[storage] struct Storage { - initialized: bool + Initializable_initialized: bool } mod Errors { @@ -15,12 +15,12 @@ mod Initializable { #[generate_trait] impl InternalImpl of InternalTrait { fn is_initialized(self: @ContractState) -> bool { - self.initialized.read() + self.Initializable_initialized.read() } fn initialize(ref self: ContractState) { assert(!self.is_initialized(), Errors::INITIALIZED); - self.initialized.write(true); + self.Initializable_initialized.write(true); } } } diff --git a/src/security/pausable.cairo b/src/security/pausable.cairo index 7441891a8..b29bbf19d 100644 --- a/src/security/pausable.cairo +++ b/src/security/pausable.cairo @@ -13,7 +13,7 @@ mod Pausable { #[storage] struct Storage { - paused: bool + Pausable_paused: bool } #[event] @@ -41,29 +41,29 @@ mod Pausable { #[external(v0)] impl PausableImpl of super::IPausable { fn is_paused(self: @ContractState) -> bool { - self.paused.read() + self.Pausable_paused.read() } } #[generate_trait] impl InternalImpl of InternalTrait { fn assert_not_paused(self: @ContractState) { - assert(!self.paused.read(), Errors::PAUSED); + assert(!self.Pausable_paused.read(), Errors::PAUSED); } fn assert_paused(self: @ContractState) { - assert(self.paused.read(), Errors::NOT_PAUSED); + assert(self.Pausable_paused.read(), Errors::NOT_PAUSED); } fn _pause(ref self: ContractState) { self.assert_not_paused(); - self.paused.write(true); + self.Pausable_paused.write(true); self.emit(Paused { account: get_caller_address() }); } fn _unpause(ref self: ContractState) { self.assert_paused(); - self.paused.write(false); + self.Pausable_paused.write(false); self.emit(Unpaused { account: get_caller_address() }); } } diff --git a/src/security/reentrancyguard.cairo b/src/security/reentrancyguard.cairo index d6a218c62..cabc96049 100644 --- a/src/security/reentrancyguard.cairo +++ b/src/security/reentrancyguard.cairo @@ -7,7 +7,7 @@ mod ReentrancyGuard { #[storage] struct Storage { - entered: bool + ReentrancyGuard_entered: bool } mod Errors { @@ -17,12 +17,12 @@ mod ReentrancyGuard { #[generate_trait] impl InternalImpl of InternalTrait { fn start(ref self: ContractState) { - assert(!self.entered.read(), Errors::REENTRANT_CALL); - self.entered.write(true); + assert(!self.ReentrancyGuard_entered.read(), Errors::REENTRANT_CALL); + self.ReentrancyGuard_entered.write(true); } fn end(ref self: ContractState) { - self.entered.write(false); + self.ReentrancyGuard_entered.write(false); } } } diff --git a/src/tests/access/test_ownable.cairo b/src/tests/access/test_ownable.cairo index 04b9033a2..5c68a490a 100644 --- a/src/tests/access/test_ownable.cairo +++ b/src/tests/access/test_ownable.cairo @@ -2,7 +2,7 @@ use openzeppelin::access::ownable::Ownable::InternalImpl; use openzeppelin::access::ownable::Ownable::OwnableCamelOnlyImpl; use openzeppelin::access::ownable::Ownable::OwnableImpl; use openzeppelin::access::ownable::Ownable::OwnershipTransferred; -use openzeppelin::access::ownable::Ownable::_owner::InternalContractMemberStateTrait; +use openzeppelin::access::ownable::Ownable::Ownable_owner::InternalContractMemberStateTrait; use openzeppelin::access::ownable::Ownable; use openzeppelin::tests::utils::constants::{ZERO, OTHER, OWNER}; use openzeppelin::tests::utils; @@ -35,12 +35,12 @@ fn setup() -> Ownable::ContractState { #[available_gas(2000000)] fn test_initializer() { let mut state = STATE(); - assert(state._owner.read().is_zero(), 'Should be zero'); + assert(state.Ownable_owner.read().is_zero(), 'Should be zero'); InternalImpl::initializer(ref state, OWNER()); assert_event_ownership_transferred(ZERO(), OWNER()); - assert(state._owner.read() == OWNER(), 'Owner should be set'); + assert(state.Ownable_owner.read() == OWNER(), 'Owner should be set'); } // @@ -84,7 +84,7 @@ fn test__transfer_ownership() { assert_event_ownership_transferred(OWNER(), OTHER()); - assert(state._owner.read() == OTHER(), 'Owner should be OTHER'); + assert(state.Ownable_owner.read() == OTHER(), 'Owner should be OTHER'); } // diff --git a/src/tests/security/test_reentrancyguard.cairo b/src/tests/security/test_reentrancyguard.cairo index b5aa84056..231a693b6 100644 --- a/src/tests/security/test_reentrancyguard.cairo +++ b/src/tests/security/test_reentrancyguard.cairo @@ -1,5 +1,5 @@ use openzeppelin::security::reentrancyguard::ReentrancyGuard::InternalImpl; -use openzeppelin::security::reentrancyguard::ReentrancyGuard::entered::InternalContractMemberStateTrait; +use openzeppelin::security::reentrancyguard::ReentrancyGuard::ReentrancyGuard_entered::InternalContractMemberStateTrait; use openzeppelin::security::reentrancyguard::ReentrancyGuard; use openzeppelin::tests::mocks::reentrancy_attacker_mock::Attacker; use openzeppelin::tests::mocks::reentrancy_mock::IReentrancyMockDispatcher; @@ -26,9 +26,9 @@ fn deploy_mock() -> IReentrancyMockDispatcher { fn test_reentrancy_guard_start() { let mut state = STATE(); - assert(!state.entered.read(), 'Should not be entered'); + assert(!state.ReentrancyGuard_entered.read(), 'Should not be entered'); InternalImpl::start(ref state); - assert(state.entered.read(), 'Should be entered'); + assert(state.ReentrancyGuard_entered.read(), 'Should be entered'); } #[test] @@ -47,9 +47,9 @@ fn test_reentrancy_guard_end() { let mut state = STATE(); InternalImpl::start(ref state); - assert(state.entered.read(), 'Should be entered'); + assert(state.ReentrancyGuard_entered.read(), 'Should be entered'); InternalImpl::end(ref state); - assert(!state.entered.read(), 'Should no longer be entered'); + assert(!state.ReentrancyGuard_entered.read(), 'Should no longer be entered'); } // diff --git a/src/tests/token/test_erc721.cairo b/src/tests/token/test_erc721.cairo index 1351e0385..25265d7e4 100644 --- a/src/tests/token/test_erc721.cairo +++ b/src/tests/token/test_erc721.cairo @@ -1,5 +1,5 @@ -use ERC721::_owners::InternalContractMemberStateTrait as OwnersTrait; -use ERC721::_token_approvals::InternalContractMemberStateTrait as TokenApprovalsTrait; +use ERC721::ERC721_owners::InternalContractMemberStateTrait as OwnersTrait; +use ERC721::ERC721_token_approvals::InternalContractMemberStateTrait as TokenApprovalsTrait; use array::ArrayTrait; use integer::u256; @@ -191,17 +191,17 @@ fn test__exists() { let token_id = TOKEN_ID; assert(!InternalImpl::_exists(@state, token_id), 'Token should not exist'); - assert(state._owners.read(token_id) == zero, 'Invalid owner'); + assert(state.ERC721_owners.read(token_id) == zero, 'Invalid owner'); InternalImpl::_mint(ref state, RECIPIENT(), token_id); assert(InternalImpl::_exists(@state, token_id), 'Token should exist'); - assert(state._owners.read(token_id) == RECIPIENT(), 'Invalid owner'); + assert(state.ERC721_owners.read(token_id) == RECIPIENT(), 'Invalid owner'); InternalImpl::_burn(ref state, token_id); assert(!InternalImpl::_exists(@state, token_id), 'Token should not exist'); - assert(state._owners.read(token_id) == zero, 'Invalid owner'); + assert(state.ERC721_owners.read(token_id) == zero, 'Invalid owner'); } // @@ -1329,9 +1329,9 @@ fn test__burn() { InternalImpl::_burn(ref state, TOKEN_ID); assert_event_transfer(OWNER(), ZERO(), TOKEN_ID); - assert(state._owners.read(TOKEN_ID) == ZERO(), 'Ownership after'); + assert(state.ERC721_owners.read(TOKEN_ID) == ZERO(), 'Ownership after'); assert(ERC721Impl::balance_of(@state, OWNER()) == 0, 'Balance of owner after'); - assert(state._token_approvals.read(TOKEN_ID) == ZERO(), 'Approval after'); + assert(state.ERC721_token_approvals.read(TOKEN_ID) == ZERO(), 'Approval after'); } #[test] diff --git a/src/token/erc20/erc20.cairo b/src/token/erc20/erc20.cairo index 9a97cee58..36b32c593 100644 --- a/src/token/erc20/erc20.cairo +++ b/src/token/erc20/erc20.cairo @@ -12,11 +12,11 @@ mod ERC20 { #[storage] struct Storage { - _name: felt252, - _symbol: felt252, - _total_supply: u256, - _balances: LegacyMap, - _allowances: LegacyMap<(ContractAddress, ContractAddress), u256>, + ERC20_name: felt252, + ERC20_symbol: felt252, + ERC20_total_supply: u256, + ERC20_balances: LegacyMap, + ERC20_allowances: LegacyMap<(ContractAddress, ContractAddress), u256>, } #[event] @@ -68,11 +68,11 @@ mod ERC20 { #[external(v0)] impl ERC20Impl of IERC20 { fn name(self: @ContractState) -> felt252 { - self._name.read() + self.ERC20_name.read() } fn symbol(self: @ContractState) -> felt252 { - self._symbol.read() + self.ERC20_symbol.read() } fn decimals(self: @ContractState) -> u8 { @@ -80,17 +80,17 @@ mod ERC20 { } fn total_supply(self: @ContractState) -> u256 { - self._total_supply.read() + self.ERC20_total_supply.read() } fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { - self._balances.read(account) + self.ERC20_balances.read(account) } fn allowance( self: @ContractState, owner: ContractAddress, spender: ContractAddress ) -> u256 { - self._allowances.read((owner, spender)) + self.ERC20_allowances.read((owner, spender)) } fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) -> bool { @@ -173,15 +173,18 @@ mod ERC20 { #[generate_trait] impl InternalImpl of InternalTrait { fn initializer(ref self: ContractState, name: felt252, symbol: felt252) { - self._name.write(name); - self._symbol.write(symbol); + self.ERC20_name.write(name); + self.ERC20_symbol.write(symbol); } fn _increase_allowance( ref self: ContractState, spender: ContractAddress, added_value: u256 ) -> bool { let caller = get_caller_address(); - self._approve(caller, spender, self._allowances.read((caller, spender)) + added_value); + self + ._approve( + caller, spender, self.ERC20_allowances.read((caller, spender)) + added_value + ); true } @@ -191,22 +194,24 @@ mod ERC20 { let caller = get_caller_address(); self ._approve( - caller, spender, self._allowances.read((caller, spender)) - subtracted_value + caller, + spender, + self.ERC20_allowances.read((caller, spender)) - subtracted_value ); true } fn _mint(ref self: ContractState, recipient: ContractAddress, amount: u256) { assert(!recipient.is_zero(), Errors::MINT_TO_ZERO); - self._total_supply.write(self._total_supply.read() + amount); - self._balances.write(recipient, self._balances.read(recipient) + amount); + self.ERC20_total_supply.write(self.ERC20_total_supply.read() + amount); + self.ERC20_balances.write(recipient, self.ERC20_balances.read(recipient) + amount); self.emit(Transfer { from: Zeroable::zero(), to: recipient, value: amount }); } fn _burn(ref self: ContractState, account: ContractAddress, amount: u256) { assert(!account.is_zero(), Errors::BURN_FROM_ZERO); - self._total_supply.write(self._total_supply.read() - amount); - self._balances.write(account, self._balances.read(account) - amount); + self.ERC20_total_supply.write(self.ERC20_total_supply.read() - amount); + self.ERC20_balances.write(account, self.ERC20_balances.read(account) - amount); self.emit(Transfer { from: account, to: Zeroable::zero(), value: amount }); } @@ -215,7 +220,7 @@ mod ERC20 { ) { assert(!owner.is_zero(), Errors::APPROVE_FROM_ZERO); assert(!spender.is_zero(), Errors::APPROVE_TO_ZERO); - self._allowances.write((owner, spender), amount); + self.ERC20_allowances.write((owner, spender), amount); self.emit(Approval { owner, spender, value: amount }); } @@ -227,15 +232,15 @@ mod ERC20 { ) { assert(!sender.is_zero(), Errors::TRANSFER_FROM_ZERO); assert(!recipient.is_zero(), Errors::TRANSFER_TO_ZERO); - self._balances.write(sender, self._balances.read(sender) - amount); - self._balances.write(recipient, self._balances.read(recipient) + amount); + self.ERC20_balances.write(sender, self.ERC20_balances.read(sender) - amount); + self.ERC20_balances.write(recipient, self.ERC20_balances.read(recipient) + amount); self.emit(Transfer { from: sender, to: recipient, value: amount }); } fn _spend_allowance( ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256 ) { - let current_allowance = self._allowances.read((owner, spender)); + let current_allowance = self.ERC20_allowances.read((owner, spender)); if current_allowance != BoundedInt::max() { self._approve(owner, spender, current_allowance - amount); } diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 0373dad36..660a92919 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -22,13 +22,13 @@ mod ERC721 { #[storage] struct Storage { - _name: felt252, - _symbol: felt252, - _owners: LegacyMap, - _balances: LegacyMap, - _token_approvals: LegacyMap, - _operator_approvals: LegacyMap<(ContractAddress, ContractAddress), bool>, - _token_uri: LegacyMap, + ERC721_name: felt252, + ERC721_symbol: felt252, + ERC721_owners: LegacyMap, + ERC721_balances: LegacyMap, + ERC721_token_approvals: LegacyMap, + ERC721_operator_approvals: LegacyMap<(ContractAddress, ContractAddress), bool>, + ERC721_token_uri: LegacyMap, } #[event] @@ -108,16 +108,16 @@ mod ERC721 { #[external(v0)] impl ERC721MetadataImpl of interface::IERC721Metadata { fn name(self: @ContractState) -> felt252 { - self._name.read() + self.ERC721_name.read() } fn symbol(self: @ContractState) -> felt252 { - self._symbol.read() + self.ERC721_symbol.read() } fn token_uri(self: @ContractState, token_id: u256) -> felt252 { assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); - self._token_uri.read(token_id) + self.ERC721_token_uri.read(token_id) } } @@ -125,7 +125,7 @@ mod ERC721 { impl ERC721MetadataCamelOnlyImpl of interface::IERC721MetadataCamelOnly { fn tokenURI(self: @ContractState, tokenId: u256) -> felt252 { assert(self._exists(tokenId), Errors::INVALID_TOKEN_ID); - self._token_uri.read(tokenId) + self.ERC721_token_uri.read(tokenId) } } @@ -133,7 +133,7 @@ mod ERC721 { impl ERC721Impl of interface::IERC721 { fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { assert(!account.is_zero(), Errors::INVALID_ACCOUNT); - self._balances.read(account) + self.ERC721_balances.read(account) } fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress { @@ -142,13 +142,13 @@ mod ERC721 { fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); - self._token_approvals.read(token_id) + self.ERC721_token_approvals.read(token_id) } fn is_approved_for_all( self: @ContractState, owner: ContractAddress, operator: ContractAddress ) -> bool { - self._operator_approvals.read((owner, operator)) + self.ERC721_operator_approvals.read((owner, operator)) } fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) { @@ -239,8 +239,8 @@ mod ERC721 { #[generate_trait] impl InternalImpl of InternalTrait { fn initializer(ref self: ContractState, name: felt252, symbol: felt252) { - self._name.write(name); - self._symbol.write(symbol); + self.ERC721_name.write(name); + self.ERC721_symbol.write(symbol); let mut unsafe_state = src5::SRC5::unsafe_new_contract_state(); src5::SRC5::InternalImpl::register_interface(ref unsafe_state, interface::IERC721_ID); @@ -250,7 +250,7 @@ mod ERC721 { } fn _owner_of(self: @ContractState, token_id: u256) -> ContractAddress { - let owner = self._owners.read(token_id); + let owner = self.ERC721_owners.read(token_id); match owner.is_zero() { bool::False(()) => owner, bool::True(()) => panic_with_felt252(Errors::INVALID_TOKEN_ID) @@ -258,7 +258,7 @@ mod ERC721 { } fn _exists(self: @ContractState, token_id: u256) -> bool { - !self._owners.read(token_id).is_zero() + !self.ERC721_owners.read(token_id).is_zero() } fn _is_approved_or_owner( @@ -275,7 +275,7 @@ mod ERC721 { let owner = self._owner_of(token_id); assert(owner != to, Errors::APPROVAL_TO_OWNER); - self._token_approvals.write(token_id, to); + self.ERC721_token_approvals.write(token_id, to); self.emit(Approval { owner, approved: to, token_id }); } @@ -286,7 +286,7 @@ mod ERC721 { approved: bool ) { assert(owner != operator, Errors::SELF_APPROVAL); - self._operator_approvals.write((owner, operator), approved); + self.ERC721_operator_approvals.write((owner, operator), approved); self.emit(ApprovalForAll { owner, operator, approved }); } @@ -294,8 +294,8 @@ mod ERC721 { assert(!to.is_zero(), Errors::INVALID_RECEIVER); assert(!self._exists(token_id), Errors::ALREADY_MINTED); - self._balances.write(to, self._balances.read(to) + 1); - self._owners.write(token_id, to); + self.ERC721_balances.write(to, self.ERC721_balances.read(to) + 1); + self.ERC721_owners.write(token_id, to); self.emit(Transfer { from: Zeroable::zero(), to, token_id }); } @@ -308,11 +308,11 @@ mod ERC721 { assert(from == owner, Errors::WRONG_SENDER); // Implicit clear approvals, no need to emit an event - self._token_approvals.write(token_id, Zeroable::zero()); + self.ERC721_token_approvals.write(token_id, Zeroable::zero()); - self._balances.write(from, self._balances.read(from) - 1); - self._balances.write(to, self._balances.read(to) + 1); - self._owners.write(token_id, to); + self.ERC721_balances.write(from, self.ERC721_balances.read(from) - 1); + self.ERC721_balances.write(to, self.ERC721_balances.read(to) + 1); + self.ERC721_owners.write(token_id, to); self.emit(Transfer { from, to, token_id }); } @@ -321,10 +321,10 @@ mod ERC721 { let owner = self._owner_of(token_id); // Implicit clear approvals, no need to emit an event - self._token_approvals.write(token_id, Zeroable::zero()); + self.ERC721_token_approvals.write(token_id, Zeroable::zero()); - self._balances.write(owner, self._balances.read(owner) - 1); - self._owners.write(token_id, Zeroable::zero()); + self.ERC721_balances.write(owner, self.ERC721_balances.read(owner) - 1); + self.ERC721_owners.write(token_id, Zeroable::zero()); self.emit(Transfer { from: owner, to: Zeroable::zero(), token_id }); } @@ -354,7 +354,7 @@ mod ERC721 { fn _set_token_uri(ref self: ContractState, token_id: u256, token_uri: felt252) { assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); - self._token_uri.write(token_id, token_uri) + self.ERC721_token_uri.write(token_id, token_uri) } } From ddbbc813dca0b8ada1fc4f92e5cc7398e329deb8 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 26 Sep 2023 15:37:29 -0400 Subject: [PATCH 158/246] fix compatibility heading --- docs/modules/ROOT/pages/erc721.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index e0a1219ad..1e171fb5b 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -91,7 +91,7 @@ trait IERC721Metadata { } ---- -== ERC721 Compatibility +=== ERC721 compatibility :erc165-storage: https://docs.openzeppelin.com/contracts/4.x/api/utils#ERC165Storage[ERC165Storage] :src5-api: xref:introspection.adoc#src5[SRC5] From 5773786c0e0c40124c38caab2691ae5aa435e552 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 26 Sep 2023 17:17:53 -0400 Subject: [PATCH 159/246] add erc721abi interface --- docs/modules/ROOT/pages/erc721.adoc | 48 +++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 1e171fb5b..367e8fdc6 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -17,14 +17,58 @@ TIP: For detailed information on the usage and implementation check the {erc721- :ierc721metadata-interface: xref:/erc721.adoc#ierc721metadata[IERC721Metadata] :isrc5-interface: xref:/erc721.adoc#isrc5[ISRC5] -Contracts must implement both the {ierc721-interface} and {isrc5-interface} interfaces in order to have an ERC721 compliant contract. +ERC721 contracts must implement both the {ierc721-interface} and {isrc5-interface} interfaces in order to have an ERC721-compliant contract. Additionally, ERC721 contracts often include {ierc721metadata-interface} interface as well. -Contracts for Cairo's implementation includes all three interfaces. +The following implementation includes all three interfaces and represents the full interface of the Contracts for Cairo `ERC721ABI` module. WARNING: The {ierc721-interface} and {ierc721metadata-interface} interfaces are approximations of their Ethereum counterparts. These approximations include a few notable differences. See {compatibility} for more information. +[,javascript] +---- +trait IERC721ABI { + // IERC721 + fn balance_of(account: ContractAddress) -> u256; + fn owner_of(token_id: u256) -> ContractAddress; + fn safe_transfer_from( + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span + ); + fn transfer_from(from: ContractAddress, to: ContractAddress, token_id: u256); + fn approve(to: ContractAddress, token_id: u256); + fn set_approval_for_all(operator: ContractAddress, approved: bool); + fn get_approved(token_id: u256) -> ContractAddress; + fn is_approved_for_all(owner: ContractAddress, operator: ContractAddress) -> bool; + + // ISRC5 + fn supports_interface(interface_id: felt252) -> bool; + + // IERC721Metadata + fn name() -> felt252; + fn symbol() -> felt252; + fn token_uri(token_id: u256) -> felt252; + + // ERC721 Camel + fn balanceOf(account: ContractAddress) -> u256; + fn ownerOf(tokenId: u256) -> ContractAddress; + fn safeTransferFrom( + from: ContractAddress, + to: ContractAddress, + tokenId: u256, + data: Span + ); + fn transferFrom(from: ContractAddress, to: ContractAddress, tokenId: u256); + fn setApprovalForAll(operator: ContractAddress, approved: bool); + fn getApproved(tokenId: u256) -> ContractAddress; + fn isApprovedForAll(owner: ContractAddress, operator: ContractAddress) -> bool; + fn supportsInterface(interfaceId: felt252) -> bool; + fn tokenURI(tokenId: u256) -> felt252; +} +---- + === IERC721 The `IERC721` interface provides basic functionality to track, transfer, and consign NFTs. From fe26ac22d8930271b19c030378f7601bee386307 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 26 Sep 2023 17:19:50 -0400 Subject: [PATCH 160/246] fix heading --- docs/modules/ROOT/pages/erc721.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 367e8fdc6..aea4c8c99 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -253,7 +253,7 @@ The unsafe contract state allows access to `ERC721`'s storage. With this access, the constructor first calls the initializer to set the NFT name and symbol. Next, the constructor calls the custom internal function `_mint_with_uri` that mints a one-of-one NFT and sets the URI for the minted token ID. -=== Token Transfers +=== Token transfers :src-5: xref:introspection.adoc#src5[SRC5] :transfer_from-api: xref:api/erc721.adoc#IERC721-transfer_from[transfer_from] From 78d0f892c2f36ad69ea3983a95edc0ea5cbb8bf7 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 26 Sep 2023 18:55:55 -0400 Subject: [PATCH 161/246] fix interface intro --- docs/modules/ROOT/pages/erc721.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index aea4c8c99..c3f718b8a 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -19,7 +19,7 @@ TIP: For detailed information on the usage and implementation check the {erc721- ERC721 contracts must implement both the {ierc721-interface} and {isrc5-interface} interfaces in order to have an ERC721-compliant contract. Additionally, ERC721 contracts often include {ierc721metadata-interface} interface as well. -The following implementation includes all three interfaces and represents the full interface of the Contracts for Cairo `ERC721ABI` module. +The following preset contract includes all three interfaces and represents the full interface of the `ERC721ABI` module. WARNING: The {ierc721-interface} and {ierc721metadata-interface} interfaces are approximations of their Ethereum counterparts. These approximations include a few notable differences. From a6ce5fb946ec7642b268675181067c995ed4e19c Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 26 Sep 2023 19:21:27 -0400 Subject: [PATCH 162/246] consolidate token transfers section --- docs/modules/ROOT/pages/erc721.adoc | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index c3f718b8a..0ee1cbe58 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -249,43 +249,20 @@ mod MyNFT { ---- In order for the `MyNFT` contract to extend the `ERC721` contract, it utilizes the `unsafe_new_contract_state`. -The unsafe contract state allows access to `ERC721`'s storage. +The unsafe contract state allows access to ``ERC721``'s storage. With this access, the constructor first calls the initializer to set the NFT name and symbol. Next, the constructor calls the custom internal function `_mint_with_uri` that mints a one-of-one NFT and sets the URI for the minted token ID. === Token transfers -:src-5: xref:introspection.adoc#src5[SRC5] :transfer_from-api: xref:api/erc721.adoc#IERC721-transfer_from[transfer_from] :safe_transfer_from-api: xref:api/erc721.adoc#IERC721-safe_transfer_from[safe_transfer_from] -:isrc6: xref:accounts.adoc#isrc6_interface[ISRC6] This library includes {transfer_from-api} and {safe_transfer_from-api} to transfer NFTs. If using `transfer_from`, *the caller is responsible to confirm that the recipient is capable of receiving NFTs or else they may be permanently lost.* The `safe_transfer_from` method mitigates this risk by querying the recipient contract's interface support. -The safe mechanism first queries if the recipient contract supports the `IERC721Receiver` interface through introspection ({src-5}). -If the recipient contract does _not_ support the receiver interface, then the safe mechanism checks if the recipient contract supports the {isrc6} interface, which is the standard account contract interface. -If either case is true, then the token transfer behaves as if `transfer_from` was called. -Otherwise, the transfer will fail. - -To better visualize the process, see the snippet below. - -[,javascript] ----- -fn _check_on_erc721_received( - from: ContractAddress, to: ContractAddress, token_id: u256, data: Span -) -> bool { - if { - // `to` supports ISRC5 && - // `to` has registered support for IERC721Receiver - // return true; - } else { - // `to` is an account contract - // return true; - } -} ----- +WARNING: Usage of `safe_transfer_from` prevents loss, though the caller must understand this adds an external call which potentially creates a reentrancy vulnerability. === Receiving tokens From cf3ff1e5d4ad320c729d1fa27d972e74381b3106 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 26 Sep 2023 20:39:00 -0400 Subject: [PATCH 163/246] normalize function order --- docs/modules/ROOT/pages/api/erc721.adoc | 124 ++++++++++++------------ docs/modules/ROOT/pages/erc721.adoc | 10 +- 2 files changed, 65 insertions(+), 69 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 14b446d8f..f3e94de97 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -26,8 +26,8 @@ Interface of the IERC721 standard as defined in {eip721}. -- * xref:#IERC721-balance_of[`++balance_of(account)++`] * xref:#IERC721-owner_of[`++owner_of(token_id)++`] -* xref:#IERC721-transfer_from[`++transfer_from(from, to, token_id)++`] * xref:#IERC721-safe_transfer_from[`++safe_transfer_from(from, to, token_id, data)++`] +* xref:#IERC721-transfer_from[`++transfer_from(from, to, token_id)++`] * xref:#IERC721-approve[`++approve(to, token_id)++`] * xref:#IERC721-set_approval_for_all[`++set_approval_for_all(operator, approved)++`] * xref:#IERC721-get_approved[`++get_approved(token_id)++`] @@ -66,41 +66,41 @@ Requirements: - `token_id` exists. [.contract-item] -[[IERC721-transfer_from]] -==== `[.contract-item-name]#++transfer_from++#++(from: ContractAddress, to: ContractAddress, token_id: u256)++` [.item-kind]#external# +[[IERC721-safe_transfer_from]] +==== `[.contract-item-name]#++safe_transfer_from++#++(from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#external# -Transfer ownership of `token_id` from `from` to `to`. - -Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 transfers or else they may be permanently lost. -Usage of <> prevents loss, though the caller must understand this adds an external call which potentially creates a reentrancy vulnerability. +Safely transfer ownership of `token_id` from `from` to `to`, checking first +that `to` is aware of the ERC721 protocol to prevent tokens being locked +forever. For information regarding how contracts communicate their +awareness of the ERC721 protocol, see {receiving-tokens}. Emits a <> event. Requirements: -- Caller either approved or the `token_id` owner. +- Caller is either approved or the `token_id` owner. - `to` is not the zero address. - `from` is not the zero address. - `token_id` exists. +- `to` is either an account contract or supports the <> interface. [.contract-item] -[[IERC721-safe_transfer_from]] -==== `[.contract-item-name]#++safe_transfer_from++#++(from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#external# +[[IERC721-transfer_from]] +==== `[.contract-item-name]#++transfer_from++#++(from: ContractAddress, to: ContractAddress, token_id: u256)++` [.item-kind]#external# -Safely transfer ownership of `token_id` from `from` to `to`, checking first -that `to` is aware of the ERC721 protocol to prevent tokens being locked -forever. For information regarding how contracts communicate their -awareness of the ERC721 protocol, see {receiving-tokens}. +Transfer ownership of `token_id` from `from` to `to`. + +Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 transfers or else they may be permanently lost. +Usage of <> prevents loss, though the caller must understand this adds an external call which potentially creates a reentrancy vulnerability. Emits a <> event. Requirements: -- Caller is either approved or the `token_id` owner. +- Caller either approved or the `token_id` owner. - `to` is not the zero address. - `from` is not the zero address. - `token_id` exists. -- `to` is either an account contract or supports the <> interface. [.contract-item] [[IERC721-approve]] @@ -187,8 +187,8 @@ See {eip721}. * xref:#IERC721-balance_of[`++balance_of(account)++`] * xref:#IERC721-owner_of[`++owner_of(token_id)++`] -* xref:#IERC721-transfer_from[`++transfer_from(from, to, token_id)++`] * xref:#IERC721-safe_transfer_from[`++safe_transfer_from(from, to, token_id, data)++`] +* xref:#IERC721-transfer_from[`++transfer_from(from, to, token_id)++`] * xref:#IERC721-approve[`++approve(to, token_id)++`] * xref:#IERC721-set_approval_for_all[`++set_approval_for_all(operator, approved)++`] * xref:#IERC721-get_approved[`++get_approved(token_id)++`] @@ -251,46 +251,42 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi [.contract-index] .External Functions -- -[.contract-subindex-inherited] -.IERC721Metadata - -* xref:#IERC721Metadata-name[`++name(self)++`] -* xref:#IERC721Metadata-symbol[`++symbol(self)++`] -* xref:#IERC721Metadata-token_uri[`++token_uri(self, token_id)++`] - [.contract-subindex-inherited] .IERC721 * xref:#IERC721-balance_of[`++balance_of(self, account)++`] * xref:#IERC721-owner_of[`++owner_of(self, token_id)++`] -* xref:#IERC721-transfer_from[`++transfer_from(self, from, to, token_id)++`] * xref:#IERC721-safe_transfer_from[`++safe_transfer_from(self, from, to, token_id, data)++`] +* xref:#IERC721-transfer_from[`++transfer_from(self, from, to, token_id)++`] * xref:#IERC721-approve[`++approve(self, to, token_id)++`] * xref:#IERC721-set_approval_for_all[`++set_approval_for_all(self, operator, approved)++`] * xref:#IERC721-get_approved[`++get_approved(self, token_id)++`] * xref:#IERC721-is_approved_for_all[`++is_approved_for_all(self, owner, operator)++`] +[.contract-subindex-inherited] +.ISRC5 + +* xref:/api/introspection.adoc#ISRC5-supports_interface[`++supports_interface(self, interface_id)++`] + +[.contract-subindex-inherited] +.IERC721Metadata + +* xref:#IERC721Metadata-name[`++name(self)++`] +* xref:#IERC721Metadata-symbol[`++symbol(self)++`] +* xref:#IERC721Metadata-token_uri[`++token_uri(self, token_id)++`] + [.contract-subindex-inherited] .ER721Camel -* xref:#ERC721-tokenURI[`++tokenURI(self, tokenId)++`] * xref:#ERC721-balanceOf[`++balanceOf(self, account)++`] * xref:#ERC721-ownerOf[`++ownerOf(self, tokenId)++`] -* xref:#ERC721-transferFrom[`++transferFrom(self, from, to, tokenId)++`] * xref:#ERC721-safeTransferFrom[`++safeTransferFrom(self, from, to, tokenId, data)++`] +* xref:#ERC721-transferFrom[`++transferFrom(self, from, to, tokenId)++`] * xref:#ERC721-setApprovalForAll[`++setApprovalForAll(self, operator, approved)++`] * xref:#ERC721-getApproved[`++getApproved(self, tokenId)++`] * xref:#ERC721-isApprovedForAll[`++isApprovedForAll(self, owner, operator)++`] - -[.contract-subindex-inherited] -.ISRC5 - -* xref:/api/introspection.adoc#ISRC5-supports_interface[`++supports_interface(self, interface_id)++`] - -[.contract-subindex-inherited] -.SRC5Camel - * xref:/api/introspection.adoc#ISRC5-supports_interface[`++supportsInterface(self, interfaceId)++`] +* xref:#ERC721-tokenURI[`++tokenURI(self, tokenId)++`] -- [.contract-index] @@ -338,24 +334,6 @@ The constructor also registers the IERC721 and IERC721_METADATA interface ids ac ==== External functions -[.contract-item] -[[ERC721-name]] -==== `[.contract-item-name]#++name++#++(self: @ContractState) -> felt252++` [.item-kind]#external# - -See <>. - -[.contract-item] -[[ERC721-symbol]] -==== `[.contract-item-name]#++symbol++#++(self: @ContractState) -> felt252++` [.item-kind]#external# - -See <>. - -[.contract-item] -[[ERC721-token_uri]] -==== `[.contract-item-name]#++token_uri++#++(self: @ContractState, token_id: u256) -> felt252++` [.item-kind]#external# - -See <>. - [.contract-item] [[ERC721-balance_of]] ==== `[.contract-item-name]#++balance_of++#++(self: @ContractState, account: ContractAddress) → u256++` [.item-kind]#external# @@ -368,18 +346,18 @@ See <>. See <>. -[.contract-item] -[[ERC721-transfer_from]] -==== `[.contract-item-name]#++transfer_from++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256)++` [.item-kind]#external# - -See <>. - [.contract-item] [[ERC721-safe_transfer_from]] ==== `[.contract-item-name]#++safe_transfer_from++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#external# See <>. +[.contract-item] +[[ERC721-transfer_from]] +==== `[.contract-item-name]#++transfer_from++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256)++` [.item-kind]#external# + +See <>. + [.contract-item] [[ERC721-approve]] ==== `[.contract-item-name]#++approve++#++(ref self: ContractState, to: ContractAddress, token_id: u256)++` [.item-kind]#external# @@ -405,12 +383,22 @@ See <>. See <>. [.contract-item] -[[ERC721-tokenURI]] -==== `[.contract-item-name]#++tokenURI++#++(self: @ContractState, tokenId: u256) -> felt252++` [.item-kind]#external# +[[ERC721-name]] +==== `[.contract-item-name]#++name++#++(self: @ContractState) -> felt252++` [.item-kind]#external# -See <>. +See <>. -Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. +[.contract-item] +[[ERC721-symbol]] +==== `[.contract-item-name]#++symbol++#++(self: @ContractState) -> felt252++` [.item-kind]#external# + +See <>. + +[.contract-item] +[[ERC721-token_uri]] +==== `[.contract-item-name]#++token_uri++#++(self: @ContractState, token_id: u256) -> felt252++` [.item-kind]#external# + +See <>. [.contract-item] [[ERC721-balanceOf]] @@ -468,6 +456,14 @@ See <>. Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. +[.contract-item] +[[ERC721-tokenURI]] +==== `[.contract-item-name]#++tokenURI++#++(self: @ContractState, tokenId: u256) -> felt252++` [.item-kind]#external# + +See <>. + +Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. + ==== Internal functions [.contract-item] diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 0ee1cbe58..bb35a29e1 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -78,17 +78,17 @@ The `IERC721` interface provides basic functionality to track, transfer, and con trait IERC721 { fn balance_of(account: ContractAddress) -> u256; fn owner_of(token_id: u256) -> ContractAddress; - fn transfer_from( - from: ContractAddress, - to: ContractAddress, - token_id: u256 - ); fn safe_transfer_from( from: ContractAddress, to: ContractAddress, token_id: u256, data: Span ); + fn transfer_from( + from: ContractAddress, + to: ContractAddress, + token_id: u256 + ); fn approve(to: ContractAddress, token_id: u256); fn set_approval_for_all( operator: ContractAddress, From fd4f44bfc1af03dc695d951eef4c0bc920e15a13 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 26 Sep 2023 20:46:29 -0400 Subject: [PATCH 164/246] remove grayed-out subsections --- docs/modules/ROOT/pages/api/erc721.adoc | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index f3e94de97..d1ce82d85 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -33,9 +33,7 @@ Interface of the IERC721 standard as defined in {eip721}. * xref:#IERC721-get_approved[`++get_approved(token_id)++`] * xref:#IERC721-is_approved_for_all[`++is_approved_for_all(owner, operator)++`] -[.contract-subindex-inherited] .ISRC5 - * xref:/api/introspection.adoc#ISRC5-supports_interface[`++supports_interface(interface_id)++`] -- @@ -182,9 +180,7 @@ See {eip721}. * xref:#IERC721Metadata-owner_of[`++symbol()++`] * xref:#IERC721Metadata-token_uri[`++token_uri(token_id)++`] -[.contract-subindex-inherited] .IERC721 - * xref:#IERC721-balance_of[`++balance_of(account)++`] * xref:#IERC721-owner_of[`++owner_of(token_id)++`] * xref:#IERC721-safe_transfer_from[`++safe_transfer_from(from, to, token_id, data)++`] @@ -194,16 +190,13 @@ See {eip721}. * xref:#IERC721-get_approved[`++get_approved(token_id)++`] * xref:#IERC721-is_approved_for_all[`++is_approved_for_all(owner, operator)++`] -[.contract-subindex-inherited] .ISRC5 - * xref:/api/introspection.adoc#ISRC5-supports_interface[`++supports_interface(interface_id)++`] -- [.contract-index] .Events -- -[.contract-subindex-inherited] .IERC721 * xref:#IERC721-Approval[`++Approval(owner, approved, token_id)++`] @@ -251,9 +244,7 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi [.contract-index] .External Functions -- -[.contract-subindex-inherited] .IERC721 - * xref:#IERC721-balance_of[`++balance_of(self, account)++`] * xref:#IERC721-owner_of[`++owner_of(self, token_id)++`] * xref:#IERC721-safe_transfer_from[`++safe_transfer_from(self, from, to, token_id, data)++`] @@ -263,21 +254,15 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi * xref:#IERC721-get_approved[`++get_approved(self, token_id)++`] * xref:#IERC721-is_approved_for_all[`++is_approved_for_all(self, owner, operator)++`] -[.contract-subindex-inherited] .ISRC5 - * xref:/api/introspection.adoc#ISRC5-supports_interface[`++supports_interface(self, interface_id)++`] -[.contract-subindex-inherited] .IERC721Metadata - * xref:#IERC721Metadata-name[`++name(self)++`] * xref:#IERC721Metadata-symbol[`++symbol(self)++`] * xref:#IERC721Metadata-token_uri[`++token_uri(self, token_id)++`] -[.contract-subindex-inherited] .ER721Camel - * xref:#ERC721-balanceOf[`++balanceOf(self, account)++`] * xref:#ERC721-ownerOf[`++ownerOf(self, tokenId)++`] * xref:#ERC721-safeTransferFrom[`++safeTransferFrom(self, from, to, tokenId, data)++`] @@ -292,10 +277,7 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi [.contract-index] .Internal Functions -- - -[.contract-subindex-inherited] .InternalImpl - * xref:#ERC721-initializer[`++initializer(self, name_, symbol_)++`] * xref:#ERC721-_owner_of[`++_owner_of(self, token_id)++`] * xref:#ERC721-_exists[`++_exists(self, token_id)++`] @@ -313,9 +295,7 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi [.contract-index] .Events -- -[.contract-subindex-inherited] .IERC721 - * xref:#IERC721-Approval[`++Approval(owner, approved, token_id)++`] * xref:#IERC721-ApprovalForAll[`++ApprovalForAll(owner, operator, approved)++`] * xref:#IERC721-Transfer[`++Transfer(from, to, token_id)++`] @@ -649,7 +629,6 @@ use openzeppelin::token::erc721::interface::IERC721Receiver; [.contract-item] [[IERC721Receiver-on_erc721_received]] -==== `[.contract-item-name]#++on_erc721_received++#++(operator: ContractAddress, from: ContractAddress, token_id: u256, data Span)++` [.item-kind]#external# +==== `[.contract-item-name]#++on_erc721_received++#++(operator: ContractAddress, from: ContractAddress, token_id: u256, data Span) -> felt252++` [.item-kind]#external# Whenever an IERC721 `token_id` token is transferred to this non-account contract via <> by `operator` from `from`, this function is called. - From 078bf9d2c9ad6a6d8d86b5431cec754e1a5020a3 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 26 Sep 2023 21:48:37 -0400 Subject: [PATCH 165/246] add src5 ids --- docs/modules/ROOT/pages/api/erc721.adoc | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index d1ce82d85..5ab909fb4 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -2,6 +2,9 @@ :eip721: https://eips.ethereum.org/EIPS/eip-721[EIP721] :receiving-tokens: xref:/erc721.adoc#receiving_tokens[Receiving Tokens] :casing-discussion: https://github.com/OpenZeppelin/cairo-contracts/discussions/34[here] +:inner-ierc721: xref:api/erc721.adoc#IERC721[SRC5 ID] +:inner-ierc721metadata: xref:api/erc721.adoc#IERC721Metadata[SRC5 ID] +:inner-ierc721receiver: xref:api/erc721.adoc#IERC721Receiver[SRC5 ID] = ERC721 @@ -21,6 +24,12 @@ use openzeppelin::token::erc721::interface::IERC721; ``` Interface of the IERC721 standard as defined in {eip721}. +[.contract-index] +.{inner-ierc721} +-- +0x33eb2f84c309543403fd69f0d0f363781ef06ef6faeb0131ff16ea3175bd943 +-- + [.contract-index] .Functions -- @@ -173,6 +182,12 @@ use openzeppelin::token::erc721::interface::IERC721Metadata; See {eip721}. +[.contract-index] +.{inner-ierc721metadata} +-- +0x6069a70848f907fa57668ba1875164eb4dcee693952468581406d131081bbd +-- + [.contract-index] .Functions -- @@ -619,6 +634,12 @@ See <>. use openzeppelin::token::erc721::interface::IERC721Receiver; ``` +[.contract-index] +.{inner-ierc721receiver} +-- +0x3a0dff5f70d80458ad14ae37bb182a728e3c8cdda0402a5daa86620bdf910bc +-- + [.contract-index] .Functions -- From 1f96b386624c01c0e684b8b361a443fcccb06ea9 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 26 Sep 2023 21:48:59 -0400 Subject: [PATCH 166/246] add link to src5 id --- docs/modules/ROOT/pages/erc721.adoc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index bb35a29e1..a7b9226ae 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -161,7 +161,7 @@ In Solidity, this means a dynamically-sized array. To be as close as possible to the standard, it accepts a dynamic array of felts. * ERC721 utilizes {src5-api} to declare and query interface support on Starknet as opposed to Ethereum's {eip165}. The design for `SRC5` is similar to OpenZeppelin's {erc165-storage}. -* `IERC721Receiver` compliant contracts return a hardcoded selector id according to Starknet selectors (as opposed to selector calculation in Solidity). +* `IERC721Receiver` compliant contracts return a hardcoded interface ID according to Starknet selectors (as opposed to selector calculation in Solidity). == Usage @@ -279,6 +279,8 @@ The recipient contract must also implement the {src5} interface which, as descri ==== IERC721Receiver +:receiver-id: xref:/api/erc721.adoc#IERC721Receiver[IERC721Receiver interface ID] + [,javascript] ---- trait IERC721Receiver { @@ -292,7 +294,7 @@ trait IERC721Receiver { ---- Implementing the `IERC721Receiver` interface exposes the {on_erc721_received-api} method. -When safe methods such as {safe_transfer_from-api} and {safe_mint-api} are called, they invoke the recipient contract's `on_erc721_received` method which *must* return the IERC721Receiver interface ID. +When safe methods such as {safe_transfer_from-api} and {safe_mint-api} are called, they invoke the recipient contract's `on_erc721_received` method which *must* return the {receiver-id}. Otherwise, the transaction will fail. TIP: For information on how to calculate interface IDs, see {computing-interface-id}. From 251bc2cbfe87076b58790b676f41d03ae70c880a Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 26 Sep 2023 21:49:25 -0400 Subject: [PATCH 167/246] remove extra line --- docs/modules/ROOT/pages/erc721.adoc | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index a7b9226ae..83d35ff55 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -273,7 +273,6 @@ WARNING: Usage of `safe_transfer_from` prevents loss, though the caller must und :safe_transfer_from-api: xref:api/erc721.adoc#IERC721-safe_transfer_from[safe_transfer_from] :safe_mint-api: xref:api/erc721.adoc#ERC721-_safe_mint[_safe_mint] - In order to be sure a non-account contract can safely accept ERC721 tokens, said contract must implement the `IERC721Receiver` interface. The recipient contract must also implement the {src5} interface which, as described earlier, supports interface introspection. From 420f320084033628d8aa4477107d84f2c21e0eea Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Wed, 27 Sep 2023 16:09:54 +0200 Subject: [PATCH 168/246] Sanitizing for release. (#736) * refactor: sanitizing * feat: sanitizing * feat: apply review updates * feat: apply review updates --- src/access/accesscontrol/accesscontrol.cairo | 9 ++--- .../accesscontrol/dual_accesscontrol.cairo | 2 - src/access/ownable/dual_ownable.cairo | 6 --- src/access/ownable/ownable.cairo | 1 - src/account.cairo | 2 - src/account/account.cairo | 33 ++++------------ src/account/dual_account.cairo | 2 - src/account/interface.cairo | 2 - src/introspection/dual_src5.cairo | 1 - src/introspection/src5.cairo | 5 +++ src/tests/access/test_accesscontrol.cairo | 1 - .../access/test_dual_accesscontrol.cairo | 1 - src/tests/access/test_dual_ownable.cairo | 16 +------- src/tests/access/test_ownable.cairo | 2 - src/tests/account/test_account.cairo | 7 +--- src/tests/introspection/test_dual_src5.cairo | 1 - src/tests/mocks/erc721_receiver.cairo | 4 +- src/tests/mocks/upgrades_v1.cairo | 1 - src/tests/mocks/upgrades_v2.cairo | 1 - src/tests/security/test_pausable.cairo | 1 - src/tests/token/test_dual20.cairo | 29 ++------------ src/tests/token/test_dual721.cairo | 38 ++----------------- src/tests/token/test_dual721_receiver.cairo | 30 +-------------- src/tests/token/test_erc20.cairo | 5 --- src/tests/token/test_erc721.cairo | 18 +-------- src/tests/upgrades/test_upgradeable.cairo | 14 +------ src/tests/utils.cairo | 1 - src/tests/utils/constants.cairo | 18 +++++++++ src/token/erc20/dual20.cairo | 1 - src/token/erc20/erc20.cairo | 1 - src/token/erc721/dual721.cairo | 1 - src/token/erc721/dual721_receiver.cairo | 2 - src/token/erc721/erc721.cairo | 14 ++----- src/token/erc721/interface.cairo | 1 - src/upgrades/upgradeable.cairo | 1 - src/utils.cairo | 4 -- src/utils/serde.cairo | 2 - src/utils/unwrap_and_cast.cairo | 4 -- 38 files changed, 51 insertions(+), 231 deletions(-) diff --git a/src/access/accesscontrol/accesscontrol.cairo b/src/access/accesscontrol/accesscontrol.cairo index 0f3aa7535..cd7cdde3a 100644 --- a/src/access/accesscontrol/accesscontrol.cairo +++ b/src/access/accesscontrol/accesscontrol.cairo @@ -7,6 +7,7 @@ mod AccessControl { use openzeppelin::introspection::interface::ISRC5; use openzeppelin::introspection::interface::ISRC5Camel; use openzeppelin::introspection::src5::SRC5; + use openzeppelin::introspection::src5::unsafe_state as src5_state; use starknet::ContractAddress; use starknet::get_caller_address; @@ -66,16 +67,14 @@ mod AccessControl { #[external(v0)] impl SRC5Impl of ISRC5 { fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { - let unsafe_state = SRC5::unsafe_new_contract_state(); - SRC5::SRC5Impl::supports_interface(@unsafe_state, interface_id) + SRC5::SRC5Impl::supports_interface(@src5_state(), interface_id) } } #[external(v0)] impl SRC5CamelImpl of ISRC5Camel { fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { - let unsafe_state = SRC5::unsafe_new_contract_state(); - SRC5::SRC5CamelImpl::supportsInterface(@unsafe_state, interfaceId) + SRC5::SRC5CamelImpl::supportsInterface(@src5_state(), interfaceId) } } @@ -138,7 +137,7 @@ mod AccessControl { #[generate_trait] impl InternalImpl of InternalTrait { fn initializer(ref self: ContractState) { - let mut unsafe_state = SRC5::unsafe_new_contract_state(); + let mut unsafe_state = src5_state(); SRC5::InternalImpl::register_interface(ref unsafe_state, interface::IACCESSCONTROL_ID); } diff --git a/src/access/accesscontrol/dual_accesscontrol.cairo b/src/access/accesscontrol/dual_accesscontrol.cairo index eb086840e..1fa18ff1d 100644 --- a/src/access/accesscontrol/dual_accesscontrol.cairo +++ b/src/access/accesscontrol/dual_accesscontrol.cairo @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.7.0 (access/accesscontrol/dual_accesscontrol.cairo) -use array::ArrayTrait; - use openzeppelin::utils::Felt252TryIntoBool; use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; diff --git a/src/access/ownable/dual_ownable.cairo b/src/access/ownable/dual_ownable.cairo index 03d2ba51b..923f97c5b 100644 --- a/src/access/ownable/dual_ownable.cairo +++ b/src/access/ownable/dual_ownable.cairo @@ -1,21 +1,15 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.7.0 (access/ownable/dual_ownable.cairo) -use array::ArrayTrait; -use array::SpanTrait; -use core::result::ResultTrait; - use openzeppelin::utils::Felt252TryIntoBool; use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; use openzeppelin::utils::try_selector_with_fallback; -use option::OptionTrait; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use starknet::SyscallResultTrait; use starknet::call_contract_syscall; -use traits::TryInto; #[derive(Copy, Drop)] struct DualCaseOwnable { diff --git a/src/access/ownable/ownable.cairo b/src/access/ownable/ownable.cairo index 856de8190..9fac9da4a 100644 --- a/src/access/ownable/ownable.cairo +++ b/src/access/ownable/ownable.cairo @@ -6,7 +6,6 @@ mod Ownable { use openzeppelin::access::ownable::interface; use starknet::ContractAddress; use starknet::get_caller_address; - use zeroable::Zeroable; #[storage] struct Storage { diff --git a/src/account.cairo b/src/account.cairo index 1ca2c1578..501227b0b 100644 --- a/src/account.cairo +++ b/src/account.cairo @@ -3,8 +3,6 @@ mod dual_account; mod interface; use account::Account; -use account::QUERY_VERSION; -use account::TRANSACTION_VERSION; use interface::AccountABIDispatcher; use interface::AccountABIDispatcherTrait; use interface::AccountCamelABIDispatcher; diff --git a/src/account/account.cairo b/src/account/account.cairo index 90408a30f..ef73ef44a 100644 --- a/src/account/account.cairo +++ b/src/account/account.cairo @@ -1,18 +1,6 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.7.0 (account/account.cairo) -use array::ArrayTrait; -use array::SpanTrait; -use option::OptionTrait; -use serde::Serde; -use starknet::ContractAddress; -use starknet::account::Call; - -const TRANSACTION_VERSION: felt252 = 1; - -// 2**128 + TRANSACTION_VERSION -const QUERY_VERSION: felt252 = 340282366920938463463374607431768211457; - trait PublicKeyTrait { fn set_public_key(ref self: TState, new_public_key: felt252); fn get_public_key(self: @TState) -> felt252; @@ -25,24 +13,21 @@ trait PublicKeyCamelTrait { #[starknet::contract] mod Account { - use array::ArrayTrait; - use array::SpanTrait; - use box::BoxTrait; use ecdsa::check_ecdsa_signature; use openzeppelin::account::interface; use openzeppelin::introspection::interface::ISRC5; use openzeppelin::introspection::interface::ISRC5Camel; use openzeppelin::introspection::src5::SRC5; - use option::OptionTrait; + use openzeppelin::introspection::src5::unsafe_state as src5_state; + use starknet::account::Call; use starknet::get_caller_address; use starknet::get_contract_address; use starknet::get_tx_info; - use super::Call; - use super::QUERY_VERSION; - use super::TRANSACTION_VERSION; - use zeroable::Zeroable; + const TRANSACTION_VERSION: felt252 = 1; + // 2**128 + TRANSACTION_VERSION + const QUERY_VERSION: felt252 = 0x100000000000000000000000000000001; #[storage] struct Storage { @@ -134,16 +119,14 @@ mod Account { #[external(v0)] impl SRC5Impl of ISRC5 { fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { - let unsafe_state = SRC5::unsafe_new_contract_state(); - SRC5::SRC5Impl::supports_interface(@unsafe_state, interface_id) + SRC5::SRC5Impl::supports_interface(@src5_state(), interface_id) } } #[external(v0)] impl SRC5CamelImpl of ISRC5Camel { fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { - let unsafe_state = SRC5::unsafe_new_contract_state(); - SRC5::SRC5CamelImpl::supportsInterface(@unsafe_state, interfaceId) + SRC5::SRC5CamelImpl::supportsInterface(@src5_state(), interfaceId) } } @@ -188,7 +171,7 @@ mod Account { #[generate_trait] impl InternalImpl of InternalTrait { fn initializer(ref self: ContractState, _public_key: felt252) { - let mut unsafe_state = SRC5::unsafe_new_contract_state(); + let mut unsafe_state = src5_state(); SRC5::InternalImpl::register_interface(ref unsafe_state, interface::ISRC6_ID); self._set_public_key(_public_key); } diff --git a/src/account/dual_account.cairo b/src/account/dual_account.cairo index 9e4d321c8..0f0ddfdee 100644 --- a/src/account/dual_account.cairo +++ b/src/account/dual_account.cairo @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.7.0 (account/dual_account.cairo) -use array::ArrayTrait; -use array::SpanTrait; use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; diff --git a/src/account/interface.cairo b/src/account/interface.cairo index b9a5abbef..e58c5bb30 100644 --- a/src/account/interface.cairo +++ b/src/account/interface.cairo @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.7.0 (account/interface.cairo) -use array::ArrayTrait; -use array::SpanTrait; use starknet::ContractAddress; use starknet::account::Call; diff --git a/src/introspection/dual_src5.cairo b/src/introspection/dual_src5.cairo index 48d7c9c68..6d68e89ec 100644 --- a/src/introspection/dual_src5.cairo +++ b/src/introspection/dual_src5.cairo @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.7.0 (introspection/dual_src5.cairo) -use array::ArrayTrait; use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; use openzeppelin::utils::try_selector_with_fallback; diff --git a/src/introspection/src5.cairo b/src/introspection/src5.cairo index 3b94f4218..ec8ec85b5 100644 --- a/src/introspection/src5.cairo +++ b/src/introspection/src5.cairo @@ -43,3 +43,8 @@ mod SRC5 { } } } + +#[inline(always)] +fn unsafe_state() -> SRC5::ContractState { + SRC5::unsafe_new_contract_state() +} diff --git a/src/tests/access/test_accesscontrol.cairo b/src/tests/access/test_accesscontrol.cairo index c8aeab26b..d86dd4490 100644 --- a/src/tests/access/test_accesscontrol.cairo +++ b/src/tests/access/test_accesscontrol.cairo @@ -11,7 +11,6 @@ use openzeppelin::tests::utils::constants::{ ADMIN, AUTHORIZED, OTHER, OTHER_ADMIN, ROLE, OTHER_ROLE, ZERO }; use openzeppelin::tests::utils; -use option::OptionTrait; use starknet::ContractAddress; use starknet::contract_address_const; use starknet::testing; diff --git a/src/tests/access/test_dual_accesscontrol.cairo b/src/tests/access/test_dual_accesscontrol.cairo index 0a2f3527d..b7f922963 100644 --- a/src/tests/access/test_dual_accesscontrol.cairo +++ b/src/tests/access/test_dual_accesscontrol.cairo @@ -1,4 +1,3 @@ -use array::ArrayTrait; use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; use openzeppelin::access::accesscontrol::interface::IACCESSCONTROL_ID; use openzeppelin::access::accesscontrol::interface::IAccessControlDispatcherTrait; diff --git a/src/tests/access/test_dual_ownable.cairo b/src/tests/access/test_dual_ownable.cairo index 8055bd1fc..40f736bf5 100644 --- a/src/tests/access/test_dual_ownable.cairo +++ b/src/tests/access/test_dual_ownable.cairo @@ -9,25 +9,11 @@ use openzeppelin::tests::mocks::dual_ownable_mocks::CamelOwnablePanicMock; use openzeppelin::tests::mocks::dual_ownable_mocks::SnakeOwnableMock; use openzeppelin::tests::mocks::dual_ownable_mocks::SnakeOwnablePanicMock; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; +use openzeppelin::tests::utils::constants::{OWNER, NEW_OWNER}; use openzeppelin::tests::utils; use openzeppelin::utils::serde::SerializedAppend; -use starknet::ContractAddress; -use starknet::contract_address_const; use starknet::testing::set_caller_address; use starknet::testing::set_contract_address; -use zeroable::Zeroable; - -// -// Constants -// - -fn OWNER() -> ContractAddress { - contract_address_const::<10>() -} - -fn NEW_OWNER() -> ContractAddress { - contract_address_const::<20>() -} // // Setup diff --git a/src/tests/access/test_ownable.cairo b/src/tests/access/test_ownable.cairo index 5c68a490a..8bb58ac01 100644 --- a/src/tests/access/test_ownable.cairo +++ b/src/tests/access/test_ownable.cairo @@ -6,11 +6,9 @@ use openzeppelin::access::ownable::Ownable::Ownable_owner::InternalContractMembe use openzeppelin::access::ownable::Ownable; use openzeppelin::tests::utils::constants::{ZERO, OTHER, OWNER}; use openzeppelin::tests::utils; -use option::OptionTrait; use starknet::ContractAddress; use starknet::contract_address_const; use starknet::testing; -use zeroable::Zeroable; // // Setup diff --git a/src/tests/account/test_account.cairo b/src/tests/account/test_account.cairo index 83bea533b..2ca9550a8 100644 --- a/src/tests/account/test_account.cairo +++ b/src/tests/account/test_account.cairo @@ -1,5 +1,3 @@ -use array::ArrayTrait; -use core::traits::Into; use openzeppelin::account::Account::OwnerAdded; use openzeppelin::account::Account::OwnerRemoved; use openzeppelin::account::Account::PublicKeyCamelImpl; @@ -7,8 +5,7 @@ use openzeppelin::account::Account::PublicKeyImpl; use openzeppelin::account::Account; use openzeppelin::account::AccountABIDispatcher; use openzeppelin::account::AccountABIDispatcherTrait; -use openzeppelin::account::QUERY_VERSION; -use openzeppelin::account::TRANSACTION_VERSION; +use openzeppelin::account::Account::{TRANSACTION_VERSION, QUERY_VERSION}; use openzeppelin::account::interface::ISRC6_ID; use openzeppelin::introspection::interface::ISRC5_ID; use openzeppelin::tests::utils; @@ -18,8 +15,6 @@ use openzeppelin::token::erc20::interface::IERC20Dispatcher; use openzeppelin::token::erc20::interface::IERC20DispatcherTrait; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; -use option::OptionTrait; -use serde::Serde; use starknet::ContractAddress; use starknet::account::Call; use starknet::contract_address_const; diff --git a/src/tests/introspection/test_dual_src5.cairo b/src/tests/introspection/test_dual_src5.cairo index f2122f2e1..9720e8968 100644 --- a/src/tests/introspection/test_dual_src5.cairo +++ b/src/tests/introspection/test_dual_src5.cairo @@ -1,4 +1,3 @@ -use array::ArrayTrait; use openzeppelin::introspection::dual_src5::DualCaseSRC5; use openzeppelin::introspection::dual_src5::DualCaseSRC5Trait; use openzeppelin::introspection::interface::ISRC5CamelDispatcher; diff --git a/src/tests/mocks/erc721_receiver.cairo b/src/tests/mocks/erc721_receiver.cairo index 9205f6d97..240246019 100644 --- a/src/tests/mocks/erc721_receiver.cairo +++ b/src/tests/mocks/erc721_receiver.cairo @@ -1,9 +1,7 @@ -const SUCCESS: felt252 = 123123; -const FAILURE: felt252 = 456456; +use openzeppelin::tests::utils::constants::{FAILURE, SUCCESS}; #[starknet::contract] mod ERC721Receiver { - use array::SpanTrait; use openzeppelin::introspection::interface::ISRC5; use openzeppelin::introspection::interface::ISRC5Camel; use openzeppelin::introspection::src5::SRC5; diff --git a/src/tests/mocks/upgrades_v1.cairo b/src/tests/mocks/upgrades_v1.cairo index d851f59d9..37d5c19b3 100644 --- a/src/tests/mocks/upgrades_v1.cairo +++ b/src/tests/mocks/upgrades_v1.cairo @@ -2,7 +2,6 @@ // The functions are NOT PROTECTED. // DO NOT USE IN PRODUCTION. -use array::ArrayTrait; use starknet::ClassHash; #[starknet::interface] diff --git a/src/tests/mocks/upgrades_v2.cairo b/src/tests/mocks/upgrades_v2.cairo index f0ad60ee7..322c47ba1 100644 --- a/src/tests/mocks/upgrades_v2.cairo +++ b/src/tests/mocks/upgrades_v2.cairo @@ -2,7 +2,6 @@ // The functions are NOT PROTECTED. // DO NOT USE IN PRODUCTION. -use array::ArrayTrait; use starknet::ClassHash; #[starknet::interface] diff --git a/src/tests/security/test_pausable.cairo b/src/tests/security/test_pausable.cairo index 26497e121..faf1fcfc6 100644 --- a/src/tests/security/test_pausable.cairo +++ b/src/tests/security/test_pausable.cairo @@ -5,7 +5,6 @@ use openzeppelin::security::pausable::Pausable::Unpaused; use openzeppelin::security::pausable::Pausable; use openzeppelin::tests::utils::constants::{CALLER, ZERO}; use openzeppelin::tests::utils; -use option::OptionTrait; use starknet::contract_address_const; use starknet::ContractAddress; use starknet::testing; diff --git a/src/tests/token/test_dual20.cairo b/src/tests/token/test_dual20.cairo index aa0bd2306..948e1f882 100644 --- a/src/tests/token/test_dual20.cairo +++ b/src/tests/token/test_dual20.cairo @@ -1,9 +1,11 @@ -use array::ArrayTrait; use openzeppelin::tests::mocks::camel20_mock::CamelERC20Mock; use openzeppelin::tests::mocks::erc20_panic::CamelERC20Panic; use openzeppelin::tests::mocks::erc20_panic::SnakeERC20Panic; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::mocks::snake20_mock::SnakeERC20Mock; +use openzeppelin::tests::utils::constants::{ + OWNER, RECIPIENT, SPENDER, OPERATOR, NAME, SYMBOL, DECIMALS, SUPPLY, VALUE +}; use openzeppelin::tests::utils; use openzeppelin::token::erc20::dual20::DualCaseERC20; use openzeppelin::token::erc20::dual20::DualCaseERC20Trait; @@ -12,33 +14,8 @@ use openzeppelin::token::erc20::interface::IERC20CamelDispatcherTrait; use openzeppelin::token::erc20::interface::IERC20Dispatcher; use openzeppelin::token::erc20::interface::IERC20DispatcherTrait; use openzeppelin::utils::serde::SerializedAppend; -use starknet::ContractAddress; -use starknet::contract_address_const; use starknet::testing::set_contract_address; -// -// Constants -// - -const NAME: felt252 = 111; -const SYMBOL: felt252 = 222; -const DECIMALS: u8 = 18_u8; -const SUPPLY: u256 = 2000; -const VALUE: u256 = 300; - -fn OWNER() -> ContractAddress { - contract_address_const::<10>() -} -fn SPENDER() -> ContractAddress { - contract_address_const::<20>() -} -fn RECIPIENT() -> ContractAddress { - contract_address_const::<30>() -} -fn OPERATOR() -> ContractAddress { - contract_address_const::<40>() -} - // // Setup // diff --git a/src/tests/token/test_dual721.cairo b/src/tests/token/test_dual721.cairo index fc65cc426..0beb985a4 100644 --- a/src/tests/token/test_dual721.cairo +++ b/src/tests/token/test_dual721.cairo @@ -1,12 +1,12 @@ -use array::ArrayTrait; use openzeppelin::tests::mocks::camel721_mock::CamelERC721Mock; use openzeppelin::tests::mocks::erc721_panic_mock::CamelERC721PanicMock; use openzeppelin::tests::mocks::erc721_panic_mock::SnakeERC721PanicMock; use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; -use openzeppelin::tests::mocks::erc721_receiver::FAILURE; -use openzeppelin::tests::mocks::erc721_receiver::SUCCESS; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::mocks::snake721_mock::SnakeERC721Mock; +use openzeppelin::tests::utils::constants::{ + DATA, OWNER, RECIPIENT, SPENDER, OPERATOR, OTHER, NAME, SYMBOL, URI, TOKEN_ID +}; use openzeppelin::tests::utils; use openzeppelin::token::erc721::dual721::DualCaseERC721; use openzeppelin::token::erc721::dual721::DualCaseERC721Trait; @@ -17,41 +17,9 @@ use openzeppelin::token::erc721::interface::IERC721DispatcherTrait; use openzeppelin::token::erc721::interface::IERC721_ID; use openzeppelin::utils::serde::SerializedAppend; use starknet::ContractAddress; -use starknet::contract_address_const; use starknet::testing::set_caller_address; use starknet::testing::set_contract_address; -// -// Constants -// - -const NAME: felt252 = 111; -const SYMBOL: felt252 = 222; -const URI: felt252 = 333; -const TOKEN_ID: u256 = 7; - -fn OWNER() -> ContractAddress { - contract_address_const::<10>() -} -fn RECIPIENT() -> ContractAddress { - contract_address_const::<20>() -} -fn SPENDER() -> ContractAddress { - contract_address_const::<30>() -} -fn OPERATOR() -> ContractAddress { - contract_address_const::<40>() -} -fn DATA(success: bool) -> Span { - let mut data = array![]; - if success { - data.append_serde(SUCCESS); - } else { - data.append_serde(FAILURE); - } - data.span() -} - // // Setup // diff --git a/src/tests/token/test_dual721_receiver.cairo b/src/tests/token/test_dual721_receiver.cairo index 876661b8c..84644dcf2 100644 --- a/src/tests/token/test_dual721_receiver.cairo +++ b/src/tests/token/test_dual721_receiver.cairo @@ -1,11 +1,9 @@ -use array::ArrayTrait; use openzeppelin::tests::mocks::dual721_receiver_mocks::CamelERC721ReceiverMock; use openzeppelin::tests::mocks::dual721_receiver_mocks::CamelERC721ReceiverPanicMock; use openzeppelin::tests::mocks::dual721_receiver_mocks::SnakeERC721ReceiverMock; use openzeppelin::tests::mocks::dual721_receiver_mocks::SnakeERC721ReceiverPanicMock; -use openzeppelin::tests::mocks::erc721_receiver::FAILURE; -use openzeppelin::tests::mocks::erc721_receiver::SUCCESS; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; +use openzeppelin::tests::utils::constants::{DATA, OPERATOR, OWNER, TOKEN_ID}; use openzeppelin::tests::utils; use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721Receiver; use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721ReceiverTrait; @@ -14,32 +12,6 @@ use openzeppelin::token::erc721::interface::IERC721ReceiverCamelDispatcherTrait; use openzeppelin::token::erc721::interface::IERC721ReceiverDispatcher; use openzeppelin::token::erc721::interface::IERC721ReceiverDispatcherTrait; use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; -use starknet::ContractAddress; -use starknet::contract_address_const; - -// -// Constants -// - -const TOKEN_ID: u256 = 7; - -fn DATA(success: bool) -> Span { - let mut data = ArrayTrait::new(); - if success { - data.append(SUCCESS); - } else { - data.append(FAILURE); - } - data.span() -} - -fn OWNER() -> ContractAddress { - contract_address_const::<10>() -} - -fn OPERATOR() -> ContractAddress { - contract_address_const::<20>() -} // // Setup diff --git a/src/tests/token/test_erc20.cairo b/src/tests/token/test_erc20.cairo index 4109e909a..6ff440859 100644 --- a/src/tests/token/test_erc20.cairo +++ b/src/tests/token/test_erc20.cairo @@ -1,6 +1,4 @@ use integer::BoundedInt; -use integer::u256; -use integer::u256_from_felt252; use openzeppelin::tests::utils::constants::{ ZERO, OWNER, SPENDER, RECIPIENT, NAME, SYMBOL, DECIMALS, SUPPLY, VALUE }; @@ -11,12 +9,9 @@ use openzeppelin::token::erc20::ERC20::ERC20Impl; use openzeppelin::token::erc20::ERC20::InternalImpl; use openzeppelin::token::erc20::ERC20::Transfer; use openzeppelin::token::erc20::ERC20; -use option::OptionTrait; use starknet::ContractAddress; use starknet::contract_address_const; use starknet::testing; -use traits::Into; -use zeroable::Zeroable; // // Setup diff --git a/src/tests/token/test_erc721.cairo b/src/tests/token/test_erc721.cairo index 25265d7e4..4299928f9 100644 --- a/src/tests/token/test_erc721.cairo +++ b/src/tests/token/test_erc721.cairo @@ -1,7 +1,6 @@ use ERC721::ERC721_owners::InternalContractMemberStateTrait as OwnersTrait; use ERC721::ERC721_token_approvals::InternalContractMemberStateTrait as TokenApprovalsTrait; -use array::ArrayTrait; use integer::u256; use integer::u256_from_felt252; use openzeppelin::account::Account; @@ -10,11 +9,9 @@ use openzeppelin::introspection; use openzeppelin::tests::mocks::camel_account_mock::CamelAccountMock; use openzeppelin::tests::mocks::dual721_receiver_mocks::CamelERC721ReceiverMock; use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; -use openzeppelin::tests::mocks::erc721_receiver::FAILURE; -use openzeppelin::tests::mocks::erc721_receiver::SUCCESS; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils::constants::{ - ZERO, OWNER, RECIPIENT, SPENDER, OPERATOR, OTHER, NAME, SYMBOL, URI, TOKEN_ID, PUBKEY, + DATA, ZERO, OWNER, RECIPIENT, SPENDER, OPERATOR, OTHER, NAME, SYMBOL, URI, TOKEN_ID, PUBKEY, }; use openzeppelin::tests::utils; use openzeppelin::token::erc721::ERC721::{ @@ -23,22 +20,9 @@ use openzeppelin::token::erc721::ERC721::{ }; use openzeppelin::token::erc721::ERC721; use openzeppelin::token::erc721; -use option::OptionTrait; use starknet::contract_address_const; use starknet::ContractAddress; use starknet::testing; -use traits::Into; -use zeroable::Zeroable; - -fn DATA(success: bool) -> Span { - let mut data = array![]; - if success { - data.append(SUCCESS); - } else { - data.append(FAILURE); - } - data.span() -} // // Setup diff --git a/src/tests/upgrades/test_upgradeable.cairo b/src/tests/upgrades/test_upgradeable.cairo index 20b9c48c3..c43f8518f 100644 --- a/src/tests/upgrades/test_upgradeable.cairo +++ b/src/tests/upgrades/test_upgradeable.cairo @@ -1,20 +1,16 @@ -use array::ArrayTrait; use openzeppelin::tests::mocks::upgrades_v1::IUpgradesV1Dispatcher; use openzeppelin::tests::mocks::upgrades_v1::IUpgradesV1DispatcherTrait; use openzeppelin::tests::mocks::upgrades_v2::IUpgradesV2Dispatcher; use openzeppelin::tests::mocks::upgrades_v2::IUpgradesV2DispatcherTrait; use openzeppelin::tests::mocks::upgrades_v1::UpgradesV1; use openzeppelin::tests::mocks::upgrades_v2::UpgradesV2; +use openzeppelin::tests::utils::constants::{CLASS_HASH_ZERO, ZERO}; use openzeppelin::tests::utils; use openzeppelin::upgrades::upgradeable::Upgradeable::Upgraded; -use option::OptionTrait; use starknet::ClassHash; use starknet::ContractAddress; use starknet::Felt252TryIntoClassHash; -use starknet::class_hash_const; -use starknet::contract_address_const; use starknet::testing; -use traits::TryInto; const VALUE: felt252 = 123; @@ -22,14 +18,6 @@ fn V2_CLASS_HASH() -> ClassHash { UpgradesV2::TEST_CLASS_HASH.try_into().unwrap() } -fn CLASS_HASH_ZERO() -> ClassHash { - class_hash_const::<0>() -} - -fn ZERO() -> ContractAddress { - contract_address_const::<0>() -} - // // Setup // diff --git a/src/tests/utils.cairo b/src/tests/utils.cairo index 77d711c53..e0ebeacb3 100644 --- a/src/tests/utils.cairo +++ b/src/tests/utils.cairo @@ -7,7 +7,6 @@ use option::OptionTrait; use starknet::class_hash::Felt252TryIntoClassHash; use starknet::ContractAddress; use starknet::testing; -use traits::TryInto; fn deploy(contract_class_hash: felt252, calldata: Array) -> ContractAddress { let (address, _) = starknet::deploy_syscall( diff --git a/src/tests/utils/constants.cairo b/src/tests/utils/constants.cairo index a66b6e721..73d22eb41 100644 --- a/src/tests/utils/constants.cairo +++ b/src/tests/utils/constants.cairo @@ -1,4 +1,6 @@ +use starknet::ClassHash; use starknet::ContractAddress; +use starknet::class_hash_const; use starknet::contract_address_const; const NAME: felt252 = 'NAME'; @@ -11,6 +13,8 @@ const OTHER_ROLE: felt252 = 'OTHER_ROLE'; const URI: felt252 = 'URI'; const TOKEN_ID: u256 = 21; const PUBKEY: felt252 = 'PUBKEY'; +const SUCCESS: felt252 = 123123; +const FAILURE: felt252 = 456456; fn ADMIN() -> ContractAddress { contract_address_const::<'ADMIN'>() @@ -24,6 +28,10 @@ fn ZERO() -> ContractAddress { contract_address_const::<0>() } +fn CLASS_HASH_ZERO() -> ClassHash { + class_hash_const::<0>() +} + fn CALLER() -> ContractAddress { contract_address_const::<'CALLER'>() } @@ -55,3 +63,13 @@ fn RECIPIENT() -> ContractAddress { fn OPERATOR() -> ContractAddress { contract_address_const::<'OPERATOR'>() } + +fn DATA(success: bool) -> Span { + let mut data = array![]; + if success { + data.append(SUCCESS); + } else { + data.append(FAILURE); + } + data.span() +} diff --git a/src/token/erc20/dual20.cairo b/src/token/erc20/dual20.cairo index dfb54584b..bbd8d8aae 100644 --- a/src/token/erc20/dual20.cairo +++ b/src/token/erc20/dual20.cairo @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.7.0 (token/erc20/dual20.cairo) -use array::ArrayTrait; use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; diff --git a/src/token/erc20/erc20.cairo b/src/token/erc20/erc20.cairo index 36b32c593..c62cd3803 100644 --- a/src/token/erc20/erc20.cairo +++ b/src/token/erc20/erc20.cairo @@ -8,7 +8,6 @@ mod ERC20 { use openzeppelin::token::erc20::interface::IERC20CamelOnly; use starknet::ContractAddress; use starknet::get_caller_address; - use zeroable::Zeroable; #[storage] struct Storage { diff --git a/src/token/erc721/dual721.cairo b/src/token/erc721/dual721.cairo index 041f267a2..2df75e071 100644 --- a/src/token/erc721/dual721.cairo +++ b/src/token/erc721/dual721.cairo @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/dual721.cairo) -use array::ArrayTrait; use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; diff --git a/src/token/erc721/dual721_receiver.cairo b/src/token/erc721/dual721_receiver.cairo index 650285d6d..a32011a37 100644 --- a/src/token/erc721/dual721_receiver.cairo +++ b/src/token/erc721/dual721_receiver.cairo @@ -1,13 +1,11 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/dual721_receiver.cairo) -use array::ArrayTrait; use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; use openzeppelin::utils::try_selector_with_fallback; use starknet::ContractAddress; -use starknet::SyscallResultTrait; #[derive(Copy, Drop)] struct DualCaseERC721Receiver { diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 660a92919..d67952f2d 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -1,24 +1,20 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/erc721.cairo) -use starknet::ContractAddress; - #[starknet::contract] mod ERC721 { - use array::SpanTrait; use openzeppelin::account; use openzeppelin::introspection::dual_src5::DualCaseSRC5; use openzeppelin::introspection::dual_src5::DualCaseSRC5Trait; use openzeppelin::introspection::interface::ISRC5; use openzeppelin::introspection::interface::ISRC5Camel; + use openzeppelin::introspection::src5::unsafe_state as src5_state; use openzeppelin::introspection::src5; use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721Receiver; use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721ReceiverTrait; use openzeppelin::token::erc721::interface; - use option::OptionTrait; use starknet::ContractAddress; use starknet::get_caller_address; - use zeroable::Zeroable; #[storage] struct Storage { @@ -92,16 +88,14 @@ mod ERC721 { #[external(v0)] impl SRC5Impl of ISRC5 { fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { - let unsafe_state = src5::SRC5::unsafe_new_contract_state(); - src5::SRC5::SRC5Impl::supports_interface(@unsafe_state, interface_id) + src5::SRC5::SRC5Impl::supports_interface(@src5_state(), interface_id) } } #[external(v0)] impl SRC5CamelImpl of ISRC5Camel { fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { - let unsafe_state = src5::SRC5::unsafe_new_contract_state(); - src5::SRC5::SRC5CamelImpl::supportsInterface(@unsafe_state, interfaceId) + src5::SRC5::SRC5CamelImpl::supportsInterface(@src5_state(), interfaceId) } } @@ -242,7 +236,7 @@ mod ERC721 { self.ERC721_name.write(name); self.ERC721_symbol.write(symbol); - let mut unsafe_state = src5::SRC5::unsafe_new_contract_state(); + let mut unsafe_state = src5_state(); src5::SRC5::InternalImpl::register_interface(ref unsafe_state, interface::IERC721_ID); src5::SRC5::InternalImpl::register_interface( ref unsafe_state, interface::IERC721_METADATA_ID diff --git a/src/token/erc721/interface.cairo b/src/token/erc721/interface.cairo index 86125721d..6fdc68272 100644 --- a/src/token/erc721/interface.cairo +++ b/src/token/erc721/interface.cairo @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/interface.cairo) -use array::SpanTrait; use starknet::ContractAddress; const IERC721_ID: felt252 = 0x33eb2f84c309543403fd69f0d0f363781ef06ef6faeb0131ff16ea3175bd943; diff --git a/src/upgrades/upgradeable.cairo b/src/upgrades/upgradeable.cairo index b10978d85..de9e546f2 100644 --- a/src/upgrades/upgradeable.cairo +++ b/src/upgrades/upgradeable.cairo @@ -2,7 +2,6 @@ mod Upgradeable { use starknet::ClassHash; use starknet::SyscallResult; - use zeroable::Zeroable; #[storage] struct Storage {} diff --git a/src/utils.cairo b/src/utils.cairo index 38b0b35dd..5396a17f6 100644 --- a/src/utils.cairo +++ b/src/utils.cairo @@ -6,10 +6,6 @@ mod selectors; mod serde; mod unwrap_and_cast; -use array::ArrayTrait; -use array::SpanTrait; -use box::BoxTrait; -use option::OptionTrait; use starknet::ContractAddress; use starknet::SyscallResult; use starknet::SyscallResultTrait; diff --git a/src/utils/serde.cairo b/src/utils/serde.cairo index 23fdad21b..2a4951355 100644 --- a/src/utils/serde.cairo +++ b/src/utils/serde.cairo @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.7.0 (utils/serde.cairo) -use serde::Serde; - trait SerializedAppend { fn append_serde(ref self: Array, value: T); } diff --git a/src/utils/unwrap_and_cast.cairo b/src/utils/unwrap_and_cast.cairo index 9e58009a2..f625c08a2 100644 --- a/src/utils/unwrap_and_cast.cairo +++ b/src/utils/unwrap_and_cast.cairo @@ -1,15 +1,11 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.7.0 (utils/unwrap_and_cast.cairo) -use array::SpanTrait; - use openzeppelin::utils::Felt252TryIntoBool; -use option::OptionTrait; use starknet::ContractAddress; use starknet::Felt252TryIntoContractAddress; use starknet::SyscallResult; use starknet::SyscallResultTrait; -use traits::TryInto; trait UnwrapAndCast { fn unwrap_and_cast(self: SyscallResult>) -> T; From 568f14c695ecdf72118c5ab7e7f7eb3bf6132948 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 27 Sep 2023 10:37:37 -0400 Subject: [PATCH 169/246] move reqs to preset api --- docs/modules/ROOT/pages/api/erc721.adoc | 72 ++++++++++++------------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 5ab909fb4..703232fde 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -68,29 +68,15 @@ Returns the number of NFTs owned by `account`. Returns the owner address of `token_id`. -Requirements: - -- `token_id` exists. - [.contract-item] [[IERC721-safe_transfer_from]] ==== `[.contract-item-name]#++safe_transfer_from++#++(from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#external# -Safely transfer ownership of `token_id` from `from` to `to`, checking first -that `to` is aware of the ERC721 protocol to prevent tokens being locked -forever. For information regarding how contracts communicate their -awareness of the ERC721 protocol, see {receiving-tokens}. +Safely transfer ownership of `token_id` from `from` to `to`, checking first that `to` is aware of the ERC721 protocol to prevent tokens being locked forever. +For information regarding how contracts communicate their awareness of the ERC721 protocol, see {receiving-tokens}. Emits a <> event. -Requirements: - -- Caller is either approved or the `token_id` owner. -- `to` is not the zero address. -- `from` is not the zero address. -- `token_id` exists. -- `to` is either an account contract or supports the <> interface. - [.contract-item] [[IERC721-transfer_from]] ==== `[.contract-item-name]#++transfer_from++#++(from: ContractAddress, to: ContractAddress, token_id: u256)++` [.item-kind]#external# @@ -102,13 +88,6 @@ Usage of <> prevents los Emits a <> event. -Requirements: - -- Caller either approved or the `token_id` owner. -- `to` is not the zero address. -- `from` is not the zero address. -- `token_id` exists. - [.contract-item] [[IERC721-approve]] ==== `[.contract-item-name]#++approve++#++(to: ContractAddress, token_id: u256)++` [.item-kind]#external# @@ -117,12 +96,6 @@ Change or reaffirm the approved address for an NFT. Emits an <> event. -Requirements: - -- The caller is either an approved operator or the `token_id` owner. -- `to` cannot be the token owner or the zero address. -- `token_id` exists. - [.contract-item] [[IERC721-set_approval_for_all]] ==== `[.contract-item-name]#++set_approval_for_all++#++(operator: ContractAddress, approved: bool)++` [.item-kind]#external# @@ -131,20 +104,12 @@ Enable or disable approval for `operator` to manage all of the caller's assets. Emits an <> event. -Requirements: - -- `operator` cannot be the caller. - [.contract-item] [[IERC721-get_approved]] ==== `[.contract-item-name]#++get_approved++#++(token_id: u256) -> u256++` [.item-kind]#external# Returns the address approved for `token_id`. -Requirements: - -- `token_id` exists. - [.contract-item] [[IERC721-is_approved_for_all]] ==== `[.contract-item-name]#++is_approved_for_all++#++(owner: ContractAddress, operator: ContractAddress) -> bool++` [.item-kind]#external# @@ -341,36 +306,69 @@ See <>. See <>. +Requirements: + +- `token_id` exists. + [.contract-item] [[ERC721-safe_transfer_from]] ==== `[.contract-item-name]#++safe_transfer_from++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#external# See <>. +Requirements: + +- Caller is either approved or the `token_id` owner. +- `to` is not the zero address. +- `from` is not the zero address. +- `token_id` exists. +- `to` is either an account contract or supports the <> interface. + [.contract-item] [[ERC721-transfer_from]] ==== `[.contract-item-name]#++transfer_from++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256)++` [.item-kind]#external# See <>. +Requirements: + +- Caller either approved or the `token_id` owner. +- `to` is not the zero address. +- `from` is not the zero address. +- `token_id` exists. + [.contract-item] [[ERC721-approve]] ==== `[.contract-item-name]#++approve++#++(ref self: ContractState, to: ContractAddress, token_id: u256)++` [.item-kind]#external# See <>. +Requirements: + +- The caller is either an approved operator or the `token_id` owner. +- `to` cannot be the token owner or the zero address. +- `token_id` exists. + [.contract-item] [[ERC721-set_approval_for_all]] ==== `[.contract-item-name]#++set_approval_for_all++#++(ref self: ContractState, operator: ContractAddress, approved: bool)++` [.item-kind]#external# See <>. +Requirements: + +- `operator` cannot be the caller. + [.contract-item] [[ERC721-get_approved]] ==== `[.contract-item-name]#++get_approved++#++(self: @ContractState, token_id: u256) -> u256++` [.item-kind]#external# See <>. +Requirements: + +- `token_id` exists. + [.contract-item] [[ERC721-is_approved_for_all]] ==== `[.contract-item-name]#++is_approved_for_all++#++(self: @ContractState, owner: ContractAddress, operator: ContractAddress) -> bool++` [.item-kind]#external# From 409c01aa9b2a626dc6a9a018ce3d6a85fdb74bb0 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 27 Sep 2023 14:22:43 -0400 Subject: [PATCH 170/246] fix description --- docs/modules/ROOT/pages/api/erc721.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 703232fde..3aff73ca8 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -578,11 +578,11 @@ Requirements: [[ERC721-_safe_transfer]] ==== `[.contract-item-name]#++_safe_transfer++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#internal# -Internal function that safely transfers `token_id` token from `from` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. +Safely transfers `token_id` token from `from` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. `data` is additional data, it has no specified format and it is sent in call to `to`. -This function is equivalent to <>, and can be used to e.g. implement alternative mechanisms to perform signature-based token transfers. +This internal function does not include permissions but can be useful for instances like implementing alternative mechanism to perform signature-based token transfers. Emits an <> event. From 8974c68541af6b8740576e10eab42b1c8e931f6c Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 27 Sep 2023 14:23:28 -0400 Subject: [PATCH 171/246] fix typo --- docs/modules/ROOT/pages/api/erc721.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 3aff73ca8..97fefb552 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -582,7 +582,7 @@ Safely transfers `token_id` token from `from` to `to`, checking first that contr `data` is additional data, it has no specified format and it is sent in call to `to`. -This internal function does not include permissions but can be useful for instances like implementing alternative mechanism to perform signature-based token transfers. +This internal function does not include permissions but can be useful for instances like implementing alternative mechanisms to perform signature-based token transfers. Emits an <> event. From fd9bb816423b36108ece5d2f387f5ffd5b1a429c Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 28 Sep 2023 13:54:25 -0400 Subject: [PATCH 172/246] fix src5 link --- docs/modules/ROOT/pages/api/erc721.adoc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 97fefb552..1f76a36a2 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -2,9 +2,7 @@ :eip721: https://eips.ethereum.org/EIPS/eip-721[EIP721] :receiving-tokens: xref:/erc721.adoc#receiving_tokens[Receiving Tokens] :casing-discussion: https://github.com/OpenZeppelin/cairo-contracts/discussions/34[here] -:inner-ierc721: xref:api/erc721.adoc#IERC721[SRC5 ID] -:inner-ierc721metadata: xref:api/erc721.adoc#IERC721Metadata[SRC5 ID] -:inner-ierc721receiver: xref:api/erc721.adoc#IERC721Receiver[SRC5 ID] +:inner-src5: xref:api/introspection.adoc#ISRC5[SRC5 ID] = ERC721 @@ -25,7 +23,7 @@ use openzeppelin::token::erc721::interface::IERC721; Interface of the IERC721 standard as defined in {eip721}. [.contract-index] -.{inner-ierc721} +.{inner-src5} -- 0x33eb2f84c309543403fd69f0d0f363781ef06ef6faeb0131ff16ea3175bd943 -- @@ -148,7 +146,7 @@ use openzeppelin::token::erc721::interface::IERC721Metadata; See {eip721}. [.contract-index] -.{inner-ierc721metadata} +.{inner-src5} -- 0x6069a70848f907fa57668ba1875164eb4dcee693952468581406d131081bbd -- @@ -633,7 +631,7 @@ use openzeppelin::token::erc721::interface::IERC721Receiver; ``` [.contract-index] -.{inner-ierc721receiver} +.{inner-src5} -- 0x3a0dff5f70d80458ad14ae37bb182a728e3c8cdda0402a5daa86620bdf910bc -- From 5d09da84ee05fb4760cad182f00cae195be7daf0 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 28 Sep 2023 13:59:52 -0400 Subject: [PATCH 173/246] fix metadata description --- docs/modules/ROOT/pages/api/erc721.adoc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 1f76a36a2..0511696c1 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -143,6 +143,7 @@ Emitted when `token_id` token is transferred from `from` to `to`. use openzeppelin::token::erc721::interface::IERC721Metadata; ``` +ERC721 extension that allows your smart contract to be interrogated for its name and for details about the assets which the NFTs represent. See {eip721}. [.contract-index] @@ -282,13 +283,15 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi ==== Constructor :src5: xref:introspection.adoc#src5[SRC5] +:ierc721: xref:/api/erc721.adoc#IERC721[IERC721] +:ierc721metadata: xref:/api/erc721.adoc#IERC721Metadata[IERC721Metadata] [.contract-item] [[ERC721-constructor]] ==== `[.contract-item-name]#++constructor++#++(ref self: ContractState, name: felt252, symbol: felt252)++` [.item-kind]#constructor# Initializes the state of the ERC721 contract by setting the token name and symbol. -The constructor also registers the IERC721 and IERC721_METADATA interface ids according to {src5}. +The constructor also registers the {ierc721} and {ierc721metadata} interface ids according to {src5}. ==== External functions From a6731c38346d537753e5809a5d91b347a4b01e4e Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 29 Sep 2023 00:09:34 +0200 Subject: [PATCH 174/246] Update Access Control docs (#719) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: update format and add api * fix: typo * feat: add counterfactual deployment doc * feat: add API entries * feat: add events * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Andrew Fleming * feat: update from reviews * feat: apply review updates * feat: update docs * feat: update docs * feat: update from account docs * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * feat: apply review updates * fix: account casing * feat: add headers * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * feat: add link * feat: move API * feat: add event references * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * refactor: update wording * Update docs/antora.yml Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * feat: apply update reviews * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * refactor: UI * fix: UI * feat: focus on SRC6 * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Andrew Fleming * feat: apply review updates * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * feat: apply review updates * Update docs/modules/ROOT/pages/access.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/access.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/access.adoc Co-authored-by: Martín Triay * feat: apply review updates * feat: apply review updates * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/access.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/access.adoc Co-authored-by: Martín Triay * feat: apply review updates * feat: remove sn_keccak in comments * feat: replace cairo-2 with replace-0.7.0 * feat: remove grayed-out areas * Update docs/modules/ROOT/pages/access.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Andrew Fleming * feat: apply review updates * Update docs/modules/ROOT/pages/access.adoc Co-authored-by: Andrew Fleming --------- Co-authored-by: Andrew Fleming Co-authored-by: Martín Triay --- docs/modules/ROOT/nav.adoc | 3 +- docs/modules/ROOT/pages/access.adoc | 892 ++++++++--------------- docs/modules/ROOT/pages/api/access.adoc | 477 ++++++++++++ docs/modules/ROOT/pages/api/account.adoc | 48 +- 4 files changed, 822 insertions(+), 598 deletions(-) create mode 100644 docs/modules/ROOT/pages/api/access.adoc diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 961aba815..331fae8d5 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -8,7 +8,8 @@ ** xref:/guides/deployment.adoc[Counterfactual deployments] ** xref:/api/account.adoc[API Reference] -// * xref:access.adoc[Access Control] +* xref:access.adoc[Access Control] +** xref:/api/access.adoc[API Reference] // * Tokens // ** xref:erc20.adoc[ERC20] diff --git a/docs/modules/ROOT/pages/access.adoc b/docs/modules/ROOT/pages/access.adoc index bc8996b98..cf9b65989 100644 --- a/docs/modules/ROOT/pages/access.adoc +++ b/docs/modules/ROOT/pages/access.adoc @@ -1,672 +1,414 @@ -:ownable-cairo: link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.6.1/src/openzeppelin/access/ownable/library.cairo[Ownable] - +:ownable-cairo: link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-0.7.0/src/access/ownable/ownable.cairo[Ownable] +:sn_keccak: https://docs.starknet.io/documentation/architecture_and_concepts/Cryptography/hash-functions/#starknet_keccak[sn_keccak] :extensibility-pattern: xref:extensibility.adoc#the_pattern = Access -CAUTION: Expect these modules to evolve. - -Access control--that is, "who is allowed to do this thing"--is incredibly important in the world of smart contracts. +Access control--that is, "who is allowed to do this thing"—is incredibly important in the world of smart contracts. The access control of your contract may govern who can mint tokens, vote on proposals, freeze transfers, and many other things. -It is therefore critical to understand how you implement it, lest someone else https://blog.openzeppelin.com/on-the-parity-wallet-multisig-hack-405a8c12e8f7/[steals your whole system]. - -== Table of Contents - -* <> - ** <> - ** <> - ** <> -* <> - ** <> - ** <> - ** <> - ** <> - ** <> +It is therefore critical to understand how you implement it, lest someone else +https://blog.openzeppelin.com/on-the-parity-wallet-multisig-hack-405a8c12e8f7/[steals your whole system]. -== Ownable +== Ownership and `Ownable` -The most common and basic form of access control is the concept of ownership: there's an account that is the `owner` of a contract and can do administrative tasks on it. +The most common and basic form of access control is the concept of ownership: there's an account that is the `owner` +of a contract and can do administrative tasks on it. This approach is perfectly reasonable for contracts that have a single administrative user. OpenZeppelin Contracts for Cairo provides {ownable-cairo} for implementing ownership in your contracts. -=== Quickstart +=== Usage -Integrating {ownable-cairo} into a contract first requires assigning an owner. -The implementing contract's constructor should set the initial owner by passing the owner's address to Ownable's <> like this: +Integrating this module into a contract first requires assigning an owner. +The implementing contract's constructor should set the initial owner by passing the owner's address to Ownable's +xref:/api/access.adoc#AccessControl-initializer[`initializer`] like this: -[,cairo] +[,javascript] ---- -from openzeppelin.access.ownable.library import Ownable +use openzeppelin::access::ownable::Ownable; -@constructor -func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(owner: felt) { - Ownable.initializer(owner); - return (); -} ----- +#[starknet::contract] +mod MyContract { + use starknet::ContractAddress; + use super::Ownable; -To restrict a function's access to the owner only, add in the `assert_only_owner` method: + #[storage] + struct Storage {} -[,cairo] ----- -from openzeppelin.access.ownable.library import Ownable + #[constructor] + fn constructor(self: @ContractState, owner: ContractAddress) { + let mut unsafe_state = Ownable::unsafe_new_contract_state(); + Ownable::InternalImpl::initializer(ref unsafe_state, owner); + } -func protected_function{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - Ownable.assert_only_owner(); - return (); + (...) } ---- -=== Ownable library API +To restrict a function's access to the owner only, add in the `assert_only_owner` method: -[,cairo] +[,javascript] ---- -func initializer(owner: felt) { -} - -func assert_only_owner() { -} - -func owner() -> (owner: felt) { -} +#[starknet::contract] +mod MyContract { + use openzeppelin::access::ownable::Ownable; -func transfer_ownership(new_owner: felt) { -} + (...) -func renounce_ownership() { -} + #[external(v0)] + fn foo(ref self: ContractState) { + // This function can only be called by the owner + let unsafe_state = Ownable::unsafe_new_contract_state(); + Ownable::InternalImpl::assert_only_owner(@unsafe_state); -func _transfer_ownership(new_owner: felt) { + (...) + } } ---- -==== `initializer` - -Initializes Ownable access control and should be called in the implementing contract's constructor. -Assigns `owner` as the initial owner address of the contract. - -This must be called only once. - -Parameters: - -[,cairo] ----- -owner: felt ----- - -Returns: None. - -==== `assert_only_owner` - -Reverts if called by any account other than the owner. -In case of renounced ownership, any call from the default zero address will also be reverted. - -Parameters: None. - -Returns: None. - -==== `owner` - -Returns the address of the current owner. - -Parameters: None. - -Returns: - -[,cairo] ----- -owner: felt ----- - -==== `transfer_ownership` - -Transfers ownership of the contract to a new account (`new_owner`). -Can only be called by the current owner. - -Emits a <> event. - -Parameters: - -[,cairo] ----- -new_owner: felt ----- - -Returns: None. - -==== `renounce_ownership` - -Leaves the contract without owner. -It will not be possible to call functions with `assert_only_owner` anymore. -Can only be called by the current owner. - -Emits a <> event. - -Parameters: None. - -Returns: None. - -[#transfer-ownership-internal] -==== `_transfer_ownership` +=== Interface -Transfers ownership of the contract to a new account (`new_owner`). {extensibility-pattern}[`internal`] function without access restriction. +This is the full interface of the `Ownable` implementation: -Emits a <> event. - -Parameters: - -[,cairo] +[,javascript] ---- -new_owner: felt ----- - -Returns: None. +trait IOwnable { + /// Returns the current owner. + fn owner() -> ContractAddress; -=== Ownable events + /// Transfers the ownership from the current owner to a new owner. + fn transfer_ownership(new_owner: ContractAddress); -[,cairo] ----- -func OwnershipTransferred(previousOwner: felt, newOwner: felt) { + /// Renounces the ownership of the contract. + fn renounce_ownership(); } ---- -==== OwnershipTransferred - -Emitted when ownership of a contract is transferred from `previousOwner` to `newOwner`. +Ownable also lets you: -Parameters: +- `transfer_ownership` from the owner account to a new one, and +- `renounce_ownership` for the owner to relinquish this administrative privilege, a common pattern +after an initial stage with centralized administration is over. -[,cairo] ----- -previousOwner: felt -newOwner: felt ----- +WARNING: Removing the owner altogether will mean that administrative tasks that are protected by `assert_only_owner` +will no longer be callable! -== AccessControl +== Role-Based `AccessControl` -While the simplicity of ownership can be useful for simple systems or quick prototyping, different levels of authorization are often needed. -You may want for an account to have permission to ban users from a system, but not create new tokens. -https://en.wikipedia.org/wiki/Role-based_access_control[Role-Based Access Control (RBAC)] offers flexibility in this regard. +While the simplicity of ownership can be useful for simple systems or quick prototyping, different levels of +authorization are often needed. You may want for an account to have permission to ban users from a system, but not +create new tokens. https://en.wikipedia.org/wiki/Role-based_access_control[Role-Based Access Control (RBAC)] offers +flexibility in this regard. In essence, we will be defining multiple roles, each allowed to perform different sets of actions. -An account may have, for example, 'moderator', 'minter' or 'admin' roles, which you will then check for instead of simply using <>. -This check can be enforced through <>. +An account may have, for example, 'moderator', 'minter' or 'admin' roles, which you will then check for +instead of simply using xref:/api/access.adoc#Ownable-assert_only_owner[`assert_only_owner`]. This check can be enforced through xref:/api/access.adoc#AccessControl-assert_only_role[`assert_only_role`]. Separately, you will be able to define rules for how accounts can be granted a role, have it revoked, and more. -Most software uses access control systems that are role-based: some users are regular users, some may be supervisors or managers, and a few will often have administrative privileges. +Most software uses access control systems that are role-based: some users are regular users, some may be supervisors +or managers, and a few will often have administrative privileges. === Usage -For each role that you want to define, you will create a new _role identifier_ that is used to grant, revoke, and check if an account has that role (see <> for information on creating identifiers). - -Here's a simple example of implementing `AccessControl` on a portion of an link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.6.1/src/openzeppelin/token/erc20/presets/ERC20.cairo[ERC20 token contract] which defines and sets the 'minter' role: - -[,cairo] ----- -from openzeppelin.token.erc20.library import ERC20 - -from openzeppelin.access.accesscontrol.library import AccessControl - - -const MINTER_ROLE = 0x4f96f87f6963bb246f2c30526628466840c642dc5c50d5a67777c6cc0e44ab5 - -@constructor -func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - name: felt, symbol: felt, decimals: felt, minter: felt -) { - ERC20.initializer(name, symbol, decimals); - AccessControl.initializer(); - AccessControl._grant_role(MINTER_ROLE, minter); - return (); -} - -@external -func mint{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - to: felt, amount: Uint256 -) { - AccessControl.assert_only_role(MINTER_ROLE); - ERC20._mint(to, amount); - return (); +For each role that you want to define, you will create a new _role identifier_ that is used to grant, revoke, and +check if an account has that role. See xref:#creating_role_identifiers[Creating role identifiers] for information +on creating identifiers. + +Here's a simple example of implementing `AccessControl` on a portion of an ERC20 token contract which defines +and sets a 'minter' role: + +[,javascript] +---- +const MINTER_ROLE: felt252 = selector!('MINTER_ROLE'); + +#[starknet::contract] +mod MyContract { + use openzeppelin::access::accesscontrol::AccessControl::InternalImpl::assert_only_role; + use openzeppelin::access::accesscontrol::AccessControl; + use openzeppelin::token::erc20::ERC20; + use starknet::ContractAddress; + use super::MINTER_ROLE; + + #[storage] + struct Storage {} + + #[constructor] + fn constructor( + ref self: ContractState, + name: felt252, + symbol: felt252, + initial_supply: u256, + recipient: ContractAddress, + minter: ContractAddress + ) { + // ERC20 related initialization + let mut erc20_state = ERC20::unsafe_new_contract_state(); + ERC20::InternalImpl::initializer(ref erc20_state, name, symbol); + ERC20::InternalImpl::_mint(ref erc20_state, recipient, initial_supply); + + // AccessControl related initialization + let mut access_state = AccessControl::unsafe_new_contract_state(); + AccessControl::InternalImpl::initializer(ref access_state); + AccessControl::InternalImpl::_grant_role( + ref access_state, + MINTER_ROLE, + minter + ); + } + + // This function can only be called by a minter + #[external(v0)] + fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) { + let access_state = AccessControl::unsafe_new_contract_state(); + assert_only_role(@access_state, MINTER_ROLE); + + let mut erc20_state = AccessControl::unsafe_new_contract_state(); + ERC20::InternalImpl::_mint(ref erc20_state, recipient, amount); + } } ---- -CAUTION: Make sure you fully understand how <> works before using it on your system, or copy-pasting the examples from this guide. - -While clear and explicit, this isn't anything we wouldn't have been able to achieve with <>. -Indeed, where `AccessControl` shines is in scenarios where granular permissions are required, which can be implemented by defining _multiple_ roles. - -Let's augment our ERC20 token example by also defining a 'burner' role, which lets accounts destroy tokens, and by using `assert_only_role`: - -[,cairo] ----- -from openzeppelin.token.erc20.library import ERC20 - -from openzeppelin.access.accesscontrol.library import AccessControl - - -const MINTER_ROLE = 0x4f96f87f6963bb246f2c30526628466840c642dc5c50d5a67777c6cc0e44ab5 -const BURNER_ROLE = 0x7823a2d975ffa03bed39c38809ec681dc0ae931ebe0048c321d4a8440aed509 - -@constructor -func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - name: felt, symbol: felt, decimals: felt, minter: felt, burner: felt -) { - ERC20.initializer(name, symbol, decimals); - AccessControl.initializer(); - AccessControl._grant_role(MINTER_ROLE, minter); - AccessControl._grant_role(BURNER_ROLE, burner); - return (); -} - -@external -func mint{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - to: felt, amount: Uint256 -) { - AccessControl.assert_only_role(MINTER_ROLE); - ERC20._mint(to, amount); - return (); -} - -@external -func burn{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - from_: felt, amount: Uint256 -) { - AccessControl.assert_only_role(BURNER_ROLE); - ERC20._burn(from_, amount); - return (); +CAUTION: Make sure you fully understand how xref:api/access.adoc#AccessControl[AccessControl] works before +using it on your system, or copy-pasting the examples from this guide. + +While clear and explicit, this isn't anything we wouldn't have been able to achieve with +xref:api/access.adoc#Ownable[Ownable]. Where `AccessControl` shines the most is in scenarios where granular +permissions are required, which can be implemented by defining _multiple_ roles. + +Let's augment our ERC20 token example by also defining a 'burner' role, which lets accounts destroy tokens: + +[,javascript] +---- +const MINTER_ROLE: felt252 = selector!('MINTER_ROLE'); +const BURNER_ROLE: felt252 = selector!('BURNER_ROLE'); + +#[starknet::contract] +mod MyContract { + use openzeppelin::access::accesscontrol::AccessControl::InternalImpl::assert_only_role; + use openzeppelin::access::accesscontrol::AccessControl; + use openzeppelin::token::erc20::ERC20; + use starknet::ContractAddress; + use super::{MINTER_ROLE, BURNER_ROLE}; + + #[storage] + struct Storage {} + + #[constructor] + fn constructor( + ref self: ContractState, + name: felt252, + symbol: felt252, + initial_supply: u256, + recipient: ContractAddress, + minter: ContractAddress, + burner: ContractAddress + ) { + // ERC20 related initialization + let mut erc20_state = ERC20::unsafe_new_contract_state(); + ERC20::InternalImpl::initializer(ref erc20_state, name, symbol); + ERC20::InternalImpl::_mint(ref erc20_state, recipient, initial_supply); + + // AccessControl related initialization + let mut access_state = AccessControl::unsafe_new_contract_state(); + AccessControl::InternalImpl::initializer(ref access_state); + AccessControl::InternalImpl::_grant_role( + ref access_state, + MINTER_ROLE, + minter + ); + AccessControl::InternalImpl::_grant_role( + ref access_state, + BURNER_ROLE, + burner + ); + } + + + // This function can only be called by a minter + #[external(v0)] + fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) { + let access_state = AccessControl::unsafe_new_contract_state(); + assert_only_role(@access_state, MINTER_ROLE); + + let mut erc20_state = AccessControl::unsafe_new_contract_state(); + ERC20::InternalImpl::_mint(ref erc20_state, recipient, amount); + } + + // This function can only be called by a burner + #[external(v0)] + fn burn(ref self: ContractState, account: ContractAddress, amount: u256) { + let access_state = AccessControl::unsafe_new_contract_state(); + assert_only_role(@access_state, BURNER_ROLE); + + let mut erc20_state = AccessControl::unsafe_new_contract_state(); + ERC20::InternalImpl::_burn(ref erc20_state, account, amount); + } } ---- So clean! -By splitting concerns this way, more granular levels of permission may be implemented than were possible with the simpler ownership approach to access control. -Limiting what each component of a system is able to do is known as the https://en.wikipedia.org/wiki/Principle_of_least_privilege[principle of least privilege], and is a good security practice. -Note that each account may still have more than one role, if so desired. +By splitting concerns this way, more granular levels of permission may be implemented than were possible with the +simpler ownership approach to access control. Limiting what each component of a system is able to do is known +as the https://en.wikipedia.org/wiki/Principle_of_least_privilege[principle of least privilege], and is a good +security practice. Note that each account may still have more than one role, if so desired. === Granting and revoking roles -The ERC20 token example above uses `_grant_role`, an {extensibility-pattern}[`internal`] function that is useful when programmatically assigning roles (such as during construction). -But what if we later want to grant the 'minter' role to additional accounts? +The ERC20 token example above uses xref:api/access.adoc#AccessControl-_grant_role[`_grant_role`], +an `internal` function that is useful when programmatically assigning +roles (such as during construction). But what if we later want to grant the 'minter' role to additional accounts? -By default, *accounts with a role cannot grant it or revoke it from other accounts*: all having a role does is making the `assert_only_role` check pass. -To grant and revoke roles dynamically, you will need help from the role's _admin_. +By default, *accounts with a role cannot grant it or revoke it from other accounts*: all having a role does is making +the xref:api/access.adoc#AccessControl-assert_only_role[`assert_only_role`] check pass. To grant and revoke roles dynamically, you will need help from the role's _admin_. -Every role has an associated admin role, which grants permission to call the `grant_role` and `revoke_role` functions. +Every role has an associated admin role, which grants permission to call the +xref:api/access.adoc#AccessControl-grant_role[`grant_role`] and +xref:api/access.adoc#AccessControl-revoke_role[`revoke_role`] functions. A role can be granted or revoked by using these if the calling account has the corresponding admin role. Multiple roles may have the same admin role to make management easier. -A role's admin can even be the same role itself, which would cause accounts with that role to be able to also grant and revoke it. +A role's admin can even be the same role itself, which would cause accounts with that role to be able +to also grant and revoke it. -This mechanism can be used to create complex permissioning structures resembling organizational charts, but it also provides an easy way to manage simpler applications. -`AccessControl` includes a special role with the role identifier of `0`, called `DEFAULT_ADMIN_ROLE`, which acts as the *default admin role for all roles*. -An account with this role will be able to manage any other role, unless `_set_role_admin` is used to select a new admin role. +This mechanism can be used to create complex permissioning structures resembling organizational charts, but it also +provides an easy way to manage simpler applications. `AccessControl` includes a special role with the role identifier +of `0`, called `DEFAULT_ADMIN_ROLE`, which acts as the *default admin role for all roles*. +An account with this role will be able to manage any other role, unless +xref:api/access.adoc#AccessControl-_set_role_admin[`_set_role_admin`] is used to select a new admin role. Let's take a look at the ERC20 token example, this time taking advantage of the default admin role: -[,cairo] ----- -from openzeppelin.token.erc20.library import ERC20 - -from openzeppelin.access.accesscontrol.library import AccessControl - -from openzeppelin.utils.constants import DEFAULT_ADMIN_ROLE - - -const MINTER_ROLE = 0x4f96f87f6963bb246f2c30526628466840c642dc5c50d5a67777c6cc0e44ab5 -const BURNER_ROLE = 0x7823a2d975ffa03bed39c38809ec681dc0ae931ebe0048c321d4a8440aed509 - -@constructor -func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - name: felt, symbol: felt, decimals: felt, admin: felt, -) { - ERC20.initializer(name, symbol, decimals); - AccessControl.initializer(); - - AccessControl._grant_role(DEFAULT_ADMIN_ROLE, admin); - return (); -} - -@external -func mint{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - to: felt, amount: Uint256 -) { - AccessControl.assert_only_role(MINTER_ROLE); - ERC20._mint(to, amount); - return (); -} - -@external -func burn{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - from_: felt, amount: Uint256 -) { - AccessControl.assert_only_role(BURNER_ROLE); - ERC20._burn(from_, amount); - return (); +[,javascript] +---- +const MINTER_ROLE: felt252 = selector!('MINTER_ROLE'); +const BURNER_ROLE: felt252 = selector!('BURNER_ROLE'); + +#[starknet::contract] +mod MyContract { + use openzeppelin::access::accesscontrol::AccessControl::InternalImpl::assert_only_role; + use openzeppelin::access::accesscontrol::AccessControl; + use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; + use openzeppelin::token::erc20::ERC20; + use starknet::ContractAddress; + use super::{MINTER_ROLE, BURNER_ROLE}; + + #[storage] + struct Storage {} + + #[constructor] + fn constructor( + ref self: ContractState, + name: felt252, + symbol: felt252, + initial_supply: u256, + recipient: ContractAddress, + admin: ContractAddress + ) { + // ERC20 related initialization + let mut erc20_state = ERC20::unsafe_new_contract_state(); + ERC20::InternalImpl::initializer(ref erc20_state, name, symbol); + ERC20::InternalImpl::_mint(ref erc20_state, recipient, initial_supply); + + // AccessControl related initialization + let mut access_state = AccessControl::unsafe_new_contract_state(); + AccessControl::InternalImpl::initializer(ref access_state); + AccessControl::InternalImpl::_grant_role( + ref access_state, + DEFAULT_ADMIN_ROLE, + admin + ); + } + + // This function can only be called by a minter + #[external(v0)] + fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) { + let access_state = AccessControl::unsafe_new_contract_state(); + assert_only_role(@access_state, MINTER_ROLE); + + let mut erc20_state = AccessControl::unsafe_new_contract_state(); + ERC20::InternalImpl::_mint(ref erc20_state, recipient, amount); + } + + // This function can only be called by a burner + #[external(v0)] + fn burn(ref self: ContractState, account: ContractAddress, amount: u256) { + let access_state = AccessControl::unsafe_new_contract_state(); + assert_only_role(@access_state, BURNER_ROLE); + + let mut erc20_state = AccessControl::unsafe_new_contract_state(); + ERC20::InternalImpl::_burn(ref erc20_state, account, amount); + } + + // These function can only be called by the roles' admin + #[external(v0)] + fn grant_role(ref self: ContractState, role: felt252, account: ContractAddress) { + let mut unsafe_state = AccessControl::unsafe_new_contract_state(); + AccessControl::AccessControlImpl::grant_role(ref unsafe_state, role, account); + } + #[external(v0)] + fn revoke_role(ref self: ContractState, role: felt252, account: ContractAddress) { + let mut unsafe_state = AccessControl::unsafe_new_contract_state(); + AccessControl::AccessControlImpl::revoke_role(ref unsafe_state, role, account); + } } ---- Note that, unlike the previous examples, no accounts are granted the 'minter' or 'burner' roles. -However, because those roles' admin role is the default admin role, and that role was granted to the 'admin', that same account can call `grant_role` to give minting or burning permission, and `revoke_role` to remove it. - -Dynamic role allocation is often a desirable property, for example in systems where trust in a participant may vary over time. -It can also be used to support use cases such as https://en.wikipedia.org/wiki/Know_your_customer[KYC], where the list of role-bearers may not be known up-front, or may be prohibitively expensive to include in a single transaction. - -The following example uses the link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.6.1/tests/mocks/AccessControl.cairo[AccessControl mock contract] which exposes the role management functions. -To grant and revoke roles in Python, for example: +However, because those roles' admin role is the default admin role, and that role was granted to the 'admin', that +same account can call `grant_role` to give minting or burning permission, and `revoke_role` to remove it. -[,python] ----- -MINTER_ROLE = 0x4f96f87f6963bb246f2c30526628466840c642dc5c50d5a67777c6cc0e44ab5 -BURNER_ROLE = 0x7823a2d975ffa03bed39c38809ec681dc0ae931ebe0048c321d4a8440aed509 - -# grants MINTER_ROLE and BURNER_ROLE to account1 and account2 respectively -await signer.send_transactions( - admin, [ - (accesscontrol.contract_address, 'grantRole', [MINTER_ROLE, account1.contract_address]), - (accesscontrol.contract_address, 'grantRole', [BURNER_ROLE, account2.contract_address]) - ] -) - -# revokes MINTER_ROLE from account1 -await signer.send_transaction( - admin, - accesscontrol.contract_address, - 'revokeRole', - [MINTER_ROLE, account1.contract_address] -) ----- +Dynamic role allocation is often a desirable property, for example in systems where trust in a participant may vary +over time. It can also be used to support use cases such as https://en.wikipedia.org/wiki/Know_your_customer[KYC], +where the list of role-bearers may not be known up-front, or may be prohibitively expensive to include in a single transaction. === Creating role identifiers -In the Solidity implementation of AccessControl, contracts generally refer to the https://docs.soliditylang.org/en/latest/units-and-global-variables.html?highlight=keccak256#mathematical-and-cryptographic-functions[keccak256 hash] of a role as the role identifier. +In the Solidity implementation of AccessControl, contracts generally refer to the +https://docs.soliditylang.org/en/latest/units-and-global-variables.html?highlight=keccak256#mathematical-and-cryptographic-functions[keccak256 hash] +of a role as the role identifier. + For example: -[,solidity] +[,javascript] ---- bytes32 public constant SOME_ROLE = keccak256("SOME_ROLE") ---- These identifiers take up 32 bytes (256 bits). -Cairo field elements store a maximum of 252 bits. -Even further, a declared _constant_ field element in a StarkNet contract stores even less (see https://github.com/starkware-libs/cairo-lang/blob/release-v0.6.1/src/starkware/cairo/lang/cairo_constants.py#L1[cairo_constants]). +Cairo field elements (`felt252`) store a maximum of 252 bits. With this discrepancy, this library maintains an agnostic stance on how contracts should create identifiers. Some ideas to consider: -* Use the first or last 251 bits of keccak256 hash digests. -* Use Cairo's https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/cairo/common/hash.cairo[hash2]. - -=== AccessControl library API - -[,cairo] ----- -func initializer() { -} - -func assert_only_role(role: felt) { -} - -func has_role(role: felt, user: felt) -> (has_role: felt) { -} - -func get_role_admin(role: felt) -> (admin: felt) { -} - -func grant_role(role: felt, user: felt) { -} - -func revoke_role(role: felt, user: felt) { -} - -func renounce_role(role: felt, user: felt) { -} - -func _grant_role(role: felt, user: felt) { -} - -func _revoke_role(role: felt, user: felt) { -} - -func _set_role_admin(role: felt, admin_role: felt) { -} ----- - -[#initializer-accesscontrol] -==== `initializer` - -Initializes AccessControl and should be called in the implementing contract's constructor. - -This must only be called once. - -Parameters: None. - -Returns: None. - -==== `assert_only_role` - -Checks that an account has a specific role. -Reverts with a message including the required role. - -Parameters: - -[,cairo] ----- -role: felt ----- - -Returns: None. - -==== has_role - -Returns `TRUE` if `user` has been granted `role`, `FALSE` otherwise. - -Parameters: - -[,cairo] ----- -role: felt -user: felt ----- - -Returns: - -[,cairo] ----- -has_role: felt ----- - -==== `get_role_admin` - -Returns the admin role that controls `role`. -See <> and <>. - -To change a role's admin, use <>. - -Parameters: - -[,cairo] ----- -role: felt ----- - -Returns: - -[,cairo] ----- -admin: felt ----- - -==== `grant_role` - -Grants `role` to `user`. - -If `user` had not been already granted `role`, emits a <> event. - -Requirements: - -* The caller must have ``role``'s admin role. - -Parameters: - -[,cairo] ----- -role: felt -user: felt ----- - -Returns: None. - -==== `revoke_role` - -Revokes `role` from `user`. - -If `user` had been granted `role`, emits a <> event. - -Requirements: - -* The caller must have ``role``'s admin role. - -Parameters: - -[,cairo] ----- -role: felt -user: felt ----- - -Returns: None. - -==== `renounce_role` - -Revokes `role` from the calling `user`. - -Roles are often managed via <> and <>: this function's purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). - -If the calling `user` had been revoked `role`, emits a <> event. - -Requirements: - -* The caller must be `user`. - -Parameters: - -[,cairo] ----- -role: felt -user: felt ----- - -Returns: None. - -[#grantrole-internal] -==== `_grant_role` - -Grants `role` to `user`. - -{extensibility-pattern}[`internal`] function without access restriction. - -Emits a <> event. - -Parameters: - -[,cairo] ----- -role: felt -user: felt ----- - -Returns: None. - -[#revokerole-internal] -==== `_revoke_role` - -Revokes `role` from `user`. - -{extensibility-pattern}[`internal`] function without access restriction. - -Emits a <> event. - -Parameters: - -[,cairo] ----- -role: felt -user: felt ----- - -Returns: None. - -[#setroleadmin] -==== `_set_role_admin` +* Use {sn_keccak} instead. +* Use Cairo friendly hashing algorithms like Poseidon, which are implemented in the +https://github.com/starkware-libs/cairo/blob/main/corelib/src/poseidon.cairo[Cairo corelib]. -{extensibility-pattern}[`internal`] function that sets `admin_role` as ``role``'s admin role. +TIP: The `selector!` macro can be used to compute {sn_keccak} in Cairo. -Emits a <> event. +=== Interface -Parameters: +This is the full interface of the `AccessControl` implementation: -[,cairo] +[,javascript] ---- -role: felt -admin_role: felt ----- - -Returns: None. +trait IAccessControl { + /// Returns whether the account has the role or not. + fn has_role(role: felt252, account: ContractAddress) -> bool; -=== AccessControl events + /// Returns the adming role that controls `role`. + fn get_role_admin(role: felt252) -> felt252; -[,cairo] ----- -func RoleGranted(role: felt, account: felt, sender: felt) { -} + /// Grants `role` to `account`. + fn grant_role(role: felt252, account: ContractAddress); -func RoleRevoked(role: felt, account: felt, sender: felt) { -} + /// Revokes `role` from `account`. + fn revoke_role(role: felt252, account: ContractAddress); -func RoleAdminChanged(role: felt, previousAdminRole: felt, newAdminRole: felt) { + /// Revokes `role` from self. + fn renounce_role(role: felt252, account: ContractAddress); } ---- -==== `RoleGranted` - -Emitted when `account` is granted `role`. - -`sender` is the account that originated the contract call, an admin role bearer. - -Parameters: - -[,cairo] ----- -role: felt -account: felt -sender: felt ----- - -==== `RoleRevoked` - -Emitted when account is revoked role. - -`sender` is the account that originated the contract call: - -* If using <>, it is the admin role bearer. -* If using <>, it is the role bearer (i.e. -`account`). - -[,cairo] ----- -role: felt -account: felt -sender: felt ----- - -==== `RoleAdminChanged` - -Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`. - -`DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite `RoleAdminChanged` not being emitted signaling this. - -[,cairo] ----- -role: felt -previousAdminRole: felt -newAdminRole: felt ----- +`AccessControl` also lets you `renounce_role` from the calling account. +The method expects an account as input as an extra security measure, to ensure you are +not renouncing a role from an unintended account. diff --git a/docs/modules/ROOT/pages/api/access.adoc b/docs/modules/ROOT/pages/api/access.adoc new file mode 100644 index 000000000..1a00ffa31 --- /dev/null +++ b/docs/modules/ROOT/pages/api/access.adoc @@ -0,0 +1,477 @@ +:github-icon: pass:[] +:AccessControl: xref:AccessControl[AccessControl] +:Ownable: xref:Ownable[Ownable] +:src5: https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md[SRC5] +:inner-src5: xref:api/introspection.adoc#ISRC5[SRC5 ID] +:_set_role_admin: xref:#AccessControl-_set_role_admin[_set_role_admin] + += Access Control + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/access + +This directory provides ways to restrict who can access the functions of a contract or when they can do it. + +- {Ownable} is a simple mechanism with a single "owner" role that can be assigned to a single account. +This mechanism can be useful in simple scenarios, but fine-grained access needs are likely to outgrow it. +- {AccessControl} provides a general role-based access control mechanism. Multiple hierarchical roles can be created and +assigned each to multiple accounts. + +== Authorization + +[.contract] +[[Ownable]] +=== `++Ownable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-0.7.0/src/access/ownable/ownable.cairo[{github-icon},role=heading-link] + +```javascript +use openzeppelin::access::ownable::Ownable; +``` + +`Ownable` provides a basic access control mechanism where an account + (an owner) can be granted exclusive access to specific functions. + +This module includes the `assert_only_owner` internal to restrict a function to be used only by the owner. + +[.contract-index] +.External Functions +-- +.OwnableImpl + +* xref:Ownable-owner[`++owner(self)++`] +* xref:Ownable-transfer_ownership[`++transfer_ownership(self, new_owner)++`] +* xref:Ownable-renounce_ownership[`++renounce_ownership(self)++`] +-- + +[.contract-index] +.Internal Functions +-- +.InternalImpl + +* xref:Ownable-initializer[`++initializer(self, owner)++`] +* xref:Ownable-assert_only_owner[`++assert_only_owner(self)++`] +* xref:Ownable-_transfer_ownership[`++_transfer_ownership(self, new_owner)++`] +-- + +[.contract-index] +.Events +-- +* xref:Ownable-OwnershipTransferred[`++OwnershipTransferred(previous_owner, new_owner)++`] +-- + +[#Ownable-External-Functions] +==== External Functions + +[.contract-item] +[[Ownable-owner]] +==== `[.contract-item-name]#++owner++#++(self: @ContractState) → ContractAddress++` [.item-kind]#external# + +Returns the address of the current owner. + +[.contract-item] +[[Ownable-transfer_ownership]] +==== `[.contract-item-name]#++transfer_ownership++#++(ref self: ContractState, new_owner: ContractAddress)++` [.item-kind]#external# + +Transfers ownership of the contract to a new account (`new_owner`). +Can only be called by the current owner. + +Emits an xref:Ownable-OwnershipTransferred[OwnershipTransferred] event. + +[.contract-item] +[[Ownable-renounce_ownership]] +==== `[.contract-item-name]#++renounce_ownership++#++(ref self: ContractState)++` [.item-kind]#external# + +Leaves the contract without owner. It will not be possible to call +`assert_only_owner` functions anymore. Can only be called by the current owner. + +NOTE: Renouncing ownership will leave the contract without an owner, +thereby removing any functionality that is only available to the owner. + +[#Ownable-Internal-Functions] +==== Internal Functions + +[.contract-item] +[[Ownable-initializer]] +==== `[.contract-item-name]#++initializer++#++(ref self: ContractState, owner: ContractAddress)++` [.item-kind]#internal# + +Initializes the contract and sets `owner` as the initial owner. + +Emits an xref:Ownable-OwnershipTransferred[OwnershipTransferred] event. + +[.contract-item] +[[Ownable-assert_only_owner]] +==== `[.contract-item-name]#++assert_only_owner++#++(self: @ContractState)++` [.item-kind]#internal# + +Panics if called by any account other than the owner. + +[.contract-item] +[[Ownable-_transfer_ownership]] +==== `[.contract-item-name]#++_transfer_ownership++#++(ref self: ContractState, new_owner: ContractAddress)++` [.item-kind]#internal# + +Transfers ownership of the contract to a new account (`new_owner`). +Internal function without access restriction. + +Emits an xref:Ownable-OwnershipTransferred[OwnershipTransferred] event. + +[#Ownable-Events] +==== Events + +[.contract-item] +[[Ownable-OwnershipTransferred]] +==== `[.contract-item-name]#++OwnershipTransferred++#++(previous_owner: ContractAddress, new_owner: ContractAddress)++` [.item-kind]#event# + +Emitted when the ownership is transferred. + +[.contract] +[[IAccessControl]] +=== `++IAccessControl++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/05429e4fd34a250ce7a01450190c53275e5c1c0b/src/access/accesscontrol/interface.cairo#L10[{github-icon},role=heading-link] + +:grant_role: xref:#IAccessControl-grant_role[grant_role] +:revoke_role: xref:#IAccessControl-revoke_role[revoke_role] +:RoleGranted: xref:#IAccessControl-RoleGranted[RoleGranted] +:RoleRevoked: xref:#IAccessControl-RoleRevoked[RoleRevoked] +:RoleAdminChanged: xref:#IAccessControl-RoleAdminChanged[RoleAdminChanged] + +```javascript +use openzeppelin::access::accesscontrol::interface::IAccessControl; +``` + +External interface of AccessControl. + +[.contract-index] +.{inner-src5} +-- +0x23700be02858dbe2ac4dc9c9f66d0b6b0ed81ec7f970ca6844500a56ff61751 +-- + +[.contract-index] +.Functions +-- +* xref:IAccessControl-has_role[`++has_role(role, account)++`] +* xref:IAccessControl-get_role_admin[`++get_role_admin(role)++`] +* xref:IAccessControl-grant_role[`++grant_role(role, account)++`] +* xref:IAccessControl-revoke_role[`++revoke_role(role, account)++`] +* xref:IAccessControl-renounce_role[`++renounce_role(role, account)++`] +-- + +[.contract-index] +.Events +-- +* xref:IAccessControl-RoleAdminChanged[`++RoleAdminChanged(role, previous_admin_role, new_admin_role)++`] +* xref:IAccessControl-RoleGranted[`++RoleGranted(role, account, sender)++`] +* xref:IAccessControl-RoleRevoked[`++RoleRevoked(role, account, sender)++`] + +-- + +[#IAccessControl-Functions] +==== Functions + +[.contract-item] +[[IAccessControl-has_role]] +==== `[.contract-item-name]#++has_role++#++(role: felt252, account: ContractAddress) → bool++` [.item-kind]#external# + +Returns `true` if `account` has been granted `role`. + +[.contract-item] +[[IAccessControl-get_role_admin]] +==== `[.contract-item-name]#++get_role_admin++#++(role: felt252) → felt252++` [.item-kind]#external# + +Returns the admin role that controls `role`. See {grant_role} and +{revoke_role}. + +To change a role's admin, use {_set_role_admin}. + +[.contract-item] +[[IAccessControl-grant_role]] +==== `[.contract-item-name]#++grant_role++#++(role: felt252, account: ContractAddress)++` [.item-kind]#external# + +Grants `role` to `account`. + +If `account` had not been already granted `role`, emits a {RoleGranted} +event. + +Requirements: + +- the caller must have ``role``'s admin role. + +[.contract-item] +[[IAccessControl-revoke_role]] +==== `[.contract-item-name]#++revoke_role++#++(role: felt252, account: ContractAddress)++` [.item-kind]#external# + +Revokes `role` from `account`. + +If `account` had been granted `role`, emits a {RoleRevoked} event. + +Requirements: + +- the caller must have ``role``'s admin role. + +[.contract-item] +[[IAccessControl-renounce_role]] +==== `[.contract-item-name]#++renounce_role++#++(role: felt252, account: ContractAddress)++` [.item-kind]#external# + +Revokes `role` from the calling account. + +Roles are often managed via {grant_role} and {revoke_role}. This function's +purpose is to provide a mechanism for accounts to lose their privileges +if they are compromised (such as when a trusted device is misplaced). + +If the calling account had been granted `role`, emits a {RoleRevoked} +event. + +Requirements: + +- the caller must be `account`. + +[#IAccessControl-Events] +==== Events + +[.contract-item] +[[IAccessControl-RoleAdminChanged]] +==== `[.contract-item-name]#++RoleAdminChanged++#++(role: felt252, previous_admin_role: ContractAddress, new_admin_role: ContractAddress)++` [.item-kind]#event# + +Emitted when `new_admin_role` is set as ``role``'s admin role, replacing `previous_admin_role` + +`DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite +{RoleAdminChanged} not being emitted signaling this. + +[.contract-item] +[[IAccessControl-RoleGranted]] +==== `[.contract-item-name]#++RoleGranted++#++(role: felt252, account: ContractAddress, sender: ContractAddress)++` [.item-kind]#event# + +Emitted when `account` is granted `role`. + +`sender` is the account that originated the contract call, an admin role +bearer. + +[.contract-item] +[[IAccessControl-RoleRevoked]] +==== `[.contract-item-name]#++RoleRevoked++#++(role: felt252, account: ContractAddress, sender: ContractAddress)++` [.item-kind]#event# + +Emitted when `account` is revoked `role`. + +`sender` is the account that originated the contract call: + +- if using `revoke_role`, it is the admin role bearer. +- if using `renounce_role`, it is the role bearer (i.e. `account`). + +[.contract] +[[AccessControl]] +=== `++AccessControl++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-0.7.0/src/access/accesscontrol/accesscontrol.cairo[{github-icon},role=heading-link] + +:assert_only_role: xref:#AccessControl-assert_only_role +:grant_role: xref:#AccessControl-grant_role[grant_role] +:revoke_role: xref:#AccessControl-revoke_role[revoke_role] + +```javascript +use openzeppelin::access::accesscontrol::AccessControl; +``` + +Contract module that allows children to implement role-based access control mechanisms. +Roles are referred to by their `felt252` identifier: + +```javascript +const MY_ROLE: felt252 = selector!('MY_ROLE'); +``` + +Roles can be used to represent a set of permissions. To restrict access to a +function call, use {assert_only_role}[`assert_only_role`]: + +```javascript +use openzeppelin::access::accesscontrol::AccessControl::InternalImpl::assert_only_role; +use openzeppelin::access::accesscontrol::AccessControl; +use openzeppelin::token::erc20::ERC20; + +#[external(v0)] +fn foo(ref self: ContractState, account: ContractAddress, amount: u256) { + let access_state = AccessControl::unsafe_new_contract_state(); + assert_only_role(@access_state, BURNER_ROLE); + + let mut erc20_state = ERC20::unsafe_new_contract_state(); + ERC20::InternalImpl::_burn(ref erc20_state, account, amount); +} +``` + +Roles can be granted and revoked dynamically via the {grant_role} and +{revoke_role} functions. Each role has an associated admin role, and only +accounts that have a role's admin role can call {grant_role} and {revoke_role}. + +By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means +that only accounts with this role will be able to grant or revoke other +roles. More complex role relationships can be created by using +{_set_role_admin}. + +WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to +grant and revoke this role. Extra precautions should be taken to secure +accounts that have been granted it. + +[.contract-index] +.External Functions +-- +.AccessControlImpl + +* xref:#AccessControl-has_role[`++has_role(self, role, account)++`] +* xref:#AccessControl-get_role_admin[`++get_role_admin(self, role)++`] +* xref:#AccessControl-grant_role[`++grant_role(self, role, account)++`] +* xref:#AccessControl-revoke_role[`++revoke_role(self, role, account)++`] +* xref:#AccessControl-renounce_role[`++renounce_role(self, role, account)++`] + +.SRC5Impl +* xref:#AccessControl-supports_interface[`++supports_interface(self, interface_id: felt252)++`] +-- + +[.contract-index] +.Internal Functions +-- +.InternalImpl + +* xref:#AccessControl-initializer[`++initializer(self)++`] +* xref:#AccessControl-_set_role_admin[`++_set_role_admin(self, role, admin_role)++`] +* xref:#AccessControl-_grant_role[`++_grant_role(self, role, account)++`] +* xref:#AccessControl-_revoke_role[`++_revoke_role(self, role, account)++`] +* xref:#AccessControl-assert_only_role[`++assert_only_role(self, role)++`] +-- + +[.contract-index] +.Events +-- +.IAccessControl +* xref:#AccessControl-RoleAdminChanged[`++RoleAdminChanged(role, previous_admin_role, new_admin_role)++`] +* xref:#AccessControl-RoleGranted[`++RoleGranted(role, account, sender)++`] +* xref:#AccessControl-RoleRevoked[`++RoleRevoked(role, account, sender)++`] +-- + +[#AccessControl-External-Functions] +==== External Functions + +[.contract-item] +[[AccessControl-has_role]] +==== `[.contract-item-name]#++has_role++#++(self: @ContractState, role: felt252, account: ContractAddress) → bool++` [.item-kind]#external# + +Returns `true` if `account` has been granted `role`. + +[.contract-item] +[[AccessControl-get_role_admin]] +==== `[.contract-item-name]#++get_role_admin++#++(self: @ContractState, role: felt252) → felt252++` [.item-kind]#external# + +Returns the admin role that controls `role`. See {grant_role} and +{revoke_role}. + +To change a role's admin, use {_set_role_admin}. + +[.contract-item] +[[AccessControl-grant_role]] +==== `[.contract-item-name]#++grant_role++#++(ref self: ContractState, role: felt252, account: ContractAddress)++` [.item-kind]#external# + +Grants `role` to `account`. + +If `account` had not been already granted `role`, emits a {RoleGranted} +event. + +Requirements: + +- the caller must have ``role``'s admin role. + +May emit a {RoleGranted} event. + +[.contract-item] +[[AccessControl-revoke_role]] +==== `[.contract-item-name]#++revoke_role++#++(ref self: ContractState, role: felt252, account: ContractAddress)++` [.item-kind]#external# + +Revokes `role` from `account`. + +If `account` had been granted `role`, emits a {RoleRevoked} event. + +Requirements: + +- the caller must have ``role``'s admin role. + +May emit a {RoleRevoked} event. + +[.contract-item] +[[AccessControl-renounce_role]] +==== `[.contract-item-name]#++renounce_role++#++(ref self: ContractState, role: felt252, account: ContractAddress)++` [.item-kind]#external# + +Revokes `role` from the calling account. + +Roles are often managed via {grant_role} and {revoke_role}. This function's +purpose is to provide a mechanism for accounts to lose their privileges +if they are compromised (such as when a trusted device is misplaced). + +If the calling account had been revoked `role`, emits a {RoleRevoked} +event. + +Requirements: + +- the caller must be `account`. + +May emit a {RoleRevoked} event. + +[.contract-item] +[[AccessControl-supports_interface]] +==== `[.contract-item-name]#++supports_interface++#++(self: @ContractState, interface_id: felt252) → bool++` [.item-kind]#external# + +Returns whether a contract implements a given interface or not. + +[#AccessControl-Internal-Functions] +==== Internal Functions + +[.contract-item] +[[AccessControl-initializer]] +==== `[.contract-item-name]#++initializer++#++(ref self: ContractState)++` [.item-kind]#internal# + +Initializes the contract by registering the xref:#IAccessControl[IAccessControl] interface ID. + +[.contract-item] +[[AccessControl-_set_role_admin]] +==== `[.contract-item-name]#++_set_role_admin++#++(ref self: ContractState, role: felt252, admin_role: felt252)++` [.item-kind]#internal# + +Sets `admin_role` as ``role``'s admin role. + +Emits a {RoleAdminChanged} event. + +[.contract-item] +[[AccessControl-_grant_role]] +==== `[.contract-item-name]#++_grant_role++#++(ref self: ContractState, role: felt252, account: ContractAddress)++` [.item-kind]#internal# + +Grants `role` to `account`. + +Internal function without access restriction. + +May emit a {RoleGranted} event. + +[.contract-item] +[[AccessControl-_revoke_role]] +==== `[.contract-item-name]#++_revoke_role++#++(ref self: ContractState, role: felt252, account: ContractAddress)++` [.item-kind]#internal# + +Revokes `role` from `account`. + +Internal function without access restriction. + +May emit a {RoleRevoked} event. + +[.contract-item] +[[AccessControl-assert_only_role]] +==== `[.contract-item-name]#++assert_only_role++#++(self: @ContractState, role: felt252)++` [.item-kind]#internal# + +Panics if called by any account without the given `role`. + +[#AccessControl-Events] +==== Events + +[.contract-item] +[[AccessControl-RoleAdminChanged]] +==== `[.contract-item-name]#++RoleAdminChanged++#++(role: felt252, previous_admin_role: ContractAddress, new_admin_role: ContractAddress)++` [.item-kind]#event# + +See xref:IAccessControl-RoleAdminChanged[IAccessControl::RoleAdminChanged]. + +[.contract-item] +[[AccessControl-RoleGranted]] +==== `[.contract-item-name]#++RoleGranted++#++(role: felt252, account: ContractAddress, sender: ContractAddress)++` [.item-kind]#event# + +See xref:IAccessControl-RoleGranted[IAccessControl::RoleGranted]. + +[.contract-item] +[[AccessControl-RoleRevoked]] +==== `[.contract-item-name]#++RoleRevoked++#++(role: felt252, account: ContractAddress, sender: ContractAddress)++` [.item-kind]#event# + +See xref:IAccessControl-RoleRevoked[IAccessControl::RoleRevoked]. diff --git a/docs/modules/ROOT/pages/api/account.adoc b/docs/modules/ROOT/pages/api/account.adoc index 52ed25191..942158af4 100644 --- a/docs/modules/ROOT/pages/api/account.adoc +++ b/docs/modules/ROOT/pages/api/account.adoc @@ -1,5 +1,6 @@ :github-icon: pass:[] :snip6: https://github.com/ericnordelo/SNIPs/blob/feat/standard-account/SNIPS/snip-6.md[SNIP-6] +:inner-src5: xref:api/introspection.adoc#ISRC5[SRC5 ID] = Account @@ -9,7 +10,7 @@ Reference of interfaces, presets, and utilities related to account contracts. [.contract] [[ISRC6]] -=== `++ISRC6++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/cairo-2/src/account/interface.cairo#L12[{github-icon},role=heading-link] +=== `++ISRC6++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-0.7.0/src/account/interface.cairo#L12[{github-icon},role=heading-link] ```javascript use openzeppelin::account::interface::ISRC6; @@ -17,6 +18,12 @@ use openzeppelin::account::interface::ISRC6; Interface of the SRC6 Standard Account as defined in the {snip6}. +[.contract-index] +.{inner-src5} +-- +0x2ceccef7f994940b3962a6c67e0ba4fcd37df7d131417c604f91e03caecc1cd +-- + [.contract-index] .Functions -- @@ -56,7 +63,7 @@ Returns the short string `'VALID'` if valid, otherwise it reverts. [.contract] [[Account]] -=== `++Account++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/cairo-2/src/account/account.cairo#L27[{github-icon},role=heading-link] +=== `++Account++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-0.7.0/src/account/account.cairo#L27[{github-icon},role=heading-link] :OwnerAdded: xref:Account-OwnerAdded[OwnerAdded] :OwnerRemoved: xref:Account-OwnerRemoved[OwnerRemoved] @@ -67,9 +74,9 @@ use openzeppelin::account::Account; Account contract implementation extending xref:ISRC6[`ISRC6`]. [.contract-index] -.Utilities +.Constructor -- -* xref:#Account-assert_only_self[`++InternalImpl::assert_only_self(self)++`] +* xref:#Account-constructor[`++constructor(self, _public_key)++`] -- [.contract-index] @@ -77,24 +84,20 @@ Account contract implementation extending xref:ISRC6[`ISRC6`]. -- * xref:#Account-\\__validate_deploy__[`++__validate_deploy__(self, hash, signature)++`] -[.contract-subindex-inherited] .SRC6Impl * xref:#Account-\\__execute__[`++__execute__(self, calls)++`] * xref:#Account-\\__validate__[`++__validate__(self, calls)++`] * xref:#Account-is_valid_signature[`++is_valid_signature(self, hash, signature)++`] -[.contract-subindex-inherited] .SRC5Impl * xref:#Account-supports_interface[`++supports_interface(self, interface_id)++`] -[.contract-subindex-inherited] .DeclarerImpl * xref:#Account-\\__validate_declare__[`++__validate_declare__(self, class_hash)++`] -[.contract-subindex-inherited] .PublicKeyImpl * xref:#Account-set_public_key[`++set_public_key(self, new_public_key)++`] @@ -104,15 +107,13 @@ Account contract implementation extending xref:ISRC6[`ISRC6`]. [.contract-index] .Internal Functions -- -* xref:#Account-constructor[`++constructor(self, _public_key)++`] - -[.contract-subindex-inherited] .InternalImpl * xref:#Account-initializer[`++initializer(self, _public_key)++`] * xref:#Account-validate_transaction[`++validate_transaction(self)++`] * xref:#Account-_set_public_key[`++_set_public_key(self, new_public_key)++`] * xref:#Account-_is_valid_signature[`++_is_valid_signature(self, hash, signature)++`] +* xref:#Account-assert_only_self[`++assert_only_self(self)++`] -- [.contract-index] @@ -122,17 +123,8 @@ Account contract implementation extending xref:ISRC6[`ISRC6`]. * xref:#Account-OwnerRemoved[`++OwnerRemoved(removed_owner_guid)++`] -- -[#Account-Utilities] -==== Utilities - -[.contract-item] -[[Account-assert_only_self]] -==== `[.contract-item-name]#++assert_only_self++#++(self: @ContractState)++` [.item-kind]#internal# - -Validates that the caller is the account itself. Otherwise it reverts. - -[#Account-Functions] -==== Functions +[#Account-Constructor] +==== Constructor [.contract-item] [[Account-constructor]] @@ -142,6 +134,9 @@ Initializes the account with the given public key, and registers the ISRC6 inter Emits an {OwnerAdded} event. +[#Account-External-Functions] +==== External Functions + [.contract-item] [[Account-__validate_deploy__]] ==== `[.contract-item-name]#++__validate_deploy__++#++(self: @ContractState, class_hash: felt252, contract_address_salt: felt252, _public_key: felt252) → felt252++` [.item-kind]#external# @@ -197,6 +192,9 @@ Emits both an {OwnerRemoved} and an {OwnerAdded} event. Returns the current public key of the account. +[#Account-Internal-Functions] +==== Internal Functions + [.contract-item] [[Account-initializer]] ==== `[.contract-item-name]#++initializer++#++(ref self: ContractState, _public_key: felt252)++` [.item-kind]#internal# @@ -230,6 +228,12 @@ CAUTION: The usage of this method outside the `set_public_key` function is disco Validates the provided `signature` for the `hash`, using the account current public key. +[.contract-item] +[[Account-assert_only_self]] +==== `[.contract-item-name]#++assert_only_self++#++(self: @ContractState)++` [.item-kind]#internal# + +Validates that the caller is the account itself. Otherwise it reverts. + [#Account-Events] ==== Events From 2ac98beefbed0770d4dddd094575f14a5305df4d Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Fri, 29 Sep 2023 02:10:06 -0400 Subject: [PATCH 175/246] Add indexed keys to token events (#746) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add indexed keys to events * fix event assertions * use pop_log from utils * fix formatting * remove event id from keys * remove PartialEq from events * revert event assertion changes * fix sentence * remove extra line * fix comment * add assert_indexed_keys * apply assert_indexed_keys to tests * fix formatting * tidy up code * Apply suggestions from code review Co-authored-by: Martín Triay * remove import --------- Co-authored-by: Martín Triay --- src/tests/account/test_account.cairo | 14 +++++++------- src/tests/token/test_erc20.cairo | 13 +++++++++++++ src/tests/token/test_erc721.cairo | 21 +++++++++++++++++++++ src/tests/upgrades/test_upgradeable.cairo | 3 +-- src/tests/utils.cairo | 21 ++++++++++++++++++++- src/token/erc20/erc20.cairo | 4 ++++ src/token/erc721/erc721.cairo | 8 ++++++++ 7 files changed, 74 insertions(+), 10 deletions(-) diff --git a/src/tests/account/test_account.cairo b/src/tests/account/test_account.cairo index 2ca9550a8..cef5a31e3 100644 --- a/src/tests/account/test_account.cairo +++ b/src/tests/account/test_account.cairo @@ -101,7 +101,7 @@ fn test_constructor() { let mut state = STATE(); Account::constructor(ref state, PUBLIC_KEY); - let event = testing::pop_log::(ZERO()).unwrap(); + let event = utils::pop_log::(ZERO()).unwrap(); assert(event.new_owner_guid == PUBLIC_KEY, 'Invalid owner key'); utils::assert_no_events_left(ZERO()); @@ -447,10 +447,10 @@ fn test_public_key_setter_and_getter() { // Set key PublicKeyImpl::set_public_key(ref state, NEW_PUBKEY); - let event = testing::pop_log::(ACCOUNT_ADDRESS()).unwrap(); + let event = utils::pop_log::(ACCOUNT_ADDRESS()).unwrap(); assert(event.removed_owner_guid == 0, 'Invalid old owner key'); - let event = testing::pop_log::(ACCOUNT_ADDRESS()).unwrap(); + let event = utils::pop_log::(ACCOUNT_ADDRESS()).unwrap(); assert(event.new_owner_guid == NEW_PUBKEY, 'Invalid new owner key'); utils::assert_no_events_left(ACCOUNT_ADDRESS()); @@ -488,10 +488,10 @@ fn test_public_key_setter_and_getter_camel() { // Set key PublicKeyCamelImpl::setPublicKey(ref state, NEW_PUBKEY); - let event = testing::pop_log::(ACCOUNT_ADDRESS()).unwrap(); + let event = utils::pop_log::(ACCOUNT_ADDRESS()).unwrap(); assert(event.removed_owner_guid == 0, 'Invalid old owner key'); - let event = testing::pop_log::(ACCOUNT_ADDRESS()).unwrap(); + let event = utils::pop_log::(ACCOUNT_ADDRESS()).unwrap(); assert(event.new_owner_guid == NEW_PUBKEY, 'Invalid new owner key'); utils::assert_no_events_left(ACCOUNT_ADDRESS()); @@ -521,7 +521,7 @@ fn test_initializer() { let mut state = STATE(); Account::InternalImpl::initializer(ref state, PUBLIC_KEY); - let event = testing::pop_log::(ZERO()).unwrap(); + let event = utils::pop_log::(ZERO()).unwrap(); assert(event.new_owner_guid == PUBLIC_KEY, 'Invalid owner key'); utils::assert_no_events_left(ZERO()); @@ -577,7 +577,7 @@ fn test__set_public_key() { let mut state = STATE(); Account::InternalImpl::_set_public_key(ref state, PUBLIC_KEY); - let event = testing::pop_log::(ZERO()).unwrap(); + let event = utils::pop_log::(ZERO()).unwrap(); assert(event.new_owner_guid == PUBLIC_KEY, 'Invalid owner key'); utils::assert_no_events_left(ZERO()); diff --git a/src/tests/token/test_erc20.cairo b/src/tests/token/test_erc20.cairo index 6ff440859..73f4034a3 100644 --- a/src/tests/token/test_erc20.cairo +++ b/src/tests/token/test_erc20.cairo @@ -9,6 +9,7 @@ use openzeppelin::token::erc20::ERC20::ERC20Impl; use openzeppelin::token::erc20::ERC20::InternalImpl; use openzeppelin::token::erc20::ERC20::Transfer; use openzeppelin::token::erc20::ERC20; +use openzeppelin::utils::serde::SerializedAppend; use starknet::ContractAddress; use starknet::contract_address_const; use starknet::testing; @@ -597,6 +598,12 @@ fn assert_event_approval(owner: ContractAddress, spender: ContractAddress, value assert(event.owner == owner, 'Invalid `owner`'); assert(event.spender == spender, 'Invalid `spender`'); assert(event.value == value, 'Invalid `value`'); + + // Check indexed keys + let mut indexed_keys = array![]; + indexed_keys.append_serde(owner); + indexed_keys.append_serde(spender); + utils::assert_indexed_keys(event, indexed_keys.span()) } fn assert_only_event_approval(owner: ContractAddress, spender: ContractAddress, value: u256) { @@ -609,6 +616,12 @@ fn assert_event_transfer(from: ContractAddress, to: ContractAddress, value: u256 assert(event.from == from, 'Invalid `from`'); assert(event.to == to, 'Invalid `to`'); assert(event.value == value, 'Invalid `value`'); + + // Check indexed keys + let mut indexed_keys = array![]; + indexed_keys.append_serde(from); + indexed_keys.append_serde(to); + utils::assert_indexed_keys(event, indexed_keys.span()); } fn assert_only_event_transfer(from: ContractAddress, to: ContractAddress, value: u256) { diff --git a/src/tests/token/test_erc721.cairo b/src/tests/token/test_erc721.cairo index 4299928f9..c0eb59bea 100644 --- a/src/tests/token/test_erc721.cairo +++ b/src/tests/token/test_erc721.cairo @@ -20,6 +20,7 @@ use openzeppelin::token::erc721::ERC721::{ }; use openzeppelin::token::erc721::ERC721; use openzeppelin::token::erc721; +use openzeppelin::utils::serde::SerializedAppend; use starknet::contract_address_const; use starknet::ContractAddress; use starknet::testing; @@ -1389,6 +1390,12 @@ fn assert_event_approval_for_all( assert(event.operator == operator, 'Invalid `operator`'); assert(event.approved == approved, 'Invalid `approved`'); utils::assert_no_events_left(ZERO()); + + // Check indexed keys + let mut indexed_keys = array![]; + indexed_keys.append_serde(owner); + indexed_keys.append_serde(operator); + utils::assert_indexed_keys(event, indexed_keys.span()); } fn assert_event_approval(owner: ContractAddress, approved: ContractAddress, token_id: u256) { @@ -1397,6 +1404,13 @@ fn assert_event_approval(owner: ContractAddress, approved: ContractAddress, toke assert(event.approved == approved, 'Invalid `approved`'); assert(event.token_id == token_id, 'Invalid `token_id`'); utils::assert_no_events_left(ZERO()); + + // Check indexed keys + let mut indexed_keys = array![]; + indexed_keys.append_serde(owner); + indexed_keys.append_serde(approved); + indexed_keys.append_serde(token_id); + utils::assert_indexed_keys(event, indexed_keys.span()); } fn assert_event_transfer(from: ContractAddress, to: ContractAddress, token_id: u256) { @@ -1405,4 +1419,11 @@ fn assert_event_transfer(from: ContractAddress, to: ContractAddress, token_id: u assert(event.to == to, 'Invalid `to`'); assert(event.token_id == token_id, 'Invalid `token_id`'); utils::assert_no_events_left(ZERO()); + + // Check indexed keys + let mut indexed_keys = array![]; + indexed_keys.append_serde(from); + indexed_keys.append_serde(to); + indexed_keys.append_serde(token_id); + utils::assert_indexed_keys(event, indexed_keys.span()); } diff --git a/src/tests/upgrades/test_upgradeable.cairo b/src/tests/upgrades/test_upgradeable.cairo index c43f8518f..fff1d8ed6 100644 --- a/src/tests/upgrades/test_upgradeable.cairo +++ b/src/tests/upgrades/test_upgradeable.cairo @@ -10,7 +10,6 @@ use openzeppelin::upgrades::upgradeable::Upgradeable::Upgraded; use starknet::ClassHash; use starknet::ContractAddress; use starknet::Felt252TryIntoClassHash; -use starknet::testing; const VALUE: felt252 = 123; @@ -46,7 +45,7 @@ fn test_upgraded_event() { let v1 = deploy_v1(); v1.upgrade(V2_CLASS_HASH()); - let event = testing::pop_log::(v1.contract_address).unwrap(); + let event = utils::pop_log::(v1.contract_address).unwrap(); assert(event.class_hash == V2_CLASS_HASH(), 'Invalid class hash'); } diff --git a/src/tests/utils.cairo b/src/tests/utils.cairo index e0ebeacb3..74ce5893a 100644 --- a/src/tests/utils.cairo +++ b/src/tests/utils.cairo @@ -18,16 +18,35 @@ fn deploy(contract_class_hash: felt252, calldata: Array) -> ContractAdd /// Pop the earliest unpopped logged event for the contract as the requested type /// and checks there's no more data left on the event, preventing unaccounted params. -/// Indexed event members are currently not supported, so they are ignored. +/// This function also removes the first key from the event. This is because indexed +/// params are set as event keys, but the first event key is always set as the +/// event ID. fn pop_log, impl TEvent: starknet::Event>( address: ContractAddress ) -> Option { let (mut keys, mut data) = testing::pop_log_raw(address)?; + + // Remove the event ID from the keys + keys.pop_front(); + let ret = starknet::Event::deserialize(ref keys, ref data); assert(data.is_empty(), 'Event has extra data'); ret } +/// Asserts that `expected_keys` exactly matches the indexed keys from `event`. +/// `expected_keys` must include all indexed event keys for `event` in the order +/// that they're defined. +fn assert_indexed_keys, impl TEvent: starknet::Event>( + event: T, expected_keys: Span +) { + let mut keys = array![]; + let mut data = array![]; + + event.append_keys_and_data(ref keys, ref data); + assert(expected_keys == keys.span(), 'Invalid keys'); +} + fn assert_no_events_left(address: ContractAddress) { assert(testing::pop_log_raw(address).is_none(), 'Events remaining on queue'); } diff --git a/src/token/erc20/erc20.cairo b/src/token/erc20/erc20.cairo index c62cd3803..ee1204d36 100644 --- a/src/token/erc20/erc20.cairo +++ b/src/token/erc20/erc20.cairo @@ -27,14 +27,18 @@ mod ERC20 { #[derive(Drop, starknet::Event)] struct Transfer { + #[key] from: ContractAddress, + #[key] to: ContractAddress, value: u256 } #[derive(Drop, starknet::Event)] struct Approval { + #[key] owner: ContractAddress, + #[key] spender: ContractAddress, value: u256 } diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index d67952f2d..8572f50c8 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -37,21 +37,29 @@ mod ERC721 { #[derive(Drop, starknet::Event)] struct Transfer { + #[key] from: ContractAddress, + #[key] to: ContractAddress, + #[key] token_id: u256 } #[derive(Drop, starknet::Event)] struct Approval { + #[key] owner: ContractAddress, + #[key] approved: ContractAddress, + #[key] token_id: u256 } #[derive(Drop, starknet::Event)] struct ApprovalForAll { + #[key] owner: ContractAddress, + #[key] operator: ContractAddress, approved: bool } From 4ce53099da8d782a37de4b287d8a41021b270c43 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 29 Sep 2023 17:20:01 +0200 Subject: [PATCH 176/246] Update Introspection docs (#721) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: update format and add api * fix: typo * feat: add counterfactual deployment doc * feat: add API entries * feat: add events * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Andrew Fleming * feat: update from reviews * feat: apply review updates * feat: update docs * feat: update docs * feat: update from account docs * feat: update main page * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * feat: apply review updates * fix: account casing * feat: add headers * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * feat: add link * feat: move API * feat: add event references * feat: update API * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * refactor: update wording * Update docs/antora.yml Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * feat: apply update reviews * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * refactor: UI * fix: UI * feat: focus on SRC6 * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Andrew Fleming * feat: apply review updates * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * feat: apply review updates * Update docs/modules/ROOT/pages/access.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/access.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/access.adoc Co-authored-by: Martín Triay * feat: apply review updates * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/introspection.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/introspection.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/introspection.adoc Co-authored-by: Martín Triay * feat: moving external functions on top * feat: add example * feat: apply review updates * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/introspection.adoc Co-authored-by: Andrew Fleming * feat: apply update reviews * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Martín Triay * feat: apply review updates * feat: apply update reviews * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Martín Triay * feat: apply update reviews * Update docs/modules/ROOT/pages/access.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/access.adoc Co-authored-by: Martín Triay * feat: apply review updates * feat: remove sn_keccak in comments * feat: replace cairo-2 with replace-0.7.0 * feat: replace cairo-2 with replace-0.7.0 * feat: remove grayed-out areas * feat: remove grayed-out areas --------- Co-authored-by: Andrew Fleming Co-authored-by: Martín Triay --- docs/modules/ROOT/nav.adoc | 5 +- docs/modules/ROOT/pages/accounts.adoc | 3 +- docs/modules/ROOT/pages/api/access.adoc | 34 +++- docs/modules/ROOT/pages/api/account.adoc | 2 +- .../modules/ROOT/pages/api/introspection.adoc | 94 +++++++++ docs/modules/ROOT/pages/introspection.adoc | 186 ++++++------------ src/tests/introspection/test_dual_src5.cairo | 4 - 7 files changed, 185 insertions(+), 143 deletions(-) create mode 100644 docs/modules/ROOT/pages/api/introspection.adoc diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 331fae8d5..9cf7f760a 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -17,7 +17,10 @@ // ** xref:erc1155.adoc[ERC1155] // * xref:security.adoc[Security] -// * xref:introspection.adoc[Introspection] + +* xref:introspection.adoc[Introspection] +** xref:/api/introspection.adoc[API Reference] + // * xref:udc.adoc[Universal Deployer Contract] // * xref:utilities.adoc[Utilities] diff --git a/docs/modules/ROOT/pages/accounts.adoc b/docs/modules/ROOT/pages/accounts.adoc index 101f71f9c..da71bce85 100644 --- a/docs/modules/ROOT/pages/accounts.adoc +++ b/docs/modules/ROOT/pages/accounts.adoc @@ -71,7 +71,6 @@ trait ISRC5 { Even though these interfaces are not enforced by the protocol, it's recommended to implement them for enabling interoperability with the ecosystem. - == Protocol-level methods In this section we will describe the methods that the protocol uses for abstracting the accounts. The first two @@ -98,4 +97,4 @@ wraps and exposes the `deploy_syscall` to provide arbitrary deployments through But if you don't have an account to invoke it, you will probably want to use the latter. To do counterfactual deployments, you need to implement another protocol-level entrypoint named -`\\__validate_deploy__`. You can check the {counterfactual} guide to learn how. +`\\__validate_deploy__`. Check the {counterfactual} guide to learn how. diff --git a/docs/modules/ROOT/pages/api/access.adoc b/docs/modules/ROOT/pages/api/access.adoc index 1a00ffa31..5e29b0240 100644 --- a/docs/modules/ROOT/pages/api/access.adoc +++ b/docs/modules/ROOT/pages/api/access.adoc @@ -7,14 +7,11 @@ = Access Control -[.readme-notice] -NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/access - This directory provides ways to restrict who can access the functions of a contract or when they can do it. - {Ownable} is a simple mechanism with a single "owner" role that can be assigned to a single account. -This mechanism can be useful in simple scenarios, but fine-grained access needs are likely to outgrow it. -- {AccessControl} provides a general role-based access control mechanism. Multiple hierarchical roles can be created and +This mechanism can be useful in simple scenarios, but fine grained access needs are likely to outgrow it. +- {AccessControl} provides a general role based access control mechanism. Multiple hierarchical roles can be created and assigned each to multiple accounts. == Authorization @@ -48,8 +45,13 @@ This module includes the `assert_only_owner` internal to restrict a function to .InternalImpl * xref:Ownable-initializer[`++initializer(self, owner)++`] +<<<<<<< HEAD +* xref:Ownable-_transfer_ownership[`++_transfer_ownership(self, new_owner)++`] +* xref:Ownable-assert_only_owner[`++assert_only_owner(self)++`] +======= * xref:Ownable-assert_only_owner[`++assert_only_owner(self)++`] * xref:Ownable-_transfer_ownership[`++_transfer_ownership(self, new_owner)++`] +>>>>>>> 2ac98beefbed0770d4dddd094575f14a5305df4d -- [.contract-index] @@ -77,7 +79,11 @@ Can only be called by the current owner. Emits an xref:Ownable-OwnershipTransferred[OwnershipTransferred] event. [.contract-item] +<<<<<<< HEAD +[[Ownable-renounce_ownership--]] +======= [[Ownable-renounce_ownership]] +>>>>>>> 2ac98beefbed0770d4dddd094575f14a5305df4d ==== `[.contract-item-name]#++renounce_ownership++#++(ref self: ContractState)++` [.item-kind]#external# Leaves the contract without owner. It will not be possible to call @@ -93,17 +99,24 @@ thereby removing any functionality that is only available to the owner. [[Ownable-initializer]] ==== `[.contract-item-name]#++initializer++#++(ref self: ContractState, owner: ContractAddress)++` [.item-kind]#internal# +<<<<<<< HEAD +Initializes the contract setting `owner` as the initial owner. +======= Initializes the contract and sets `owner` as the initial owner. +>>>>>>> 2ac98beefbed0770d4dddd094575f14a5305df4d Emits an xref:Ownable-OwnershipTransferred[OwnershipTransferred] event. [.contract-item] +<<<<<<< HEAD +======= [[Ownable-assert_only_owner]] ==== `[.contract-item-name]#++assert_only_owner++#++(self: @ContractState)++` [.item-kind]#internal# Panics if called by any account other than the owner. [.contract-item] +>>>>>>> 2ac98beefbed0770d4dddd094575f14a5305df4d [[Ownable-_transfer_ownership]] ==== `[.contract-item-name]#++_transfer_ownership++#++(ref self: ContractState, new_owner: ContractAddress)++` [.item-kind]#internal# @@ -112,6 +125,15 @@ Internal function without access restriction. Emits an xref:Ownable-OwnershipTransferred[OwnershipTransferred] event. +<<<<<<< HEAD +[.contract-item] +[[Ownable-assert_only_owner]] +==== `[.contract-item-name]#++assert_only_owner++#++(self: @ContractState)++` [.item-kind]#internal# + +Panics if called by any account other than the owner. + +======= +>>>>>>> 2ac98beefbed0770d4dddd094575f14a5305df4d [#Ownable-Events] ==== Events @@ -410,7 +432,7 @@ May emit a {RoleRevoked} event. [[AccessControl-supports_interface]] ==== `[.contract-item-name]#++supports_interface++#++(self: @ContractState, interface_id: felt252) → bool++` [.item-kind]#external# -Returns whether a contract implements a given interface or not. +See xref:api/introspection.adoc#ISRC5-supports_interface[ISRC5::supports_interface]. [#AccessControl-Internal-Functions] ==== Internal Functions diff --git a/docs/modules/ROOT/pages/api/account.adoc b/docs/modules/ROOT/pages/api/account.adoc index 942158af4..74fd497c3 100644 --- a/docs/modules/ROOT/pages/api/account.adoc +++ b/docs/modules/ROOT/pages/api/account.adoc @@ -168,7 +168,7 @@ See xref:ISRC6-is_valid_signature[ISRC6::is_valid_signature]. [[Account-supports_interface]] ==== `[.contract-item-name]#++supports_interface++#++(self: @ContractState, interface_id: felt252) → bool++` [.item-kind]#external# -Returns whether a contract implements a given interface or not. +See xref:api/introspection.adoc#ISRC5-supports_interface[ISRC5::supports_interface]. [.contract-item] [[Account-__validate_declare__]] diff --git a/docs/modules/ROOT/pages/api/introspection.adoc b/docs/modules/ROOT/pages/api/introspection.adoc new file mode 100644 index 000000000..2e58603b6 --- /dev/null +++ b/docs/modules/ROOT/pages/api/introspection.adoc @@ -0,0 +1,94 @@ +:github-icon: pass:[] +:snip5: https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md[SNIP-5] +:inner-src5: xref:api/introspection.adoc#ISRC5[SRC5 ID] + += Introspection + +Reference of interfaces and utilities related to https://en.wikipedia.org/wiki/Type_introspection[type introspection]. + +== Core + +[.contract] +[[ISRC5]] +=== `++ISRC5++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-0.7.0/src/introspection/interface.cairo#L7[{github-icon},role=heading-link] + +```javascript +use openzeppelin::introspection::interface::ISRC5; +``` + +Interface of the SRC5 Introspection Standard as defined in {snip5}. + +[.contract-index] +.{inner-src5} +-- +0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055 +-- + +[.contract-index] +.Functions +-- +* xref:#ISRC5-supports_interface[`++supports_interface(interface_id)++`] +-- + +[#ISRC5-Functions] +==== Functions + +[.contract-item] +[[ISRC5-supports_interface]] +==== `[.contract-item-name]#++supports_interface++#++(interface_id: felt252) → bool++` [.item-kind]#external# + +Checks whether the contract implements the given interface. + +TIP: Check xref:introspection#computing_the_interface_id[Computing the Interface ID] for more information +on how to compute this ID. + +[.contract] +[[SRC5]] +=== `++SRC5++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-0.7.0/src/introspection/src5.cairo[{github-icon},role=heading-link] + +```javascript +use openzeppelin::introspection::src5::SRC5; +``` + +SRC5 contract implementation extending xref:ISRC5[`ISRC5`]. + +[.contract-index] +.External Functions +-- +.SRC5Impl + +* xref:#SRC5-supports_interface[`++supports_interface(self, interface_id)++`] +-- + +[.contract-index] +.Internal Functions +-- +.InternalImpl + +* xref:#SRC5-register_interface[`++register_interface(self, interface_id)++`] +* xref:#SRC5-deregister_interface[`++deregister_interface(self, interface_id)++`] +-- + +[#SRC5-External-Functions] +==== External Functions + +[.contract-item] +[[SRC5-supports_interface]] +==== `[.contract-item-name]#++supports_interface++#++(self: @ContractState, interface_id: felt252) → bool++` [.item-kind]#external# + +See xref:ISRC5-supports_interface[`ISRC5::supports_interface`]. + +[#SRC5-Internal-Functions] +==== Internal Functions + +[.contract-item] +[[SRC5-register_interface]] +==== `[.contract-item-name]#++register_interface++#++(ref self: ContractState, interface_id: felt252)++` [.item-kind]#internal# + +Registers support for the given `interface_id`. + +[.contract-item] +[[SRC5-deregister_interface]] +==== `[.contract-item-name]#++deregister_interface++#++(ref self: ContractState, interface_id: felt252)++` [.item-kind]#internal# + +Deregisters support for the given `interface_id`. diff --git a/docs/modules/ROOT/pages/introspection.adoc b/docs/modules/ROOT/pages/introspection.adoc index 859d78735..9c162374b 100644 --- a/docs/modules/ROOT/pages/introspection.adoc +++ b/docs/modules/ROOT/pages/introspection.adoc @@ -1,160 +1,88 @@ -= Introspection - -CAUTION: Expect this module to evolve. - -== Table of Contents - -* <> - ** <> - ** <> - ** <> - ** <> - ** <> - *** <> - ** <> - *** <> - *** <> - -== ERC165 - -The ERC165 standard allows smart contracts to exercise https://en.wikipedia.org/wiki/Type_introspection[type introspection] on other contracts, that is, examining which functions can be called on them. -This is usually referred to as a contract's interface. +:eip165: https://eips.ethereum.org/EIPS/eip-165[EIP165] +:src5: https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md[SRC5] +:src5-rs: https://github.com/ericnordelo/src5-rs[src5-rs] -Cairo contracts, like Ethereum contracts, have no native concept of an interface, so applications must usually simply trust they are not making an incorrect call. -For trusted setups this is a non-issue, but often unknown and untrusted third-party addresses need to be interacted with. -There may even not be any direct calls to them! -(e.g. -ERC20 tokens may be sent to a contract that lacks a way to transfer them out of it, locking them forever). -In these cases, a contract declaring its interface can be very helpful in preventing errors. += Introspection -It should be noted that the https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.6.1/src/openzeppelin/utils/constants/library.cairo[constants library] includes constant variables referencing all of the interface ids used in these contracts. -This allows for more legible code i.e. -using `IERC165_ID` instead of `0x01ffc9a7`. +To smooth interoperability, often standards require smart contracts to implement https://en.wikipedia.org/wiki/Type_introspection[introspection mechanisms]. -=== Interface calculations +In Ethereum, the {eip165} standard defines how contracts should declare +their support for a given interface, and how other contracts may query this support. -In order to ensure EVM/StarkNet compatibility, interface identifiers are not calculated on StarkNet. -Instead, the interface IDs are hardcoded from their EVM calculations. -On the EVM, the interface ID is calculated from the selector's first four bytes of the hash of the function's signature while Cairo selectors are 252 bytes long. -Due to this difference, hardcoding EVM's already-calculated interface IDs is the most consistent approach to both follow the EIP165 standard and EVM compatibility. +Starknet offers a similar mechanism for interface introspection defined by the {src5} standard. -=== Registering interfaces +== SRC5 -For a contract to declare its support for a given interface, the contract should import the ERC165 library and register its support. -It's recommended to register interface support upon contract deployment through a constructor either directly or indirectly (as an initializer) like this: +Similar to its Ethereum counterpart, the {src5} standard requires contracts to implement the `supports_interface` function, +which can be used by others to query if a given interface is supported: -[,cairo] +[,javascript] ---- -from openzeppelin.introspection.erc165.library import ERC165 - -INTERFACE_ID = 0x12345678 - -@constructor -func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { - ERC165.register_interface(INTERFACE_ID); - return (); +trait ISRC5 { + /// Query if a contract implements an interface. + /// Receives the interface identifier as specified in SRC-5. + /// Returns `true` if the contract implements `interface_id`, `false` otherwise. + fn supports_interface(interface_id: felt252) -> bool; } ---- -=== Querying interfaces +=== Computing the interface ID -To query a target contract's support for an interface, the querying contract should call `supportsInterface` through IERC165 with the target contract's address and the queried interface id. -Here's an example: +The interface ID, as specified in the standard, is the https://en.wikipedia.org/wiki/Exclusive_or[XOR] of all the +https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md#extended-function-selector[Extended Function Selectors] +of the interface. We strongly advise reading the SNIP to understand the specifics of computing these +extended function selectors. There are tools such as {src5-rs} that can help with this process. -[,cairo] ----- -from openzeppelin.introspection.erc265.IERC165 import IERC165 +=== Registering interfaces -INTERFACE_ID = 0x12345678 +For a contract to declare its support for a given interface, the contract should import the SRC5 module and +register its support. It's recommended to register interface support upon contract deployment through a constructor +either directly or indirectly (as an initializer) like this: -func check_support{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - target_contract: felt -) -> (success: felt) { - let (is_supported) = IERC165.supportsInterface(target_contract, INTERFACE_ID); - return (is_supported); -} +[,javascript] ---- +#[starknet::contract] +mod MyContract { + use openzeppelin::account::interface; + use openzeppelin::introspection::src5::SRC5; -NOTE: `supportsInterface` is camelCased because it is an exposed contract method as part of ERC165's interface. -This differs from library methods (such as `supports_interface` from the https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.6.1/src/openzeppelin/introspection/erc165/library.cairo[ERC165 library]) which are snake_cased and not exposed. -See the xref:extensibility.adoc#function_names_and_coding_style[Function names and coding style] for more details. - -=== IERC165 + #[storage] + struct Storage {} -[,cairo] ----- -@contract_interface -namespace IERC165 { - func supportsInterface(interfaceId: felt) -> (success: felt) { + #[constructor] + fn constructor(ref self: ContractState) { + let mut unsafe_state = SRC5::unsafe_new_contract_state(); + SRC5::InternalImpl::register_interface(ref unsafe_state, interface::ISRC6_ID); } -} ----- -=== IERC165 API Specification - -[,cairo] ----- -func supportsInterface(interfaceId: felt) -> (success: felt) { + (...) } ---- -==== `supportsInterface` - -Returns true if this contract implements the interface defined by `interfaceId`. - -Parameters: - -[,cairo] ----- -interfaceId: felt ----- +=== Querying interfaces -Returns: +Use the `supports_interface` function to query a contract's support for a given interface. -[,cairo] ----- -success: felt +[,javascript] ---- +#[starknet::contract] +mod MyContract { + use openzeppelin::account::interface; + use openzeppelin::introspection::interface::ISRC5DispatcherTrait; + use openzeppelin::introspection::interface::ISRC5Dispatcher; + use openzeppelin::introspection::src5::SRC5; + use starknet::ContractAddress; -=== ERC165 Library Functions - -[,cairo] ----- -func supports_interface(interface_id: felt) -> (success: felt) { -} + #[storage] + struct Storage {} -func register_interface(interface_id: felt) { + #[external(v0)] + fn query_is_account(self: @ContractState, target: ContractAddress) -> bool { + let dispatcher = ISRC5Dispatcher { contract_address: target }; + dispatcher.supports_interface(interface::ISRC6_ID) + } } ---- -[#supportsinterface2] -==== `supports_interface` - -Returns true if this contract implements the interface defined by `interface_id`. - -Parameters: - -[,cairo] ----- -interface_id: felt ----- - -Returns: - -[,cairo] ----- -success: felt ----- - -==== `register_interface` - -Calling contract declares support for a specific interface defined by `interface_id`. - -Parameters: - -[,cairo] ----- -interface_id: felt ----- - -Returns: None. +TIP: If you are unsure whether a contract implements SRC5 or not, you can follow the process described in +https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md#how-to-detect-if-a-contract-implements-src-5[here]. diff --git a/src/tests/introspection/test_dual_src5.cairo b/src/tests/introspection/test_dual_src5.cairo index 9720e8968..80c13e56a 100644 --- a/src/tests/introspection/test_dual_src5.cairo +++ b/src/tests/introspection/test_dual_src5.cairo @@ -1,9 +1,5 @@ use openzeppelin::introspection::dual_src5::DualCaseSRC5; use openzeppelin::introspection::dual_src5::DualCaseSRC5Trait; -use openzeppelin::introspection::interface::ISRC5CamelDispatcher; -use openzeppelin::introspection::interface::ISRC5CamelDispatcherTrait; -use openzeppelin::introspection::interface::ISRC5Dispatcher; -use openzeppelin::introspection::interface::ISRC5DispatcherTrait; use openzeppelin::introspection::interface::ISRC5_ID; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::mocks::src5_mocks::CamelSRC5Mock; From fe695e422c367443f6695fe862c861fbb836b1fe Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 29 Sep 2023 17:31:12 +0200 Subject: [PATCH 177/246] Update upgrade docs (#731) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: update format and add api * fix: typo * feat: add counterfactual deployment doc * feat: add API entries * feat: add events * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Andrew Fleming * feat: update from reviews * feat: apply review updates * feat: update docs * feat: update docs * feat: update from account docs * feat: update main page * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/guides/deployment.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * feat: apply review updates * fix: account casing * feat: add headers * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * feat: add link * feat: move API * feat: add event references * feat: update API * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming * refactor: update wording * Update docs/antora.yml Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * feat: apply update reviews * Update docs/modules/ROOT/pages/api/account.adoc Co-authored-by: Martín Triay * refactor: UI * fix: UI * feat: focus on SRC6 * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Andrew Fleming * feat: apply review updates * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/introspection.adoc Co-authored-by: Andrew Fleming * feat: finish v1 * Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Martín Triay * feat: apply review updates * Update docs/modules/ROOT/pages/access.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/access.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/access.adoc Co-authored-by: Martín Triay * feat: apply review updates * Update docs/modules/ROOT/pages/upgrades.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/upgrades.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/upgrades.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/upgrades.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/upgrades.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/upgrades.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/upgrades.adoc Co-authored-by: Andrew Fleming * feat: apply review updates * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/introspection.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/introspection.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/introspection.adoc Co-authored-by: Martín Triay * feat: moving external functions on top * feat: add example * feat: apply review updates * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/api/introspection.adoc Co-authored-by: Andrew Fleming * feat: apply update reviews * feat: apply update reviews * refactor: fmt files * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/upgrades.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/upgrades.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/upgrades.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/upgrades.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/upgrades.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/upgrades.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/upgrades.adoc Co-authored-by: Martín Triay * feat: apply review updates * Update docs/modules/ROOT/pages/introspection.adoc Co-authored-by: Martín Triay * feat: apply review updates * feat: apply update reviews * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/api/access.adoc Co-authored-by: Martín Triay * feat: apply update reviews * Update docs/modules/ROOT/pages/upgrades.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/upgrades.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/upgrades.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/upgrades.adoc Co-authored-by: Andrew Fleming * feat: apply review updates * Update docs/modules/ROOT/pages/access.adoc Co-authored-by: Martín Triay * Update docs/modules/ROOT/pages/access.adoc Co-authored-by: Martín Triay * feat: apply review updates * Update docs/modules/ROOT/pages/upgrades.adoc Co-authored-by: Martín Triay * feat: apply review updates * feat: remove sn_keccak in comments * feat: apply review updates * Update docs/modules/ROOT/pages/upgrades.adoc Co-authored-by: Andrew Fleming * Update docs/modules/ROOT/pages/upgrades.adoc Co-authored-by: Martín Triay * feat: replace cairo-2 with replace-0.7.0 * feat: replace cairo-2 with replace-0.7.0 * feat: replace cairo-2 with release-0.7.0 * feat: remove types * feat: remove grayed-out areas * feat: remove grayed-out areas * feat: remove grayed-out areas * feat: apply review update --------- Co-authored-by: Andrew Fleming Co-authored-by: Martín Triay --- docs/modules/ROOT/nav.adoc | 3 +- docs/modules/ROOT/pages/api/access.adoc | 25 -- docs/modules/ROOT/pages/api/upgrades.adoc | 85 ++++++ docs/modules/ROOT/pages/proxies.adoc | 353 ---------------------- docs/modules/ROOT/pages/upgrades.adoc | 94 ++++++ src/tests/mocks/upgrades_v1.cairo | 6 +- src/tests/mocks/upgrades_v2.cairo | 6 +- src/upgrades.cairo | 2 + src/upgrades/interface.cairo | 2 +- src/upgrades/upgradeable.cairo | 1 - 10 files changed, 190 insertions(+), 387 deletions(-) create mode 100644 docs/modules/ROOT/pages/api/upgrades.adoc delete mode 100644 docs/modules/ROOT/pages/proxies.adoc create mode 100644 docs/modules/ROOT/pages/upgrades.adoc diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 9cf7f760a..342abe67c 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -1,8 +1,9 @@ * xref:index.adoc[Overview] //* xref:wizard.adoc[Wizard] //* xref:extensibility.adoc[Extensibility] -//* xref:proxies.adoc[Proxies and Upgrades] * xref:interfaces.adoc[Interfaces and Dispatchers] +* xref:upgrades.adoc[Upgrades] +** xref:/api/upgrades.adoc[API Reference] * xref:accounts.adoc[Accounts] ** xref:/guides/deployment.adoc[Counterfactual deployments] diff --git a/docs/modules/ROOT/pages/api/access.adoc b/docs/modules/ROOT/pages/api/access.adoc index 5e29b0240..86fd86e4d 100644 --- a/docs/modules/ROOT/pages/api/access.adoc +++ b/docs/modules/ROOT/pages/api/access.adoc @@ -45,13 +45,8 @@ This module includes the `assert_only_owner` internal to restrict a function to .InternalImpl * xref:Ownable-initializer[`++initializer(self, owner)++`] -<<<<<<< HEAD -* xref:Ownable-_transfer_ownership[`++_transfer_ownership(self, new_owner)++`] -* xref:Ownable-assert_only_owner[`++assert_only_owner(self)++`] -======= * xref:Ownable-assert_only_owner[`++assert_only_owner(self)++`] * xref:Ownable-_transfer_ownership[`++_transfer_ownership(self, new_owner)++`] ->>>>>>> 2ac98beefbed0770d4dddd094575f14a5305df4d -- [.contract-index] @@ -79,11 +74,7 @@ Can only be called by the current owner. Emits an xref:Ownable-OwnershipTransferred[OwnershipTransferred] event. [.contract-item] -<<<<<<< HEAD -[[Ownable-renounce_ownership--]] -======= [[Ownable-renounce_ownership]] ->>>>>>> 2ac98beefbed0770d4dddd094575f14a5305df4d ==== `[.contract-item-name]#++renounce_ownership++#++(ref self: ContractState)++` [.item-kind]#external# Leaves the contract without owner. It will not be possible to call @@ -99,24 +90,17 @@ thereby removing any functionality that is only available to the owner. [[Ownable-initializer]] ==== `[.contract-item-name]#++initializer++#++(ref self: ContractState, owner: ContractAddress)++` [.item-kind]#internal# -<<<<<<< HEAD -Initializes the contract setting `owner` as the initial owner. -======= Initializes the contract and sets `owner` as the initial owner. ->>>>>>> 2ac98beefbed0770d4dddd094575f14a5305df4d Emits an xref:Ownable-OwnershipTransferred[OwnershipTransferred] event. [.contract-item] -<<<<<<< HEAD -======= [[Ownable-assert_only_owner]] ==== `[.contract-item-name]#++assert_only_owner++#++(self: @ContractState)++` [.item-kind]#internal# Panics if called by any account other than the owner. [.contract-item] ->>>>>>> 2ac98beefbed0770d4dddd094575f14a5305df4d [[Ownable-_transfer_ownership]] ==== `[.contract-item-name]#++_transfer_ownership++#++(ref self: ContractState, new_owner: ContractAddress)++` [.item-kind]#internal# @@ -125,15 +109,6 @@ Internal function without access restriction. Emits an xref:Ownable-OwnershipTransferred[OwnershipTransferred] event. -<<<<<<< HEAD -[.contract-item] -[[Ownable-assert_only_owner]] -==== `[.contract-item-name]#++assert_only_owner++#++(self: @ContractState)++` [.item-kind]#internal# - -Panics if called by any account other than the owner. - -======= ->>>>>>> 2ac98beefbed0770d4dddd094575f14a5305df4d [#Ownable-Events] ==== Events diff --git a/docs/modules/ROOT/pages/api/upgrades.adoc b/docs/modules/ROOT/pages/api/upgrades.adoc new file mode 100644 index 000000000..3c0eb3505 --- /dev/null +++ b/docs/modules/ROOT/pages/api/upgrades.adoc @@ -0,0 +1,85 @@ +:github-icon: pass:[] +:class_hash: https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/class-hash/[class hash] + += Upgrades + +Reference of interfaces and utilities related to upgradeability. + +== Core + +[.contract] +[[IUpgradeable]] +=== `++IUpgradeable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-0.7.0/src/upgrades/interface.cairo#L3[{github-icon},role=heading-link] + +:Upgraded: xref:Upgradeable-Upgraded[Upgraded] + +```javascript +use openzeppelin::upgrades::interface::IUpgradeable; +``` + +Interface of an upgradeable contract. + +[.contract-index] +.Functions +-- +* xref:#IUpgradeable-upgrade[`++upgrade(new_class_hash)++`] +-- + +[#IUpgradeable-Functions] +==== Functions + +[.contract-item] +[[IUpgradeable-upgrade]] +==== `[.contract-item-name]#++upgrade++#++(new_class_hash: ClassHash)++` [.item-kind]#external# + +Upgrades the contract code by updating its {class_hash}. + +NOTE: This function is usually protected by an xref:access.adoc[Access Control] mechanism. + +[.contract] +[[Upgradeable]] +=== `++Upgradeable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-0.7.0/src/upgrades/upgradeable.cairo[{github-icon},role=heading-link] + +```javascript +use openzeppelin::upgrades::upgradeable::Upgradeable; +``` + +Upgradeable contract module. + +[.contract-index] +.Internal Functions +-- +.InternalImpl + +* xref:#Upgradeable-_upgrade[`++_upgrade(self, new_class_hash)++`] +-- + +[.contract-index] +.Events +-- +* xref:#Upgradeable-Upgraded[`++Upgraded(class_hash)++`] +-- + +[#Upgradeable-Internal-Functions] +==== Internal Functions + +[.contract-item] +[[Upgradeable-_upgrade]] +==== `[.contract-item-name]#++_upgrade++#++(ref self: ContractState, new_class_hash: ClassHash)++` [.item-kind]#internal# + +Upgrades the contract by updating the contract {class_hash}. + +Requirements: + +- `new_class_hash` must be different from zero. + +Emits an {Upgraded} event. + +[#Upgradeable-Events] +==== Events + +[.contract-item] +[[Upgradeable-Upgraded]] +==== `[.contract-item-name]#++Upgraded++#++(class_hash: ClassHash)++` [.item-kind]#event# + +Emitted when the {class_hash} is upgraded. \ No newline at end of file diff --git a/docs/modules/ROOT/pages/proxies.adoc b/docs/modules/ROOT/pages/proxies.adoc deleted file mode 100644 index 8eb94047a..000000000 --- a/docs/modules/ROOT/pages/proxies.adoc +++ /dev/null @@ -1,353 +0,0 @@ -= Proxies - -NOTE: Expect rapid iteration as this pattern matures and more patterns potentially emerge. - -== Table of Contents - -* <> -* <> - ** <> - ** <> -* <> - ** <> - ** <> -* <> - ** <> - ** <> -* <> - ** <> - ** <> - ** <> -* <> - -== Quickstart - -The general workflow is: - -. Declare an implementation https://starknet.io/docs/hello_starknet/intro.html#declare-the-contract-on-the-starknet-testnet[contract class]. -. Deploy proxy contract with the implementation contract's class hash, and the inputs describing the call to initialize the proxy from the implementation (if required). This will redirect the call as a library_call to the implementation (similar to Solidity delegatecall). - -In Python, this would look as follows: - -[,python] ----- -# declare implementation contract -IMPLEMENTATION = await starknet.declare( - "path/to/implementation.cairo", -) - -# deploy proxy -selector = get_selector_from_name('initializer') -params = [ - proxy_admin # admin account -] -PROXY = await starknet.deploy( - "path/to/proxy.cairo", - constructor_calldata=[ - IMPLEMENTATION.class_hash, # set implementation contract class hash - selector, # initializer function selector - len(params), # calldata length in felt - *params # actual calldata - ] -) ----- - -== Solidity/Cairo upgrades comparison - -=== Constructors - -OpenZeppelin Contracts for Solidity requires the use of an alternative library for upgradeable contracts. -Consider that in Solidity constructors are not part of the deployed contract's runtime bytecode; -rather, a constructor's logic is executed only once when the contract instance is deployed and then discarded. -This is why proxies can't imitate the construction of its implementation, therefore requiring a different initialization mechanism. - -The constructor problem in upgradeable contracts is resolved by the use of initializer methods. -Initializer methods are essentially regular methods that execute the logic that would have been in the constructor. -Care needs to be exercised with initializers to ensure they can only be called once. -Thus, OpenZeppelin offers an https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable[upgradeable contracts library] where much of this process is abstracted away. -See OpenZeppelin's https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable[Writing Upgradeable Contracts] for more info. - -The Cairo programming language does not support inheritance. -Instead, Cairo contracts follow the xref:extensibility.adoc[Extensibility Pattern] which already uses initializer methods to mimic constructors. -Upgradeable contracts do not, therefore, require a separate library with refactored constructor logic. - -=== Storage - -OpenZeppelin's alternative Upgrades library also implements https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies#unstructured-storage-proxies[unstructured storage] for its upgradeable contracts. -The basic idea behind unstructured storage is to pseudo-randomize the storage structure of the upgradeable contract so it's based on variable names instead of declaration order, which makes the chances of storage collision during an upgrade extremely unlikely. - -The StarkNet compiler, meanwhile, already creates pseudo-random storage addresses by hashing the storage variable names (and keys in mappings) by default. -In other words, StarkNet already uses unstructured storage and does not need a second library to modify how storage is set. -See StarkNet's https://starknet.io/documentation/contracts/#contracts_storage[Contracts Storage documentation] for more information. - -[#proxies2] -== Proxies - -A proxy contract is a contract that delegates function calls to another contract. -This type of pattern decouples state and logic. -Proxy contracts store the state and redirect function calls to an implementation contract that handles the logic. -This allows for different patterns such as upgrades, where implementation contracts can change but the proxy contract (and thus the state) does not; -as well as deploying multiple proxy instances pointing to the same implementation. -This can be useful to deploy many contracts with identical logic but unique initialization data. - -In the case of contract upgrades, it is achieved by simply changing the proxy's reference to the class hash of the declared implementation. -This allows developers to add features, update logic, and fix bugs without touching the state or the contract address to interact with the application. - -=== Proxy contract - -The https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.6.1/src/openzeppelin/upgrades/presets/Proxy.cairo[Proxy contract] includes two core methods: - -. The `\\__default__` method is a fallback method that redirects a function call and associated calldata to the implementation contract. -. The `\\__l1_default__` method is also a fallback method; -however, it redirects the function call and associated calldata to a layer one contract. -In order to invoke `\\__l1_default__`, the original function call must include the library function `send_message_to_l1`. -See Cairo's https://www.cairo-lang.org/docs/hello_starknet/l1l2.html[Interacting with L1 contracts] for more information. - -Since this proxy is designed to work both as an https://eips.ethereum.org/EIPS/eip-1822[UUPS-flavored upgrade proxy] as well as a non-upgradeable proxy, it does not know how to handle its own state. -Therefore it requires the implementation contract class to be declared beforehand, so its class hash can be passed to the Proxy on construction time. - -When interacting with the contract, function calls should be sent by the user to the proxy. -The proxy's fallback function redirects the function call to the implementation contract to execute. - -=== Implementation contract - -The implementation contract, also known as the logic contract, receives the redirected function calls from the proxy contract. -The implementation contract should follow the xref:extensibility.adoc#the_pattern[Extensibility pattern] and import directly from the https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.6.1/src/openzeppelin/upgrades/library.cairo[Proxy library]. - -The implementation contract should: - -* Import `Proxy` namespace. -* Provide an external initializer function (calling `Proxy.initializer`) to intialize the proxy immediately after deployment. - -If the implementation is upgradeable, it should: - -* Include a method to upgrade the implementation (i.e. -`upgrade`). -* Use access control to protect the contract's upgradeability. - -The implementation contract should NOT: - -* Be deployed like a regular contract. -Instead, the implementation contract should be declared (which creates a `DeclaredClass` containing its hash and abi). -* Set its initial state with a traditional constructor (decorated with `@constructor`). -Instead, use an initializer method that invokes the Proxy `constructor`. - -NOTE: The Proxy `constructor` includes a check that ensures the initializer can only be called once; -however, `_set_implementation` does not include this check. -It's up to the developers to protect their implementation contract's upgradeability with access controls such as <>. - -For a full implementation contract example, please see: - -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/tests/mocks/ProxiableImplementation.cairo[Proxiable implementation] - -== Upgrades library API - -=== Methods - -[,cairo] ----- -func initializer(proxy_admin: felt) { -} - -func assert_only_admin() { -} - -func get_implementation_hash() -> (implementation: felt) { -} - -func get_admin() -> (admin: felt) { -} - -func _set_admin(new_admin: felt) { -} - -func _set_implementation_hash(new_implementation: felt) { -} ----- - -==== `initializer` - -Initializes the proxy contract with an initial implementation. - -Parameters: - -[,cairo] ----- -proxy_admin: felt ----- - -Returns: None. - -==== `assert_only_admin` - -Reverts if called by any account other than the admin. - -Parameters: None. - -Returns: None. - -==== `get_implementation` - -Returns the current implementation hash. - -Parameters: None. - -Returns: - -[,cairo] ----- -implementation: felt ----- - -==== `get_admin` - -Returns the current admin. - -Parameters: None. - -Returns: - -[,cairo] ----- -admin: felt ----- - -==== `_set_admin` - -Sets `new_admin` as the admin of the proxy contract. - -Parameters: - -[,cairo] ----- -new_admin: felt ----- - -Returns: None. - -==== `_set_implementation_hash` - -Sets `new_implementation` as the implementation's contract class. -This method is included in the proxy contract's constructor and can be used to upgrade contracts. - -Parameters: - -[,cairo] ----- -new_implementation: felt ----- - -Returns: None. - -=== Events - -[,cairo] ----- -func Upgraded(implementation: felt) { -} - -func AdminChanged(previousAdmin: felt, newAdmin: felt) { -} ----- - -==== `Upgraded` - -Emitted when a proxy contract sets a new implementation class hash. - -Parameters: - -[,cairo] ----- -implementation: felt ----- - -==== `AdminChanged` - -Emitted when the `admin` changes from `previousAdmin` to `newAdmin`. - -Parameters: - -[,cairo] ----- -previousAdmin: felt -newAdmin: felt ----- - -== Using proxies - -=== Contract upgrades - -To upgrade a contract, the implementation contract should include an `upgrade` method that, when called, changes the reference to a new deployed contract like this: - -[,python] ----- -# declare first implementation -IMPLEMENTATION = await starknet.declare( - "path/to/implementation.cairo", -) - -# deploy proxy -PROXY = await starknet.deploy( - "path/to/proxy.cairo", - constructor_calldata=[ - IMPLEMENTATION.class_hash, # set implementation hash - 0, # selector set to 0 ignores initialization - 0, # calldata length in felt - *[] # empty calldata - ] -) - -# declare implementation v2 -IMPLEMENTATION_V2 = await starknet.declare( - "path/to/implementation_v2.cairo", -) - -# call upgrade with the new implementation contract class hash -await signer.send_transaction( - account, PROXY.contract_address, 'upgrade', [ - IMPLEMENTATION_V2.class_hash - ] -) ----- - -For a full deployment and upgrade implementation, please see: - -* https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.6.1/tests/mocks/UpgradesMockV1.cairo[Upgrades V1] -* https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.6.1/tests/mocks/UpgradesMockV2.cairo[Upgrades V2] - -=== Declaring contracts - -StarkNet contracts come in two forms: contract classes and contract instances. -Contract classes represent the uninstantiated, stateless code; -whereas, contract instances are instantiated and include the state. -Since the Proxy contract references the implementation contract by its class hash, declaring an implementation contract proves sufficient (as opposed to a full deployment). -For more information on declaring classes, see https://starknet.io/docs/hello_starknet/intro.html#declare-contract[StarkNet's documentation]. - -=== Testing method calls - -As with most StarkNet contracts, interacting with a proxy contract requires an xref:accounts.adoc#quickstart[account abstraction]. -Due to limitations in the StarkNet testing framework, however, `@view` methods also require an account abstraction. This is only a requirement when testing. -The differences in getter methods written in Python, for example, are as follows: - -[,python] ----- -# standard ERC20 call -result = await erc20.totalSupply().call() - -# upgradeable ERC20 call -result = await signer.send_transaction( - account, PROXY.contract_address, 'totalSupply', [] -) ----- - -== Presets - -Presets are pre-written contracts that extend from our library of contracts. -They can be deployed as-is or used as templates for customization. - -Some presets include: - -* https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/token/erc20/presets/ERC20Upgradeable.cairo[ERC20Upgradeable] -* More to come! -Have an idea? -https://github.com/OpenZeppelin/cairo-contracts/issues/new/choose[Open an issue]! diff --git a/docs/modules/ROOT/pages/upgrades.adoc b/docs/modules/ROOT/pages/upgrades.adoc new file mode 100644 index 000000000..9872b8ef7 --- /dev/null +++ b/docs/modules/ROOT/pages/upgrades.adoc @@ -0,0 +1,94 @@ +:contract_classes: https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/contract-classes/[Contract Classes] +:class_hash: https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/class-hash/[class hash] +:replace_class_syscall: https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/system-calls-cairo1/#replace_class[replace_class] +:upgradeable: https://github.com/OpenZeppelin/cairo-contracts/blob/release-0.7.0/src/upgrades/upgradeable.cairo[Upgradeable] +:ownable: xref:access.adoc#ownership_and_ownable[Ownable] +:i_upgradeable: xref:api/upgrades.adoc#IUpgradeable[IUpgradeable] +:library_calls: https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/system-calls-cairo1/#library_call[library calls] + += Upgrades + +In different blockchains, multiple patterns have been developed for making a contract upgradeable including the widely adopted proxy patterns. + +Starknet has native upgradeability through a syscall that updates the contract source code, removing the need for proxies. + +== Replacing contract classes + +To better comprehend how upgradeability works in Starknet, it's important to understand the difference between a contract and its contract class. + +{contract_classes} represent the source code of a program. All contracts are associated to a class, and many contracts can be instances of the same one. Classes are usually represented by a {class_hash}, and before a contract of a class can be deployed, the class hash needs to be declared. + +=== `replace_class_syscall` + +The `{replace_class_syscall}` syscall allows a contract to update its source code by replacing its class hash once deployed. + + +[,javascript] +---- +/// Upgrades the contract source code to the new contract class. +fn _upgrade(new_class_hash: ClassHash) { + assert(!new_class_hash.is_zero(), 'Class hash cannot be zero'); + starknet::replace_class_syscall(new_class_hash).unwrap(); +} +---- + +NOTE: If a contract is deployed without this mechanism, its class hash can still be replaced through {library_calls}. + +== `Upgradeable` module + +OpenZeppelin Contracts for Cairo provides {upgradeable} to add upgradeability support to your contracts. + +=== Usage + +Upgrades are often very sensitive operations, and some form of access control is usually required to +avoid unauthorized upgrades. The {ownable} module is used in this example. + +NOTE: We will be using the following module to implement the {i_upgradeable} interface described in the API Reference section. + +[,javascript] +---- +#[starknet::contract] +mod UpgradeableContract { + use openzeppelin::access::ownable::Ownable; + use openzeppelin::upgrades::Upgradeable; + use openzeppelin::upgrades::interface::IUpgradeable; + use starknet::ClassHash; + use starknet::ContractAddress; + + #[storage] + struct Storage {} + + #[constructor] + fn constructor(self: @ContractState, owner: ContractAddress) { + let mut unsafe_state = Ownable::unsafe_new_contract_state(); + Ownable::InternalImpl::initializer(ref unsafe_state, owner); + } + + #[external(v0)] + impl UpgradeableImpl of IUpgradeable { + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { + // This function can only be called by the owner + let ownable_state = Ownable::unsafe_new_contract_state(); + Ownable::InternalImpl::assert_only_owner(@ownable_state); + + // Replace the class hash upgrading the contract + let mut upgradeable_state = Upgradeable::unsafe_new_contract_state(); + Upgradeable::InternalImpl::_upgrade(ref upgradeable_state, new_class_hash); + } + } + + (...) +} +---- + +== Proxies and Starknet + +Proxies enable different patterns such as upgrades and clones. But since Starknet achieves the same in different ways is that there's no support to implement them. + +In the case of contract upgrades, it is achieved by simply changing the contract's class hash. As of clones, contracts already are like clones of the class they implement. + +Implementing a proxy pattern in Starknet has an important limitation: there is no fallback mechanism to be used +for redirecting every potential function call to the implementation. This means that a generic proxy contract +can't be implemented. Instead, a limited proxy contract can implement specific functions that forward +their execution to another contract class. +This can still be useful for example to upgrade the logic of some functions. diff --git a/src/tests/mocks/upgrades_v1.cairo b/src/tests/mocks/upgrades_v1.cairo index 37d5c19b3..b7391bc28 100644 --- a/src/tests/mocks/upgrades_v1.cairo +++ b/src/tests/mocks/upgrades_v1.cairo @@ -6,7 +6,7 @@ use starknet::ClassHash; #[starknet::interface] trait IUpgradesV1 { - fn upgrade(ref self: TState, impl_hash: ClassHash); + fn upgrade(ref self: TState, new_class_hash: ClassHash); fn set_value(ref self: TState, val: felt252); fn get_value(self: @TState) -> felt252; fn remove_selector(self: @TState); @@ -33,9 +33,9 @@ mod UpgradesV1 { #[external(v0)] impl UpgradeableImpl of IUpgradeable { - fn upgrade(ref self: ContractState, impl_hash: ClassHash) { + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { let mut unsafe_state = Upgradeable::unsafe_new_contract_state(); - Upgradeable::InternalImpl::_upgrade(ref unsafe_state, impl_hash); + Upgradeable::InternalImpl::_upgrade(ref unsafe_state, new_class_hash); } } diff --git a/src/tests/mocks/upgrades_v2.cairo b/src/tests/mocks/upgrades_v2.cairo index 322c47ba1..69e4ec55f 100644 --- a/src/tests/mocks/upgrades_v2.cairo +++ b/src/tests/mocks/upgrades_v2.cairo @@ -6,7 +6,7 @@ use starknet::ClassHash; #[starknet::interface] trait IUpgradesV2 { - fn upgrade(ref self: TState, impl_hash: ClassHash); + fn upgrade(ref self: TState, new_class_hash: ClassHash); fn set_value(ref self: TState, val: felt252); fn set_value2(ref self: TState, val: felt252); fn get_value(self: @TState) -> felt252; @@ -36,9 +36,9 @@ mod UpgradesV2 { #[external(v0)] impl UpgradeableImpl of IUpgradeable { - fn upgrade(ref self: ContractState, impl_hash: ClassHash) { + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { let mut unsafe_state = Upgradeable::unsafe_new_contract_state(); - Upgradeable::InternalImpl::_upgrade(ref unsafe_state, impl_hash); + Upgradeable::InternalImpl::_upgrade(ref unsafe_state, new_class_hash); } } diff --git a/src/upgrades.cairo b/src/upgrades.cairo index 18d78fc73..9530af29a 100644 --- a/src/upgrades.cairo +++ b/src/upgrades.cairo @@ -1,2 +1,4 @@ mod upgradeable; mod interface; + +use upgradeable::Upgradeable; diff --git a/src/upgrades/interface.cairo b/src/upgrades/interface.cairo index 1857c003d..f8bba7083 100644 --- a/src/upgrades/interface.cairo +++ b/src/upgrades/interface.cairo @@ -2,5 +2,5 @@ use starknet::ClassHash; #[starknet::interface] trait IUpgradeable { - fn upgrade(ref self: TState, impl_hash: ClassHash); + fn upgrade(ref self: TState, new_class_hash: ClassHash); } diff --git a/src/upgrades/upgradeable.cairo b/src/upgrades/upgradeable.cairo index de9e546f2..fcf6c7eec 100644 --- a/src/upgrades/upgradeable.cairo +++ b/src/upgrades/upgradeable.cairo @@ -1,7 +1,6 @@ #[starknet::contract] mod Upgradeable { use starknet::ClassHash; - use starknet::SyscallResult; #[storage] struct Storage {} From bdab69e14ecbc8d0d85da3af75cdae3373a535a5 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 2 Oct 2023 14:44:16 -0400 Subject: [PATCH 178/246] fix comment --- docs/modules/ROOT/pages/erc721.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 83d35ff55..9e786102e 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -51,7 +51,7 @@ trait IERC721ABI { fn symbol() -> felt252; fn token_uri(token_id: u256) -> felt252; - // ERC721 Camel + // Camel case compatibility fn balanceOf(account: ContractAddress) -> u256; fn ownerOf(tokenId: u256) -> ContractAddress; fn safeTransferFrom( From 95ffdbe01b6042fa8bbb741f6347623214ebbf51 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Fri, 13 Oct 2023 12:19:06 -0400 Subject: [PATCH 179/246] Apply suggestions from code review Co-authored-by: Eric Nordelo --- src/token/erc721/erc721.cairo | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 104beaab9..a3cc4bcc7 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -1,10 +1,10 @@ -//! SPDX-License-Identifier: MIT -//! OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/erc721.cairo) -//! -//! # ERC721 Contract and Implementation -//! -//! This ERC721 contract includes both a library and a basic preset implementation -//! which includes the IERC721Metadata implementation. +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/erc721.cairo) + +/// # ERC721 Contract and Implementation +/// +/// This ERC721 contract includes both a library and a basic preset implementation +/// which includes the IERC721Metadata implementation. #[starknet::contract] mod ERC721 { use openzeppelin::account; @@ -113,10 +113,9 @@ mod ERC721 { } } + /// Adds camelCase support for `ISRC5`. #[external(v0)] impl SRC5CamelImpl of ISRC5Camel { - /// Camel case support. - /// See [supports_interface](supports_interface). fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { src5::SRC5::SRC5CamelImpl::supportsInterface(@src5_state(), interfaceId) } @@ -142,10 +141,9 @@ mod ERC721 { } } + /// Adds camelCase support for `IERC721Metadata`. #[external(v0)] impl ERC721MetadataCamelOnlyImpl of interface::IERC721MetadataCamelOnly { - /// Camel case support. - /// See [token_uri](token_uri). fn tokenURI(self: @ContractState, tokenId: u256) -> felt252 { assert(self._exists(tokenId), Errors::INVALID_TOKEN_ID); self.ERC721_token_uri.read(tokenId) @@ -179,7 +177,7 @@ mod ERC721 { } /// Change or reaffirm the approved address for an NFT. - /// Emits an [Approval](Approval) event. + /// Emits an `Approval` event. fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) { let owner = self._owner_of(token_id); @@ -193,6 +191,7 @@ mod ERC721 { /// Enable or disable approval for `operator` to manage all of the /// caller's assets. + /// /// Emits an [Approval](Approval) event. fn set_approval_for_all( ref self: ContractState, operator: ContractAddress, approved: bool From ce7d1f38c04bf8da63ef01ac52a721cd16149782 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 13 Oct 2023 15:13:24 -0400 Subject: [PATCH 180/246] fix comments --- src/token/erc721/erc721.cairo | 148 ++++++++++++++++++++++++++-------- 1 file changed, 114 insertions(+), 34 deletions(-) diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index a3cc4bcc7..5eaf3e13f 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -134,6 +134,7 @@ mod ERC721 { } /// Returns the Uniform Resource Identifier (URI) for the `token_id` token. + /// /// If the URI is not set for the `token_id`, the return value will be `0`. fn token_uri(self: @ContractState, token_id: u256) -> felt252 { assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); @@ -159,11 +160,19 @@ mod ERC721 { } /// Returns the owner address of `token_id`. + /// + /// Requirements: + /// + /// - `token_id` exists. fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress { self._owner_of(token_id) } /// Returns the address approved for `token_id`. + /// + /// Requirements: + /// + /// - `token_id` exists. fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); self.ERC721_token_approvals.read(token_id) @@ -177,6 +186,13 @@ mod ERC721 { } /// Change or reaffirm the approved address for an NFT. + /// + /// Requirements: + /// + /// - The caller is either an approved operator or the `token_id` owner. + /// - `to` cannot be the token owner zero address. + /// - `token_id` exists. + /// /// Emits an `Approval` event. fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) { let owner = self._owner_of(token_id); @@ -192,7 +208,11 @@ mod ERC721 { /// Enable or disable approval for `operator` to manage all of the /// caller's assets. /// - /// Emits an [Approval](Approval) event. + /// Requirements: + /// + /// - `operator` cannot be the caller. + /// + /// Emits an `Approval` event. fn set_approval_for_all( ref self: ContractState, operator: ContractAddress, approved: bool ) { @@ -200,7 +220,15 @@ mod ERC721 { } /// Transfer ownership of `token_id` from `from` to `to`. - /// Emits a [Transfer](Transfer) event. + /// + /// Requirements: + /// + /// - Caller is either approved or the `token_id` owner. + /// - `to` is not the zero address. + /// - `from` is not the zero address. + /// - `token_id` exists. + /// + /// Emits a `Transfer` event. fn transfer_from( ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 ) { @@ -211,9 +239,16 @@ mod ERC721 { } /// Safely transfer ownership of `token_id` from `from` to `to`. - /// If `to` is not an account contract, `to` must support IERC721Receiver; - /// otherwise, the transaction will fail. - /// Emits a [Transfer](Transfer) event. + /// + /// Requirements: + /// + /// - Caller is either approved or the `token_id` owner. + /// - `to` is not the zero address. + /// - `from` is not the zero address. + /// - `token_id` exists. + /// - `to` is either an account contract or supports the `IERC721Receiver` interface. + /// + /// Emits a `Transfer` event. fn safe_transfer_from( ref self: ContractState, from: ContractAddress, @@ -228,50 +263,37 @@ mod ERC721 { } } + /// Adds camelCase support for `IERC721`. #[external(v0)] impl ERC721CamelOnlyImpl of interface::IERC721CamelOnly { - /// Camel case support. - /// See [balance_of](balance_of). fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 { ERC721Impl::balance_of(self, account) } - /// Camel case support. - /// See [owner_of](owner_of). fn ownerOf(self: @ContractState, tokenId: u256) -> ContractAddress { ERC721Impl::owner_of(self, tokenId) } - /// Camel case support. - /// See [get_approved](get_approved). fn getApproved(self: @ContractState, tokenId: u256) -> ContractAddress { ERC721Impl::get_approved(self, tokenId) } - /// Camel case support. - /// See [is_approved_for_all](is_approved_for_all). fn isApprovedForAll( self: @ContractState, owner: ContractAddress, operator: ContractAddress ) -> bool { ERC721Impl::is_approved_for_all(self, owner, operator) } - /// Camel case support. - /// See [set_approval_for_all](set_approval_for_all). fn setApprovalForAll(ref self: ContractState, operator: ContractAddress, approved: bool) { ERC721Impl::set_approval_for_all(ref self, operator, approved) } - /// Camel case support. - /// See [transfer_from](transfer_from). fn transferFrom( ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256 ) { ERC721Impl::transfer_from(ref self, from, to, tokenId) } - /// Camel case support. - /// See [safe_transfer_from](safe_transfer_from). fn safeTransferFrom( ref self: ContractState, from: ContractAddress, @@ -303,6 +325,10 @@ mod ERC721 { } /// Returns the owner address of `token_id`. + /// + /// Requirements: + /// + /// - `token_id` exists. fn _owner_of(self: @ContractState, token_id: u256) -> ContractAddress { let owner = self.ERC721_owners.read(token_id); match owner.is_zero() { @@ -317,6 +343,10 @@ mod ERC721 { } /// Returns whether `spender` is allowed to manage `token_id`. + /// + /// Requirements: + /// + /// - `token_id` exists. fn _is_approved_or_owner( self: @ContractState, spender: ContractAddress, token_id: u256 ) -> bool { @@ -327,8 +357,16 @@ mod ERC721 { || spender == ERC721Impl::get_approved(self, token_id) } - /// Internal function that changes or reaffirms the approved address for an NFT. - /// Emits an [Approval[(Approval) event. + /// Changes or reaffirms the approved address for an NFT. + /// + /// Internal function without access restriction. + /// + /// Requirements: + /// + /// - `token_id` exists. + /// - `to` is not the current token owner. + /// + /// Emits an `Approval` event. fn _approve(ref self: ContractState, to: ContractAddress, token_id: u256) { let owner = self._owner_of(token_id); assert(owner != to, Errors::APPROVAL_TO_OWNER); @@ -337,9 +375,14 @@ mod ERC721 { self.emit(Approval { owner, approved: to, token_id }); } - /// Internal function that enables or disables approval for `operator` - /// to manage all of the `owner` assets. - /// Emits an [Approval[(Approval) event. + /// Enables or disables approval for `operator` to manage + /// all of the `owner` assets. + /// + /// Requirements: + /// + /// - `operator` cannot be the caller. + /// + /// Emits an `Approval` event. fn _set_approval_for_all( ref self: ContractState, owner: ContractAddress, @@ -351,8 +394,11 @@ mod ERC721 { self.emit(ApprovalForAll { owner, operator, approved }); } - /// Internal function that mints `token_id` and transfers it to `to`. - /// Emits a [Transfer](Transfer) event. + /// Mints `token_id` and transfers it to `to`. + /// + /// Internal function without access restriction. + /// + /// Emits a `Transfer` event. fn _mint(ref self: ContractState, to: ContractAddress, token_id: u256) { assert(!to.is_zero(), Errors::INVALID_RECEIVER); assert(!self._exists(token_id), Errors::ALREADY_MINTED); @@ -363,8 +409,17 @@ mod ERC721 { self.emit(Transfer { from: Zeroable::zero(), to, token_id }); } - /// Internal function that transfers `token_id` from `from` to `to`. - /// Emits a [Transfer](Transfer) event. + /// Transfers `token_id` from `from` to `to`. + /// + /// Internal function without access restriction. + /// + /// Requirements: + /// + /// - `to` is not the zero address. + /// - `from` is the token owner. + /// - `token_id` exists. + /// + /// Emits a `Transfer` event. fn _transfer( ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 ) { @@ -384,9 +439,15 @@ mod ERC721 { /// Internal function that destroys `token_id`. The approval is cleared when /// the token is burned. + /// /// This internal function does not check if the sender is authorized /// to operate on the token. - /// Emits a [Transfer](Transfer) event. + /// + /// Requirements: + /// + /// - `token_id` exists. + /// + /// Emits a `Transfer` event. fn _burn(ref self: ContractState, token_id: u256) { let owner = self._owner_of(token_id); @@ -400,9 +461,16 @@ mod ERC721 { } /// Internal function that safely mints `token_id` and transfers it to `to`. + /// /// If `to` is not an account contract, `to` must support IERC721Receiver; /// otherwise, the transaction will fail. - /// Emits a [Transfer](Transfer) event. + /// + /// Requirements: + /// + /// - `token_id` exists. + /// - `to` is either an account contract or supports the `IERC721Receiver` interface. + /// + /// Emits a `Transfer` event. fn _safe_mint( ref self: ContractState, to: ContractAddress, token_id: u256, data: Span ) { @@ -413,10 +481,19 @@ mod ERC721 { ); } - /// Internal function that safely transfers `token_id` token from `from` to `to`, + /// Internal function that safely transfers `token_id` token from `from` to `to`. + /// /// If `to` is not an account contract, `to` must support IERC721Receiver; /// otherwise, the transaction will fail. - /// Emits a [Transfer](Transfer) event. + /// + /// Requirements: + /// + /// - `to` cannot be the zero address. + /// - `from` must be the token owner. + /// - `token_id` exists. + /// - `to` is either an account contract or supports the `IERC721Receiver` interface. + /// + /// Emits a `Transfer` event. fn _safe_transfer( ref self: ContractState, from: ContractAddress, @@ -431,15 +508,18 @@ mod ERC721 { } /// Sets the `token_uri` of `token_id`. + /// + /// Requirements: + /// + /// - `token_id` exists. fn _set_token_uri(ref self: ContractState, token_id: u256, token_uri: felt252) { assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); self.ERC721_token_uri.write(token_id, token_uri) } } - #[internal] /// Internal function that checks if `to` either is an account contract or - /// has registered support for the ERC721 interface through SRC-5. + /// has registered support for the `IERC721Receiver` interface through SRC-5. /// The transaction will fail if both cases are false. fn _check_on_erc721_received( from: ContractAddress, to: ContractAddress, token_id: u256, data: Span From 3b9426633884f29670e5532df6dbab34dea0614a Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 13 Oct 2023 16:51:12 -0400 Subject: [PATCH 181/246] fix fn list, add camelCase section --- docs/modules/ROOT/pages/api/erc721.adoc | 36 +++++++++++-------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 0511696c1..4e73a99ab 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -221,9 +221,9 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi * xref:#ERC721-constructor[`++constructor(self, name, symbol)++`] [.contract-index] -.External Functions +.External functions -- -.IERC721 +.IERC721Impl * xref:#IERC721-balance_of[`++balance_of(self, account)++`] * xref:#IERC721-owner_of[`++owner_of(self, token_id)++`] * xref:#IERC721-safe_transfer_from[`++safe_transfer_from(self, from, to, token_id, data)++`] @@ -233,15 +233,19 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi * xref:#IERC721-get_approved[`++get_approved(self, token_id)++`] * xref:#IERC721-is_approved_for_all[`++is_approved_for_all(self, owner, operator)++`] -.ISRC5 +.ISRC5Impl * xref:/api/introspection.adoc#ISRC5-supports_interface[`++supports_interface(self, interface_id)++`] -.IERC721Metadata +.IERC721MetadataImpl * xref:#IERC721Metadata-name[`++name(self)++`] * xref:#IERC721Metadata-symbol[`++symbol(self)++`] * xref:#IERC721Metadata-token_uri[`++token_uri(self, token_id)++`] +-- -.ER721Camel +[.contract-index] +.camelCase support +-- +.ER721CamelImpl * xref:#ERC721-balanceOf[`++balanceOf(self, account)++`] * xref:#ERC721-ownerOf[`++ownerOf(self, tokenId)++`] * xref:#ERC721-safeTransferFrom[`++safeTransferFrom(self, from, to, tokenId, data)++`] @@ -249,7 +253,11 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi * xref:#ERC721-setApprovalForAll[`++setApprovalForAll(self, operator, approved)++`] * xref:#ERC721-getApproved[`++getApproved(self, tokenId)++`] * xref:#ERC721-isApprovedForAll[`++isApprovedForAll(self, owner, operator)++`] + +.SRC5CamelImpl * xref:/api/introspection.adoc#ISRC5-supports_interface[`++supportsInterface(self, interfaceId)++`] + +.ERC721MetadataCamelOnlyImpl * xref:#ERC721-tokenURI[`++tokenURI(self, tokenId)++`] -- @@ -394,70 +402,56 @@ See <>. See <>. +==== camelCase Support + [.contract-item] [[ERC721-balanceOf]] ==== `[.contract-item-name]#++balanceOf++#++(self: @ContractState, account: ContractAddress) -> u256++` [.item-kind]#external# See <>. -Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. - [.contract-item] [[ERC721-ownerOf]] ==== `[.contract-item-name]#++ownerOf++#++(self: @ContractState, tokenId: u256) -> ContractAddress++` [.item-kind]#external# See <>. -Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. - [.contract-item] [[ERC721-transferFrom]] ==== `[.contract-item-name]#++transferFrom++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256)++` [.item-kind]#external# See <>. -Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. - [.contract-item] [[ERC721-safeTransferFrom]] ==== `[.contract-item-name]#++safeTransferFrom++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256, data: Span)++` [.item-kind]#external# See <>. -Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. - [.contract-item] [[ERC721-setApprovalForAll]] ==== `[.contract-item-name]#++setApprovalForAll++#++(ref self: ContractState, operator: ContractAddress, approved: bool)++` [.item-kind]#external# See <>. -Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. - [.contract-item] [[ERC721-getApproved]] ==== `[.contract-item-name]#++getApproved++#++(self: @ContractState, tokenId: u256) -> ContractAddress++` [.item-kind]#external# See <>. -Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. - [.contract-item] [[ERC721-isApprovedForAll]] ==== `[.contract-item-name]#++isApprovedForAll++#++(self: @ContractState, owner: ContractAddress, operator: ContractAddress) -> bool++` [.item-kind]#external# See <>. -Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. - [.contract-item] [[ERC721-tokenURI]] ==== `[.contract-item-name]#++tokenURI++#++(self: @ContractState, tokenId: u256) -> felt252++` [.item-kind]#external# See <>. -Supports the Cairo v0 convention of writing external methods in camelCase as discussed {casing-discussion}. - ==== Internal functions [.contract-item] From 6ce354c214f1b9b8bce94cdb9a2252df61c3a85a Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 13 Oct 2023 16:52:48 -0400 Subject: [PATCH 182/246] add erc721 abi --- src/token/erc721/interface.cairo | 54 ++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/token/erc721/interface.cairo b/src/token/erc721/interface.cairo index 6fdc68272..71dbe1bf4 100644 --- a/src/token/erc721/interface.cairo +++ b/src/token/erc721/interface.cairo @@ -87,3 +87,57 @@ trait IERC721ReceiverCamel { data: Span ) -> felt252; } + +// +// ERC721 ABI +// + +#[starknet::interface] +trait ERC721ABI { + // IERC721 + fn balance_of(self: @TState, account: ContractAddress) -> u256; + fn owner_of(self: @TState, token_id: u256) -> ContractAddress; + fn safe_transfer_from( + ref self: TState, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span + ); + fn transfer_from(ref self: TState, from: ContractAddress, to: ContractAddress, token_id: u256); + fn approve(ref self: TState, to: ContractAddress, token_id: u256); + fn set_approval_for_all(ref self: TState, operator: ContractAddress, approved: bool); + fn get_approved(self: @TState, token_id: u256) -> ContractAddress; + fn is_approved_for_all( + self: @TState, owner: ContractAddress, operator: ContractAddress + ) -> bool; + + // ISRC5 + fn supports_interface(self: @TState, interface_id: felt252) -> bool; + + // IERC721Metadata + fn name(self: @TState) -> felt252; + fn symbol(self: @TState) -> felt252; + fn token_uri(self: @TState, token_id: u256) -> felt252; + + // IERC721Camel + fn balanceOf(self: @TState, account: ContractAddress) -> u256; + fn ownerOf(self: @TState, tokenId: u256) -> ContractAddress; + fn safeTransferFrom( + ref self: TState, + from: ContractAddress, + to: ContractAddress, + tokenId: u256, + data: Span + ); + fn transferFrom(ref self: TState, from: ContractAddress, to: ContractAddress, tokenId: u256); + fn setApprovalForAll(ref self: TState, operator: ContractAddress, approved: bool); + fn getApproved(self: @TState, tokenId: u256) -> ContractAddress; + fn isApprovedForAll(self: @TState, owner: ContractAddress, operator: ContractAddress) -> bool; + + // ISRC5Camel + fn supportsInterface(self: @TState, interfaceId: felt252) -> bool; + + // IERC721MetadataCamelOnly + fn tokenURI(self: @TState, tokenId: u256) -> felt252; +} From 272ef18f3745d74cf6246e62cf361290f2ea52b8 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 13 Oct 2023 16:56:27 -0400 Subject: [PATCH 183/246] fix comment --- src/token/erc721/interface.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/token/erc721/interface.cairo b/src/token/erc721/interface.cairo index 71dbe1bf4..f9436fdbc 100644 --- a/src/token/erc721/interface.cairo +++ b/src/token/erc721/interface.cairo @@ -120,7 +120,7 @@ trait ERC721ABI { fn symbol(self: @TState) -> felt252; fn token_uri(self: @TState, token_id: u256) -> felt252; - // IERC721Camel + // IERC721CamelOnly fn balanceOf(self: @TState, account: ContractAddress) -> u256; fn ownerOf(self: @TState, tokenId: u256) -> ContractAddress; fn safeTransferFrom( From 007d85e54c3a18a4cb3ad8598bf534c8c881fd72 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 13 Oct 2023 16:56:42 -0400 Subject: [PATCH 184/246] fix comments --- docs/modules/ROOT/pages/erc721.adoc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 9e786102e..438cf0162 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -51,7 +51,7 @@ trait IERC721ABI { fn symbol() -> felt252; fn token_uri(token_id: u256) -> felt252; - // Camel case compatibility + // IERC721CamelOnly fn balanceOf(account: ContractAddress) -> u256; fn ownerOf(tokenId: u256) -> ContractAddress; fn safeTransferFrom( @@ -64,7 +64,11 @@ trait IERC721ABI { fn setApprovalForAll(operator: ContractAddress, approved: bool); fn getApproved(tokenId: u256) -> ContractAddress; fn isApprovedForAll(owner: ContractAddress, operator: ContractAddress) -> bool; + + // ISRC5Camel fn supportsInterface(interfaceId: felt252) -> bool; + + // IERC721MetadataCamelOnly fn tokenURI(tokenId: u256) -> felt252; } ---- From 103375f8b3d1dc8a21460bba8eb393bd0cd0b15a Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 16 Oct 2023 01:23:23 -0400 Subject: [PATCH 185/246] consolidate mocks --- src/tests/mocks.cairo | 4 +- src/tests/mocks/camel721_mock.cairo | 103 -------- src/tests/mocks/erc721_mocks.cairo | 315 ++++++++++++++++++++++++ src/tests/mocks/erc721_panic_mock.cairo | 163 ------------ src/tests/mocks/snake721_mock.cairo | 103 -------- 5 files changed, 316 insertions(+), 372 deletions(-) delete mode 100644 src/tests/mocks/camel721_mock.cairo create mode 100644 src/tests/mocks/erc721_mocks.cairo delete mode 100644 src/tests/mocks/erc721_panic_mock.cairo delete mode 100644 src/tests/mocks/snake721_mock.cairo diff --git a/src/tests/mocks.cairo b/src/tests/mocks.cairo index e598542dd..901166430 100644 --- a/src/tests/mocks.cairo +++ b/src/tests/mocks.cairo @@ -1,12 +1,11 @@ mod accesscontrol_panic_mock; mod account_panic_mock; mod camel20_mock; -mod camel721_mock; mod camel_accesscontrol_mock; mod camel_account_mock; mod dual721_receiver_mocks; mod erc20_panic; -mod erc721_panic_mock; +mod erc721_mocks; mod erc721_receiver; mod initializable_mock; mod non_implementing_mock; @@ -15,7 +14,6 @@ mod pausable_mock; mod reentrancy_attacker_mock; mod reentrancy_mock; mod snake20_mock; -mod snake721_mock; mod snake_accesscontrol_mock; mod snake_account_mock; mod src5_mocks; diff --git a/src/tests/mocks/camel721_mock.cairo b/src/tests/mocks/camel721_mock.cairo deleted file mode 100644 index 2117697d8..000000000 --- a/src/tests/mocks/camel721_mock.cairo +++ /dev/null @@ -1,103 +0,0 @@ -#[starknet::contract] -mod CamelERC721Mock { - use openzeppelin::token::erc721::ERC721::ERC721CamelOnlyImpl; - use openzeppelin::token::erc721::ERC721::InternalImpl; - use openzeppelin::token::erc721::ERC721; - use starknet::ContractAddress; - use starknet::get_caller_address; - - #[storage] - struct Storage {} - - #[constructor] - fn constructor( - ref self: ContractState, name: felt252, symbol: felt252, tokenId: u256, uri: felt252 - ) { - let mut unsafe_state = ERC721::unsafe_new_contract_state(); - InternalImpl::initializer(ref unsafe_state, name, symbol); - InternalImpl::_mint(ref unsafe_state, get_caller_address(), tokenId); - InternalImpl::_set_token_uri(ref unsafe_state, tokenId, uri); - } - - #[external(v0)] - fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { - let unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721::SRC5CamelImpl::supportsInterface(@unsafe_state, interfaceId) - } - - #[external(v0)] - fn name(self: @ContractState) -> felt252 { - let unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721::ERC721MetadataImpl::name(@unsafe_state) - } - - #[external(v0)] - fn symbol(self: @ContractState) -> felt252 { - let unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721::ERC721MetadataImpl::symbol(@unsafe_state) - } - - #[external(v0)] - fn tokenURI(self: @ContractState, tokenId: u256) -> felt252 { - let unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721::ERC721MetadataCamelOnlyImpl::tokenURI(@unsafe_state, tokenId) - } - - #[external(v0)] - fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 { - let unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721CamelOnlyImpl::balanceOf(@unsafe_state, account) - } - - #[external(v0)] - fn ownerOf(self: @ContractState, tokenId: u256) -> ContractAddress { - let unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721CamelOnlyImpl::ownerOf(@unsafe_state, tokenId) - } - - #[external(v0)] - fn getApproved(self: @ContractState, tokenId: u256) -> ContractAddress { - let unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721CamelOnlyImpl::getApproved(@unsafe_state, tokenId) - } - - #[external(v0)] - fn isApprovedForAll( - self: @ContractState, owner: ContractAddress, operator: ContractAddress - ) -> bool { - let unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721CamelOnlyImpl::isApprovedForAll(@unsafe_state, owner, operator) - } - - #[external(v0)] - fn approve(ref self: ContractState, to: ContractAddress, tokenId: u256) { - let mut unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721::ERC721Impl::approve(ref unsafe_state, to, tokenId) - } - - #[external(v0)] - fn setApprovalForAll(ref self: ContractState, operator: ContractAddress, approved: bool) { - let mut unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721CamelOnlyImpl::setApprovalForAll(ref unsafe_state, operator, approved) - } - - #[external(v0)] - fn transferFrom( - ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256 - ) { - let mut unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721CamelOnlyImpl::transferFrom(ref unsafe_state, from, to, tokenId) - } - - #[external(v0)] - fn safeTransferFrom( - ref self: ContractState, - from: ContractAddress, - to: ContractAddress, - tokenId: u256, - data: Span - ) { - let mut unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721CamelOnlyImpl::safeTransferFrom(ref unsafe_state, from, to, tokenId, data) - } -} diff --git a/src/tests/mocks/erc721_mocks.cairo b/src/tests/mocks/erc721_mocks.cairo new file mode 100644 index 000000000..313ec160b --- /dev/null +++ b/src/tests/mocks/erc721_mocks.cairo @@ -0,0 +1,315 @@ +#[starknet::contract] +mod DualCaseERC721Mock { + use openzeppelin::token::erc721::ERC721 as erc721_component; + use openzeppelin::introspection::src5::SRC5 as src5_component; + use starknet::ContractAddress; + use starknet::get_caller_address; + + component!(path: erc721_component, storage: erc721, event: ERC721Event); + component!(path: src5_component, storage: src5, event: SRC5Event); + + #[abi(embed_v0)] + impl ERC721Impl = erc721_component::ERC721Impl; + #[abi(embed_v0)] + impl SRC5Impl = src5_component::SRC5Impl; + #[abi(embed_v0)] + impl ERC721MetadataImpl = erc721_component::ERC721MetadataImpl; + #[abi(embed_v0)] + impl ERC721CamelOnly = + erc721_component::ERC721CamelOnlyImpl; + #[abi(embed_v0)] + impl ERC721MetadataCamelOnly = erc721_component::ERC721MetadataCamelOnlyImpl; + impl ERC721InternalImpl = erc721_component::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc721: erc721_component::Storage, + #[substorage(v0)] + src5: src5_component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + ERC721Event: erc721_component::Event, + SRC5Event: src5_component::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, name: felt252, symbol: felt252, token_id: u256, uri: felt252 + ) { + self.erc721.initializer(name, symbol); + self.erc721._mint(get_caller_address(), token_id); + self.erc721._set_token_uri(token_id, uri); + } +} + +#[starknet::contract] +mod SnakeERC721Mock { + use openzeppelin::token::erc721::ERC721 as erc721_component; + use openzeppelin::introspection::src5::SRC5 as src5_component; + use starknet::ContractAddress; + use starknet::get_caller_address; + + component!(path: erc721_component, storage: erc721, event: ERC721Event); + component!(path: src5_component, storage: src5, event: SRC5Event); + + #[abi(embed_v0)] + impl ERC721Impl = erc721_component::ERC721Impl; + #[abi(embed_v0)] + impl SRC5Impl = src5_component::SRC5Impl; + #[abi(embed_v0)] + impl ERC721MetadataImpl = erc721_component::ERC721MetadataImpl; + impl InternalImpl = erc721_component::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc721: erc721_component::Storage, + #[substorage(v0)] + src5: src5_component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + ERC721Event: erc721_component::Event, + SRC5Event: src5_component::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, name: felt252, symbol: felt252, token_id: u256, uri: felt252 + ) { + self.erc721.initializer(name, symbol); + self.erc721._mint(get_caller_address(), token_id); + self.erc721._set_token_uri(token_id, uri); + } +} + +#[starknet::contract] +mod CamelERC721Mock { + use openzeppelin::token::erc721::ERC721::{ERC721Impl, ERC721MetadataImpl}; + use openzeppelin::token::erc721::ERC721 as erc721_component; + use openzeppelin::introspection::src5::SRC5 as src5_component; + use starknet::ContractAddress; + use starknet::get_caller_address; + + component!(path: erc721_component, storage: erc721, event: ERC721Event); + component!(path: src5_component, storage: src5, event: SRC5Event); + + #[abi(embed_v0)] + impl ERC721CamelOnly = + erc721_component::ERC721CamelOnlyImpl; + #[abi(embed_v0)] + impl SRC5Impl = src5_component::SRC5Impl; + #[abi(embed_v0)] + impl ERC721MetadataCamelOnly = erc721_component::ERC721MetadataCamelOnlyImpl; + impl InternalImpl = erc721_component::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc721: erc721_component::Storage, + #[substorage(v0)] + src5: src5_component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + ERC721Event: erc721_component::Event, + SRC5Event: src5_component::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, name: felt252, symbol: felt252, token_id: u256, uri: felt252 + ) { + self.erc721.initializer(name, symbol); + self.erc721._mint(get_caller_address(), token_id); + self.erc721._set_token_uri(token_id, uri); + } + + /// The following external methods are included because they are case-agnostic + /// and this contract should not embed the snake_case impl. + #[external(v0)] + fn approve(ref self: ContractState, to: ContractAddress, tokenId: u256) { + self.erc721.approve(to, tokenId); + } + + #[external(v0)] + fn name(self: @ContractState) -> felt252 { + self.erc721.name() + } + + #[external(v0)] + fn symbol(self: @ContractState) -> felt252 { + self.erc721.symbol() + } +} + +/// Although these modules are designed to panic, functions +/// still need a valid return value. We chose: +/// +/// 3 for felt252 +/// zero for ContractAddress +/// u256 { 3, 3 } for u256 +#[starknet::contract] +mod SnakeERC721PanicMock { + use starknet::ContractAddress; + use zeroable::Zeroable; + + #[storage] + struct Storage {} + + #[external(v0)] + fn name(self: @ContractState) -> felt252 { + panic_with_felt252('Some error'); + 3 + } + + #[external(v0)] + fn symbol(self: @ContractState) -> felt252 { + panic_with_felt252('Some error'); + 3 + } + + #[external(v0)] + fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) { + panic_with_felt252('Some error'); + } + + #[external(v0)] + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { + panic_with_felt252('Some error'); + false + } + + #[external(v0)] + fn token_uri(self: @ContractState, token_id: u256) -> felt252 { + panic_with_felt252('Some error'); + 3 + } + + #[external(v0)] + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { + panic_with_felt252('Some error'); + u256 { low: 3, high: 3 } + } + + #[external(v0)] + fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress { + panic_with_felt252('Some error'); + Zeroable::zero() + } + + #[external(v0)] + fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { + panic_with_felt252('Some error'); + Zeroable::zero() + } + + #[external(v0)] + fn is_approved_for_all( + self: @ContractState, owner: ContractAddress, operator: ContractAddress + ) -> bool { + panic_with_felt252('Some error'); + false + } + + #[external(v0)] + fn set_approval_for_all(ref self: ContractState, operator: ContractAddress, approved: bool) { + panic_with_felt252('Some error'); + } + + #[external(v0)] + fn transfer_from( + ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 + ) { + panic_with_felt252('Some error'); + } + + #[external(v0)] + fn safe_transfer_from( + ref self: ContractState, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span + ) { + panic_with_felt252('Some error'); + } +} + +#[starknet::contract] +mod CamelERC721PanicMock { + use starknet::ContractAddress; + use zeroable::Zeroable; + + #[storage] + struct Storage {} + + #[external(v0)] + fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { + panic_with_felt252('Some error'); + false + } + + #[external(v0)] + fn tokenURI(self: @ContractState, tokenId: u256) -> felt252 { + panic_with_felt252('Some error'); + 3 + } + + #[external(v0)] + fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 { + panic_with_felt252('Some error'); + u256 { low: 3, high: 3 } + } + + #[external(v0)] + fn ownerOf(self: @ContractState, tokenId: u256) -> ContractAddress { + panic_with_felt252('Some error'); + Zeroable::zero() + } + + #[external(v0)] + fn getApproved(self: @ContractState, tokenId: u256) -> ContractAddress { + panic_with_felt252('Some error'); + Zeroable::zero() + } + + #[external(v0)] + fn isApprovedForAll( + self: @ContractState, owner: ContractAddress, operator: ContractAddress + ) -> bool { + panic_with_felt252('Some error'); + false + } + + #[external(v0)] + fn setApprovalForAll(ref self: ContractState, operator: ContractAddress, approved: bool) { + panic_with_felt252('Some error'); + } + + #[external(v0)] + fn transferFrom( + ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256 + ) { + panic_with_felt252('Some error'); + } + + #[external(v0)] + fn safeTransferFrom( + ref self: ContractState, + from: ContractAddress, + to: ContractAddress, + tokenId: u256, + data: Span + ) { + panic_with_felt252('Some error'); + } +} diff --git a/src/tests/mocks/erc721_panic_mock.cairo b/src/tests/mocks/erc721_panic_mock.cairo deleted file mode 100644 index ace550d33..000000000 --- a/src/tests/mocks/erc721_panic_mock.cairo +++ /dev/null @@ -1,163 +0,0 @@ -// Although these modules are designed to panic, functions -// still need a valid return value. We chose: -// -// 3 for felt252 -// zero for ContractAddress -// u256 { 3, 3 } for u256 - -#[starknet::contract] -mod SnakeERC721PanicMock { - use starknet::ContractAddress; - use zeroable::Zeroable; - - #[storage] - struct Storage {} - - #[external(v0)] - fn name(self: @ContractState) -> felt252 { - panic_with_felt252('Some error'); - 3 - } - - #[external(v0)] - fn symbol(self: @ContractState) -> felt252 { - panic_with_felt252('Some error'); - 3 - } - - #[external(v0)] - fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) { - panic_with_felt252('Some error'); - } - - #[external(v0)] - fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { - panic_with_felt252('Some error'); - false - } - - #[external(v0)] - fn token_uri(self: @ContractState, token_id: u256) -> felt252 { - panic_with_felt252('Some error'); - 3 - } - - #[external(v0)] - fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { - panic_with_felt252('Some error'); - u256 { low: 3, high: 3 } - } - - #[external(v0)] - fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress { - panic_with_felt252('Some error'); - Zeroable::zero() - } - - #[external(v0)] - fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { - panic_with_felt252('Some error'); - Zeroable::zero() - } - - #[external(v0)] - fn is_approved_for_all( - self: @ContractState, owner: ContractAddress, operator: ContractAddress - ) -> bool { - panic_with_felt252('Some error'); - false - } - - #[external(v0)] - fn set_approval_for_all(ref self: ContractState, operator: ContractAddress, approved: bool) { - panic_with_felt252('Some error'); - } - - #[external(v0)] - fn transfer_from( - ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 - ) { - panic_with_felt252('Some error'); - } - - #[external(v0)] - fn safe_transfer_from( - ref self: ContractState, - from: ContractAddress, - to: ContractAddress, - token_id: u256, - data: Span - ) { - panic_with_felt252('Some error'); - } -} - -#[starknet::contract] -mod CamelERC721PanicMock { - use starknet::ContractAddress; - use zeroable::Zeroable; - - #[storage] - struct Storage {} - - #[external(v0)] - fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { - panic_with_felt252('Some error'); - false - } - - #[external(v0)] - fn tokenURI(self: @ContractState, tokenId: u256) -> felt252 { - panic_with_felt252('Some error'); - 3 - } - - #[external(v0)] - fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 { - panic_with_felt252('Some error'); - u256 { low: 3, high: 3 } - } - - #[external(v0)] - fn ownerOf(self: @ContractState, tokenId: u256) -> ContractAddress { - panic_with_felt252('Some error'); - Zeroable::zero() - } - - #[external(v0)] - fn getApproved(self: @ContractState, tokenId: u256) -> ContractAddress { - panic_with_felt252('Some error'); - Zeroable::zero() - } - - #[external(v0)] - fn isApprovedForAll( - self: @ContractState, owner: ContractAddress, operator: ContractAddress - ) -> bool { - panic_with_felt252('Some error'); - false - } - - #[external(v0)] - fn setApprovalForAll(ref self: ContractState, operator: ContractAddress, approved: bool) { - panic_with_felt252('Some error'); - } - - #[external(v0)] - fn transferFrom( - ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256 - ) { - panic_with_felt252('Some error'); - } - - #[external(v0)] - fn safeTransferFrom( - ref self: ContractState, - from: ContractAddress, - to: ContractAddress, - tokenId: u256, - data: Span - ) { - panic_with_felt252('Some error'); - } -} diff --git a/src/tests/mocks/snake721_mock.cairo b/src/tests/mocks/snake721_mock.cairo deleted file mode 100644 index 6f1382c61..000000000 --- a/src/tests/mocks/snake721_mock.cairo +++ /dev/null @@ -1,103 +0,0 @@ -#[starknet::contract] -mod SnakeERC721Mock { - use openzeppelin::token::erc721::ERC721::ERC721Impl; - use openzeppelin::token::erc721::ERC721::InternalImpl; - use openzeppelin::token::erc721::ERC721; - use starknet::ContractAddress; - use starknet::get_caller_address; - - #[storage] - struct Storage {} - - #[constructor] - fn constructor( - ref self: ContractState, name: felt252, symbol: felt252, token_id: u256, uri: felt252 - ) { - let mut unsafe_state = ERC721::unsafe_new_contract_state(); - InternalImpl::initializer(ref unsafe_state, name, symbol); - InternalImpl::_mint(ref unsafe_state, get_caller_address(), token_id); - InternalImpl::_set_token_uri(ref unsafe_state, token_id, uri); - } - - #[external(v0)] - fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { - let unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721::SRC5Impl::supports_interface(@unsafe_state, interface_id) - } - - #[external(v0)] - fn name(self: @ContractState) -> felt252 { - let unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721::ERC721MetadataImpl::name(@unsafe_state) - } - - #[external(v0)] - fn symbol(self: @ContractState) -> felt252 { - let unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721::ERC721MetadataImpl::symbol(@unsafe_state) - } - - #[external(v0)] - fn token_uri(self: @ContractState, token_id: u256) -> felt252 { - let unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721::ERC721MetadataImpl::token_uri(@unsafe_state, token_id) - } - - #[external(v0)] - fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { - let unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721Impl::balance_of(@unsafe_state, account) - } - - #[external(v0)] - fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress { - let unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721Impl::owner_of(@unsafe_state, token_id) - } - - #[external(v0)] - fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { - let unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721Impl::get_approved(@unsafe_state, token_id) - } - - #[external(v0)] - fn is_approved_for_all( - self: @ContractState, owner: ContractAddress, operator: ContractAddress - ) -> bool { - let unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721Impl::is_approved_for_all(@unsafe_state, owner, operator) - } - - #[external(v0)] - fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) { - let mut unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721Impl::approve(ref unsafe_state, to, token_id) - } - - #[external(v0)] - fn set_approval_for_all(ref self: ContractState, operator: ContractAddress, approved: bool) { - let mut unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721Impl::set_approval_for_all(ref unsafe_state, operator, approved) - } - - #[external(v0)] - fn transfer_from( - ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 - ) { - let mut unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721Impl::transfer_from(ref unsafe_state, from, to, token_id) - } - - #[external(v0)] - fn safe_transfer_from( - ref self: ContractState, - from: ContractAddress, - to: ContractAddress, - token_id: u256, - data: Span - ) { - let mut unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721Impl::safe_transfer_from(ref unsafe_state, from, to, token_id, data) - } -} From 7968708dc0266894337edbdeb048eaec10267f2e Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 16 Oct 2023 01:23:46 -0400 Subject: [PATCH 186/246] add erc721abi --- src/token/erc721/interface.cairo | 54 ++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/token/erc721/interface.cairo b/src/token/erc721/interface.cairo index 6fdc68272..f9436fdbc 100644 --- a/src/token/erc721/interface.cairo +++ b/src/token/erc721/interface.cairo @@ -87,3 +87,57 @@ trait IERC721ReceiverCamel { data: Span ) -> felt252; } + +// +// ERC721 ABI +// + +#[starknet::interface] +trait ERC721ABI { + // IERC721 + fn balance_of(self: @TState, account: ContractAddress) -> u256; + fn owner_of(self: @TState, token_id: u256) -> ContractAddress; + fn safe_transfer_from( + ref self: TState, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span + ); + fn transfer_from(ref self: TState, from: ContractAddress, to: ContractAddress, token_id: u256); + fn approve(ref self: TState, to: ContractAddress, token_id: u256); + fn set_approval_for_all(ref self: TState, operator: ContractAddress, approved: bool); + fn get_approved(self: @TState, token_id: u256) -> ContractAddress; + fn is_approved_for_all( + self: @TState, owner: ContractAddress, operator: ContractAddress + ) -> bool; + + // ISRC5 + fn supports_interface(self: @TState, interface_id: felt252) -> bool; + + // IERC721Metadata + fn name(self: @TState) -> felt252; + fn symbol(self: @TState) -> felt252; + fn token_uri(self: @TState, token_id: u256) -> felt252; + + // IERC721CamelOnly + fn balanceOf(self: @TState, account: ContractAddress) -> u256; + fn ownerOf(self: @TState, tokenId: u256) -> ContractAddress; + fn safeTransferFrom( + ref self: TState, + from: ContractAddress, + to: ContractAddress, + tokenId: u256, + data: Span + ); + fn transferFrom(ref self: TState, from: ContractAddress, to: ContractAddress, tokenId: u256); + fn setApprovalForAll(ref self: TState, operator: ContractAddress, approved: bool); + fn getApproved(self: @TState, tokenId: u256) -> ContractAddress; + fn isApprovedForAll(self: @TState, owner: ContractAddress, operator: ContractAddress) -> bool; + + // ISRC5Camel + fn supportsInterface(self: @TState, interfaceId: felt252) -> bool; + + // IERC721MetadataCamelOnly + fn tokenURI(self: @TState, tokenId: u256) -> felt252; +} From 4e09fb6d8530c713d7ec29a13d137552d6ec2eae Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 16 Oct 2023 01:24:06 -0400 Subject: [PATCH 187/246] migrate to component --- src/token/erc721/erc721.cairo | 192 ++++++++++++++++++---------------- 1 file changed, 100 insertions(+), 92 deletions(-) diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index baa9765cd..5e126d520 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -1,25 +1,19 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/erc721.cairo) -#[starknet::contract] +#[starknet::component] mod ERC721 { use openzeppelin::account; use openzeppelin::introspection::dual_src5::DualCaseSRC5; use openzeppelin::introspection::dual_src5::DualCaseSRC5Trait; - use openzeppelin::introspection::src5::SRC5 as src5_component; + use openzeppelin::introspection::src5::SRC5::InternalTrait as SRC5InternalTrait; + use openzeppelin::introspection::src5::SRC5; use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721Receiver; use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721ReceiverTrait; use openzeppelin::token::erc721::interface; use starknet::ContractAddress; use starknet::get_caller_address; - component!(path: src5_component, storage: src5, event: SRC5Event); - #[abi(embed_v0)] - impl SRC5Impl = src5_component::SRC5Impl; - #[abi(embed_v0)] - impl SRC5CamelImpl = src5_component::SRC5CamelImpl; - impl SRC5InternalImpl = src5_component::InternalImpl; - #[storage] struct Storage { ERC721_name: felt252, @@ -29,8 +23,6 @@ mod ERC721 { ERC721_token_approvals: LegacyMap, ERC721_operator_approvals: LegacyMap<(ContractAddress, ContractAddress), bool>, ERC721_token_uri: LegacyMap, - #[substorage(v0)] - src5: src5_component::Storage } #[event] @@ -39,7 +31,6 @@ mod ERC721 { Transfer: Transfer, Approval: Approval, ApprovalForAll: ApprovalForAll, - SRC5Event: src5_component::Event } #[derive(Drop, starknet::Event)] @@ -84,87 +75,56 @@ mod ERC721 { const SAFE_TRANSFER_FAILED: felt252 = 'ERC721: safe transfer failed'; } - #[constructor] - fn constructor( - ref self: ContractState, - name: felt252, - symbol: felt252, - recipient: ContractAddress, - token_id: u256 - ) { - self.initializer(name, symbol); - self._mint(recipient, token_id); - } - // // External // - #[external(v0)] - impl ERC721MetadataImpl of interface::IERC721Metadata { - fn name(self: @ContractState) -> felt252 { - self.ERC721_name.read() - } - - fn symbol(self: @ContractState) -> felt252 { - self.ERC721_symbol.read() - } - - fn token_uri(self: @ContractState, token_id: u256) -> felt252 { - assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); - self.ERC721_token_uri.read(token_id) - } - } - - #[external(v0)] - impl ERC721MetadataCamelOnlyImpl of interface::IERC721MetadataCamelOnly { - fn tokenURI(self: @ContractState, tokenId: u256) -> felt252 { - assert(self._exists(tokenId), Errors::INVALID_TOKEN_ID); - self.ERC721_token_uri.read(tokenId) - } - } - - #[external(v0)] - impl ERC721Impl of interface::IERC721 { - fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { + #[embeddable_as(ERC721Impl)] + impl ERC721< + TContractState, + +HasComponent, + +SRC5::HasComponent, + +Drop + > of interface::IERC721> { + fn balance_of(self: @ComponentState, account: ContractAddress) -> u256 { assert(!account.is_zero(), Errors::INVALID_ACCOUNT); self.ERC721_balances.read(account) } - fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress { + fn owner_of(self: @ComponentState, token_id: u256) -> ContractAddress { self._owner_of(token_id) } - fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { + fn get_approved(self: @ComponentState, token_id: u256) -> ContractAddress { assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); self.ERC721_token_approvals.read(token_id) } fn is_approved_for_all( - self: @ContractState, owner: ContractAddress, operator: ContractAddress + self: @ComponentState, owner: ContractAddress, operator: ContractAddress ) -> bool { self.ERC721_operator_approvals.read((owner, operator)) } - fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) { + fn approve(ref self: ComponentState, to: ContractAddress, token_id: u256) { let owner = self._owner_of(token_id); let caller = get_caller_address(); assert( - owner == caller || ERC721Impl::is_approved_for_all(@self, owner, caller), + owner == caller || self.is_approved_for_all(owner, caller), Errors::UNAUTHORIZED ); self._approve(to, token_id); } fn set_approval_for_all( - ref self: ContractState, operator: ContractAddress, approved: bool + ref self: ComponentState, operator: ContractAddress, approved: bool ) { self._set_approval_for_all(get_caller_address(), operator, approved) } fn transfer_from( - ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 + ref self: ComponentState, from: ContractAddress, to: ContractAddress, token_id: u256 ) { assert( self._is_approved_or_owner(get_caller_address(), token_id), Errors::UNAUTHORIZED @@ -173,7 +133,7 @@ mod ERC721 { } fn safe_transfer_from( - ref self: ContractState, + ref self: ComponentState, from: ContractAddress, to: ContractAddress, token_id: u256, @@ -186,44 +146,83 @@ mod ERC721 { } } - #[external(v0)] - impl ERC721CamelOnlyImpl of interface::IERC721CamelOnly { - fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 { - ERC721Impl::balance_of(self, account) + #[embeddable_as(ERC721MetadataImpl)] + impl ERC721Metadata< + TContractState, + +HasComponent, + +SRC5::HasComponent, + +Drop + > of interface::IERC721Metadata> { + fn name(self: @ComponentState) -> felt252 { + self.ERC721_name.read() + } + + fn symbol(self: @ComponentState) -> felt252 { + self.ERC721_symbol.read() + } + + fn token_uri(self: @ComponentState, token_id: u256) -> felt252 { + assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); + self.ERC721_token_uri.read(token_id) + } + } + + #[embeddable_as(ERC721CamelOnlyImpl)] + impl ERC721CamelOnly< + TContractState, + +HasComponent, + +SRC5::HasComponent, + +Drop + > of interface::IERC721CamelOnly> { + fn balanceOf(self: @ComponentState, account: ContractAddress) -> u256 { + self.balance_of(account) } - fn ownerOf(self: @ContractState, tokenId: u256) -> ContractAddress { - ERC721Impl::owner_of(self, tokenId) + fn ownerOf(self: @ComponentState, tokenId: u256) -> ContractAddress { + self.owner_of(tokenId) } - fn getApproved(self: @ContractState, tokenId: u256) -> ContractAddress { - ERC721Impl::get_approved(self, tokenId) + fn getApproved(self: @ComponentState, tokenId: u256) -> ContractAddress { + self.get_approved(tokenId) } fn isApprovedForAll( - self: @ContractState, owner: ContractAddress, operator: ContractAddress + self: @ComponentState, owner: ContractAddress, operator: ContractAddress ) -> bool { - ERC721Impl::is_approved_for_all(self, owner, operator) + self.is_approved_for_all(owner, operator) } - fn setApprovalForAll(ref self: ContractState, operator: ContractAddress, approved: bool) { - ERC721Impl::set_approval_for_all(ref self, operator, approved) + fn setApprovalForAll(ref self: ComponentState, operator: ContractAddress, approved: bool) { + self.set_approval_for_all(operator, approved) } fn transferFrom( - ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256 + ref self: ComponentState, from: ContractAddress, to: ContractAddress, tokenId: u256 ) { - ERC721Impl::transfer_from(ref self, from, to, tokenId) + self.transfer_from(from, to, tokenId) } fn safeTransferFrom( - ref self: ContractState, + ref self: ComponentState, from: ContractAddress, to: ContractAddress, tokenId: u256, data: Span ) { - ERC721Impl::safe_transfer_from(ref self, from, to, tokenId, data) + self.safe_transfer_from(from, to, tokenId, data) + } + } + + #[embeddable_as(ERC721MetadataCamelOnlyImpl)] + impl ERC721MetadataCamelOnly< + TContractState, + +HasComponent, + +SRC5::HasComponent, + +Drop + > of interface::IERC721MetadataCamelOnly> { + fn tokenURI(self: @ComponentState, tokenId: u256) -> felt252 { + assert(self._exists(tokenId), Errors::INVALID_TOKEN_ID); + self.ERC721_token_uri.read(tokenId) } } @@ -232,16 +231,25 @@ mod ERC721 { // #[generate_trait] - impl InternalImpl of InternalTrait { - fn initializer(ref self: ContractState, name: felt252, symbol: felt252) { + impl InternalImpl< + TContractState, + +HasComponent, + +SRC5::HasComponent, + +Drop + > of InternalTrait { + fn initializer(ref self: ComponentState, name: felt252, symbol: felt252) { self.ERC721_name.write(name); self.ERC721_symbol.write(symbol); - self.src5.register_interface(interface::IERC721_ID); - self.src5.register_interface(interface::IERC721_METADATA_ID); + let mut contract = self.get_contract_mut(); + let mut src5_component = SRC5::HasComponent::< + TContractState + >::get_component_mut(ref contract); + src5_component.register_interface(interface::IERC721_ID); + src5_component.register_interface(interface::IERC721_METADATA_ID); } - fn _owner_of(self: @ContractState, token_id: u256) -> ContractAddress { + fn _owner_of(self: @ComponentState, token_id: u256) -> ContractAddress { let owner = self.ERC721_owners.read(token_id); match owner.is_zero() { bool::False(()) => owner, @@ -249,21 +257,21 @@ mod ERC721 { } } - fn _exists(self: @ContractState, token_id: u256) -> bool { + fn _exists(self: @ComponentState, token_id: u256) -> bool { !self.ERC721_owners.read(token_id).is_zero() } fn _is_approved_or_owner( - self: @ContractState, spender: ContractAddress, token_id: u256 + self: @ComponentState, spender: ContractAddress, token_id: u256 ) -> bool { let owner = self._owner_of(token_id); - let is_approved_for_all = ERC721Impl::is_approved_for_all(self, owner, spender); + let is_approved_for_all = self.is_approved_for_all(owner, spender); owner == spender || is_approved_for_all - || spender == ERC721Impl::get_approved(self, token_id) + || spender == self.get_approved(token_id) } - fn _approve(ref self: ContractState, to: ContractAddress, token_id: u256) { + fn _approve(ref self: ComponentState, to: ContractAddress, token_id: u256) { let owner = self._owner_of(token_id); assert(owner != to, Errors::APPROVAL_TO_OWNER); @@ -272,7 +280,7 @@ mod ERC721 { } fn _set_approval_for_all( - ref self: ContractState, + ref self: ComponentState, owner: ContractAddress, operator: ContractAddress, approved: bool @@ -282,7 +290,7 @@ mod ERC721 { self.emit(ApprovalForAll { owner, operator, approved }); } - fn _mint(ref self: ContractState, to: ContractAddress, token_id: u256) { + fn _mint(ref self: ComponentState, to: ContractAddress, token_id: u256) { assert(!to.is_zero(), Errors::INVALID_RECEIVER); assert(!self._exists(token_id), Errors::ALREADY_MINTED); @@ -293,7 +301,7 @@ mod ERC721 { } fn _transfer( - ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 + ref self: ComponentState, from: ContractAddress, to: ContractAddress, token_id: u256 ) { assert(!to.is_zero(), Errors::INVALID_RECEIVER); let owner = self._owner_of(token_id); @@ -309,7 +317,7 @@ mod ERC721 { self.emit(Transfer { from, to, token_id }); } - fn _burn(ref self: ContractState, token_id: u256) { + fn _burn(ref self: ComponentState, token_id: u256) { let owner = self._owner_of(token_id); // Implicit clear approvals, no need to emit an event @@ -322,7 +330,7 @@ mod ERC721 { } fn _safe_mint( - ref self: ContractState, to: ContractAddress, token_id: u256, data: Span + ref self: ComponentState, to: ContractAddress, token_id: u256, data: Span ) { self._mint(to, token_id); assert( @@ -332,7 +340,7 @@ mod ERC721 { } fn _safe_transfer( - ref self: ContractState, + ref self: ComponentState, from: ContractAddress, to: ContractAddress, token_id: u256, @@ -344,7 +352,7 @@ mod ERC721 { ); } - fn _set_token_uri(ref self: ContractState, token_id: u256, token_uri: felt252) { + fn _set_token_uri(ref self: ComponentState, token_id: u256, token_uri: felt252) { assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); self.ERC721_token_uri.write(token_id, token_uri) } From 8da4c00b20cfe4d1a606509a1c329024066572f7 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 16 Oct 2023 01:25:59 -0400 Subject: [PATCH 188/246] use abi dispatcher --- src/tests/token/test_dual721.cairo | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/tests/token/test_dual721.cairo b/src/tests/token/test_dual721.cairo index 0beb985a4..265ad1429 100644 --- a/src/tests/token/test_dual721.cairo +++ b/src/tests/token/test_dual721.cairo @@ -1,9 +1,9 @@ -use openzeppelin::tests::mocks::camel721_mock::CamelERC721Mock; -use openzeppelin::tests::mocks::erc721_panic_mock::CamelERC721PanicMock; -use openzeppelin::tests::mocks::erc721_panic_mock::SnakeERC721PanicMock; +use openzeppelin::tests::mocks::erc721_mocks::{ + CamelERC721Mock, CamelERC721PanicMock, SnakeERC721PanicMock +}; use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; -use openzeppelin::tests::mocks::snake721_mock::SnakeERC721Mock; +use openzeppelin::tests::mocks::erc721_mocks::SnakeERC721Mock; use openzeppelin::tests::utils::constants::{ DATA, OWNER, RECIPIENT, SPENDER, OPERATOR, OTHER, NAME, SYMBOL, URI, TOKEN_ID }; @@ -12,6 +12,8 @@ use openzeppelin::token::erc721::dual721::DualCaseERC721; use openzeppelin::token::erc721::dual721::DualCaseERC721Trait; use openzeppelin::token::erc721::interface::IERC721CamelOnlyDispatcher; use openzeppelin::token::erc721::interface::IERC721CamelOnlyDispatcherTrait; +use openzeppelin::token::erc721::interface::ERC721ABIDispatcher; +use openzeppelin::token::erc721::interface::ERC721ABIDispatcherTrait; use openzeppelin::token::erc721::interface::IERC721Dispatcher; use openzeppelin::token::erc721::interface::IERC721DispatcherTrait; use openzeppelin::token::erc721::interface::IERC721_ID; From 5cc8a6d10a6a875a49d968b3116b0f5671581af7 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 16 Oct 2023 01:26:24 -0400 Subject: [PATCH 189/246] start updating tests --- src/tests/token/test_erc721.cairo | 2355 ++++++++++++++--------------- 1 file changed, 1168 insertions(+), 1187 deletions(-) diff --git a/src/tests/token/test_erc721.cairo b/src/tests/token/test_erc721.cairo index 9b9016e95..f9fdbec3f 100644 --- a/src/tests/token/test_erc721.cairo +++ b/src/tests/token/test_erc721.cairo @@ -1,11 +1,10 @@ -use ERC721::ERC721_owners::InternalContractMemberStateTrait as OwnersTrait; -use ERC721::ERC721_token_approvals::InternalContractMemberStateTrait as TokenApprovalsTrait; - use integer::u256; use integer::u256_from_felt252; use openzeppelin::account::Account; -use openzeppelin::introspection::src5; use openzeppelin::introspection; +use openzeppelin::introspection::src5; +use openzeppelin::introspection::src5::SRC5::SRC5Impl; +use openzeppelin::tests::mocks::erc721_mocks::DualCaseERC721Mock; use openzeppelin::tests::mocks::camel_account_mock::CamelAccountMock; use openzeppelin::tests::mocks::dual721_receiver_mocks::CamelERC721ReceiverMock; use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; @@ -16,27 +15,28 @@ use openzeppelin::tests::utils::constants::{ use openzeppelin::tests::utils; use openzeppelin::token::erc721::ERC721::{ Approval, ApprovalForAll, ERC721CamelOnlyImpl, ERC721Impl, ERC721MetadataCamelOnlyImpl, - ERC721MetadataImpl, InternalImpl, SRC5CamelImpl, SRC5Impl, Transfer, + ERC721MetadataImpl, InternalImpl, Transfer, }; use openzeppelin::token::erc721::ERC721; use openzeppelin::token::erc721; use openzeppelin::utils::serde::SerializedAppend; use starknet::ContractAddress; use starknet::contract_address_const; +use starknet::storage::StorageMapMemberAccessTrait; use starknet::testing; // // Setup // -fn STATE() -> ERC721::ContractState { - ERC721::contract_state_for_testing() +fn STATE() -> DualCaseERC721Mock::ContractState { + DualCaseERC721Mock::contract_state_for_testing() } -fn setup() -> ERC721::ContractState { +fn setup() -> DualCaseERC721Mock::ContractState { let mut state = STATE(); - InternalImpl::initializer(ref state, NAME, SYMBOL); - InternalImpl::_mint(ref state, OWNER(), TOKEN_ID); + state.erc721.initializer(NAME, SYMBOL); + state.erc721._mint(OWNER(), TOKEN_ID); utils::drop_event(ZERO()); state } @@ -63,50 +63,24 @@ fn setup_camel_account() -> ContractAddress { // Initializers // -#[test] -#[available_gas(20000000)] -fn test_constructor() { - let mut state = STATE(); - ERC721::constructor(ref state, NAME, SYMBOL, OWNER(), TOKEN_ID); - - assert(ERC721MetadataImpl::name(@state) == NAME, 'Name should be NAME'); - assert(ERC721MetadataImpl::symbol(@state) == SYMBOL, 'Symbol should be SYMBOL'); - assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance should be one'); - assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'OWNER should be owner'); - - assert( - SRC5Impl::supports_interface(@state, erc721::interface::IERC721_ID), 'Missing interface ID' - ); - assert( - SRC5Impl::supports_interface(@state, erc721::interface::IERC721_METADATA_ID), - 'missing interface ID' - ); - assert( - SRC5Impl::supports_interface(@state, introspection::interface::ISRC5_ID), - 'missing interface ID' - ); -} - #[test] #[available_gas(20000000)] fn test_initialize() { let mut state = STATE(); - InternalImpl::initializer(ref state, NAME, SYMBOL); + state.erc721.initializer(NAME, SYMBOL); - assert(ERC721MetadataImpl::name(@state) == NAME, 'Name should be NAME'); - assert(ERC721MetadataImpl::symbol(@state) == SYMBOL, 'Symbol should be SYMBOL'); - assert(ERC721Impl::balance_of(@state, OWNER()) == 0, 'Balance should be zero'); + assert(state.erc721.name() == NAME, 'Name should be NAME'); + assert(state.erc721.symbol() == SYMBOL, 'Symbol should be SYMBOL'); + assert(state.erc721.balance_of(OWNER()) == 0, 'Balance should be zero'); assert( - SRC5Impl::supports_interface(@state, erc721::interface::IERC721_ID), 'Missing interface ID' + state.src5.supports_interface(erc721::interface::IERC721_ID), 'Missing interface ID' ); assert( - SRC5Impl::supports_interface(@state, erc721::interface::IERC721_METADATA_ID), - 'missing interface ID' + state.src5.supports_interface(erc721::interface::IERC721_METADATA_ID), 'Missing interface ID' ); assert( - SRC5Impl::supports_interface(@state, introspection::interface::ISRC5_ID), - 'missing interface ID' + state.src5.supports_interface(introspection::interface::ISRC5_ID), 'Missing interface ID' ); } @@ -118,35 +92,38 @@ fn test_initialize() { #[available_gas(20000000)] fn test_balance_of() { let state = setup(); - assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Should return balance'); + assert(state.erc721.balance_of(OWNER()) == 1, 'Should return balance'); } #[test] #[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid account',))] fn test_balance_of_zero() { - ERC721Impl::balance_of(@STATE(), ZERO()); + let state = setup(); + state.erc721.balance_of(ZERO()); } #[test] #[available_gas(20000000)] fn test_owner_of() { let state = setup(); - assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Should return owner'); + assert(state.erc721.owner_of(TOKEN_ID) == OWNER(), 'Should return owner'); } #[test] #[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid token ID',))] fn test_owner_of_non_minted() { - ERC721Impl::owner_of(@STATE(), u256_from_felt252(7)); + let state = setup(); + state.erc721.owner_of(u256_from_felt252(7)); } #[test] #[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid token ID',))] fn test_token_uri_non_minted() { - ERC721MetadataImpl::token_uri(@STATE(), u256_from_felt252(7)); + let state = setup(); + state.erc721.token_uri(u256_from_felt252(7)); } #[test] @@ -156,16 +133,17 @@ fn test_get_approved() { let spender = SPENDER(); let token_id = TOKEN_ID; - assert(ERC721Impl::get_approved(@state, token_id) == ZERO(), 'Should return non-approval'); - InternalImpl::_approve(ref state, spender, token_id); - assert(ERC721Impl::get_approved(@state, token_id) == spender, 'Should return approval'); + assert(state.erc721.get_approved(token_id) == ZERO(), 'Should return non-approval'); + state.erc721._approve(spender, token_id); + assert(state.erc721.get_approved(token_id) == spender, 'Should return approval'); } #[test] #[available_gas(20000000)] #[should_panic(expected: ('ERC721: invalid token ID',))] fn test_get_approved_nonexistent() { - ERC721Impl::get_approved(@STATE(), u256_from_felt252(7)); + let state = setup(); + state.erc721.get_approved(u256_from_felt252(7)); } #[test] @@ -175,18 +153,21 @@ fn test__exists() { let zero = ZERO(); let token_id = TOKEN_ID; - assert(!InternalImpl::_exists(@state, token_id), 'Token should not exist'); - assert(state.ERC721_owners.read(token_id) == zero, 'Invalid owner'); + assert(!state.erc721._exists(token_id), 'Token should not exist'); + let mut owner = state.erc721.ERC721_owners.read(token_id); + assert(owner == zero, ''); - InternalImpl::_mint(ref state, RECIPIENT(), token_id); + state.erc721._mint(RECIPIENT(), token_id); - assert(InternalImpl::_exists(@state, token_id), 'Token should exist'); - assert(state.ERC721_owners.read(token_id) == RECIPIENT(), 'Invalid owner'); + assert(state.erc721._exists(token_id), 'Token should exist'); + owner = state.erc721.ERC721_owners.read(token_id); + assert(owner == RECIPIENT(), ''); - InternalImpl::_burn(ref state, token_id); + state.erc721._burn(token_id); - assert(!InternalImpl::_exists(@state, token_id), 'Token should not exist'); - assert(state.ERC721_owners.read(token_id) == zero, 'Invalid owner'); + assert(!state.erc721._exists(token_id), 'Token should not exist'); + owner = state.erc721.ERC721_owners.read(token_id); + assert(owner == zero, ''); } // @@ -199,1155 +180,1155 @@ fn test_approve_from_owner() { let mut state = setup(); testing::set_caller_address(OWNER()); - ERC721Impl::approve(ref state, SPENDER(), TOKEN_ID); - assert_event_approval(OWNER(), SPENDER(), TOKEN_ID); - - assert( - ERC721Impl::get_approved(@state, TOKEN_ID) == SPENDER(), 'Spender not approved correctly' - ); -} - -#[test] -#[available_gas(20000000)] -fn test_approve_from_operator() { - let mut state = setup(); - - testing::set_caller_address(OWNER()); - ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); - utils::drop_event(ZERO()); - - testing::set_caller_address(OPERATOR()); - ERC721Impl::approve(ref state, SPENDER(), TOKEN_ID); - assert_event_approval(OWNER(), SPENDER(), TOKEN_ID); - - assert( - ERC721Impl::get_approved(@state, TOKEN_ID) == SPENDER(), 'Spender not approved correctly' - ); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: unauthorized caller',))] -fn test_approve_from_unauthorized() { - let mut state = setup(); - - testing::set_caller_address(OTHER()); - ERC721Impl::approve(ref state, SPENDER(), TOKEN_ID); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: approval to owner',))] -fn test_approve_to_owner() { - let mut state = setup(); - - testing::set_caller_address(OWNER()); - ERC721Impl::approve(ref state, OWNER(), TOKEN_ID); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID',))] -fn test_approve_nonexistent() { - let mut state = STATE(); - ERC721Impl::approve(ref state, SPENDER(), TOKEN_ID); -} - -#[test] -#[available_gas(20000000)] -fn test__approve() { - let mut state = setup(); - InternalImpl::_approve(ref state, SPENDER(), TOKEN_ID); + state.erc721.approve(SPENDER(), TOKEN_ID); assert_event_approval(OWNER(), SPENDER(), TOKEN_ID); assert( - ERC721Impl::get_approved(@state, TOKEN_ID) == SPENDER(), 'Spender not approved correctly' + state.erc721.get_approved(TOKEN_ID) == SPENDER(), 'Spender not approved correctly' ); } -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: approval to owner',))] -fn test__approve_to_owner() { - let mut state = setup(); - InternalImpl::_approve(ref state, OWNER(), TOKEN_ID); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID',))] -fn test__approve_nonexistent() { - let mut state = STATE(); - InternalImpl::_approve(ref state, SPENDER(), TOKEN_ID); -} - +//#[test] +//#[available_gas(20000000)] +//fn test_approve_from_operator() { +// let mut state = setup(); // -// set_approval_for_all & _set_approval_for_all +// testing::set_caller_address(OWNER()); +// ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); +// utils::drop_event(ZERO()); // - -#[test] -#[available_gas(20000000)] -fn test_set_approval_for_all() { - let mut state = STATE(); - testing::set_caller_address(OWNER()); - - assert(!ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), 'Invalid default value'); - - ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); - assert_event_approval_for_all(OWNER(), OPERATOR(), true); - - assert( - ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), - 'Operator not approved correctly' - ); - - ERC721Impl::set_approval_for_all(ref state, OPERATOR(), false); - assert_event_approval_for_all(OWNER(), OPERATOR(), false); - - assert( - !ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), - 'Approval not revoked correctly' - ); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: self approval',))] -fn test_set_approval_for_all_owner_equal_operator_true() { - let mut state = STATE(); - testing::set_caller_address(OWNER()); - ERC721Impl::set_approval_for_all(ref state, OWNER(), true); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: self approval',))] -fn test_set_approval_for_all_owner_equal_operator_false() { - let mut state = STATE(); - testing::set_caller_address(OWNER()); - ERC721Impl::set_approval_for_all(ref state, OWNER(), false); -} - -#[test] -#[available_gas(20000000)] -fn test__set_approval_for_all() { - let mut state = STATE(); - assert(!ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), 'Invalid default value'); - - InternalImpl::_set_approval_for_all(ref state, OWNER(), OPERATOR(), true); - assert_event_approval_for_all(OWNER(), OPERATOR(), true); - - assert( - ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), - 'Operator not approved correctly' - ); - - InternalImpl::_set_approval_for_all(ref state, OWNER(), OPERATOR(), false); - assert_event_approval_for_all(OWNER(), OPERATOR(), false); - - assert( - !ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), - 'Operator not approved correctly' - ); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: self approval',))] -fn test__set_approval_for_all_owner_equal_operator_true() { - let mut state = STATE(); - InternalImpl::_set_approval_for_all(ref state, OWNER(), OWNER(), true); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: self approval',))] -fn test__set_approval_for_all_owner_equal_operator_false() { - let mut state = STATE(); - InternalImpl::_set_approval_for_all(ref state, OWNER(), OWNER(), false); -} - +// testing::set_caller_address(OPERATOR()); +// ERC721Impl::approve(ref state, SPENDER(), TOKEN_ID); +// assert_event_approval(OWNER(), SPENDER(), TOKEN_ID); // -// transfer_from & transferFrom +// assert( +// ERC721Impl::get_approved(@state, TOKEN_ID) == SPENDER(), 'Spender not approved correctly' +// ); +//} // - -#[test] -#[available_gas(20000000)] -fn test_transfer_from_owner() { - let mut state = setup(); - let token_id = TOKEN_ID; - let owner = OWNER(); - let recipient = RECIPIENT(); - // set approval to check reset - InternalImpl::_approve(ref state, OTHER(), token_id); - utils::drop_event(ZERO()); - - assert_state_before_transfer(owner, recipient, token_id); - assert(ERC721Impl::get_approved(@state, token_id) == OTHER(), 'Approval not implicitly reset'); - - testing::set_caller_address(owner); - ERC721Impl::transfer_from(ref state, owner, recipient, token_id); - assert_event_transfer(owner, recipient, token_id); - - assert_state_after_transfer(owner, recipient, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test_transferFrom_owner() { - let mut state = setup(); - let token_id = TOKEN_ID; - let owner = OWNER(); - let recipient = RECIPIENT(); - // set approval to check reset - InternalImpl::_approve(ref state, OTHER(), token_id); - utils::drop_event(ZERO()); - - assert_state_before_transfer(owner, recipient, token_id); - assert(ERC721Impl::get_approved(@state, token_id) == OTHER(), 'Approval not implicitly reset'); - - testing::set_caller_address(owner); - ERC721CamelOnlyImpl::transferFrom(ref state, owner, recipient, token_id); - assert_event_transfer(owner, recipient, token_id); - - assert_state_after_transfer(owner, recipient, token_id); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID',))] -fn test_transfer_from_nonexistent() { - let mut state = STATE(); - ERC721Impl::transfer_from(ref state, ZERO(), RECIPIENT(), TOKEN_ID); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID',))] -fn test_transferFrom_nonexistent() { - let mut state = STATE(); - ERC721CamelOnlyImpl::transferFrom(ref state, ZERO(), RECIPIENT(), TOKEN_ID); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid receiver',))] -fn test_transfer_from_to_zero() { - let mut state = setup(); - testing::set_caller_address(OWNER()); - ERC721Impl::transfer_from(ref state, OWNER(), ZERO(), TOKEN_ID); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid receiver',))] -fn test_transferFrom_to_zero() { - let mut state = setup(); - - testing::set_caller_address(OWNER()); - ERC721CamelOnlyImpl::transferFrom(ref state, OWNER(), ZERO(), TOKEN_ID); -} - -#[test] -#[available_gas(20000000)] -fn test_transfer_from_to_owner() { - let mut state = setup(); - - assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership before'); - assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner before'); - - testing::set_caller_address(OWNER()); - ERC721Impl::transfer_from(ref state, OWNER(), OWNER(), TOKEN_ID); - assert_event_transfer(OWNER(), OWNER(), TOKEN_ID); - - assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership after'); - assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner after'); -} - -#[test] -#[available_gas(20000000)] -fn test_transferFrom_to_owner() { - let mut state = setup(); - - assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership before'); - assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner before'); - - testing::set_caller_address(OWNER()); - ERC721CamelOnlyImpl::transferFrom(ref state, OWNER(), OWNER(), TOKEN_ID); - assert_event_transfer(OWNER(), OWNER(), TOKEN_ID); - - assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership after'); - assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner after'); -} - -#[test] -#[available_gas(20000000)] -fn test_transfer_from_approved() { - let mut state = setup(); - let token_id = TOKEN_ID; - let owner = OWNER(); - let recipient = RECIPIENT(); - assert_state_before_transfer(owner, recipient, token_id); - - testing::set_caller_address(owner); - ERC721Impl::approve(ref state, OPERATOR(), token_id); - utils::drop_event(ZERO()); - - testing::set_caller_address(OPERATOR()); - ERC721Impl::transfer_from(ref state, owner, recipient, token_id); - assert_event_transfer(owner, recipient, token_id); - - assert_state_after_transfer(owner, recipient, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test_transferFrom_approved() { - let mut state = setup(); - let token_id = TOKEN_ID; - let owner = OWNER(); - let recipient = RECIPIENT(); - assert_state_before_transfer(owner, recipient, token_id); - - testing::set_caller_address(owner); - ERC721Impl::approve(ref state, OPERATOR(), token_id); - utils::drop_event(ZERO()); - - testing::set_caller_address(OPERATOR()); - ERC721CamelOnlyImpl::transferFrom(ref state, owner, recipient, token_id); - assert_event_transfer(owner, recipient, token_id); - - assert_state_after_transfer(owner, recipient, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test_transfer_from_approved_for_all() { - let mut state = setup(); - let token_id = TOKEN_ID; - let owner = OWNER(); - let recipient = RECIPIENT(); - - assert_state_before_transfer(owner, recipient, token_id); - - testing::set_caller_address(owner); - ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); - utils::drop_event(ZERO()); - - testing::set_caller_address(OPERATOR()); - ERC721Impl::transfer_from(ref state, owner, recipient, token_id); - assert_event_transfer(owner, recipient, token_id); - - assert_state_after_transfer(owner, recipient, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test_transferFrom_approved_for_all() { - let mut state = setup(); - let token_id = TOKEN_ID; - let owner = OWNER(); - let recipient = RECIPIENT(); - - assert_state_before_transfer(owner, recipient, token_id); - - testing::set_caller_address(owner); - ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); - utils::drop_event(ZERO()); - - testing::set_caller_address(OPERATOR()); - ERC721CamelOnlyImpl::transferFrom(ref state, owner, recipient, token_id); - assert_event_transfer(owner, recipient, token_id); - - assert_state_after_transfer(owner, recipient, token_id); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: unauthorized caller',))] -fn test_transfer_from_unauthorized() { - let mut state = setup(); - testing::set_caller_address(OTHER()); - ERC721Impl::transfer_from(ref state, OWNER(), RECIPIENT(), TOKEN_ID); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: unauthorized caller',))] -fn test_transferFrom_unauthorized() { - let mut state = setup(); - testing::set_caller_address(OTHER()); - ERC721CamelOnlyImpl::transferFrom(ref state, OWNER(), RECIPIENT(), TOKEN_ID); -} - +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: unauthorized caller',))] +//fn test_approve_from_unauthorized() { +// let mut state = setup(); // -// safe_transfer_from & safeTransferFrom +// testing::set_caller_address(OTHER()); +// ERC721Impl::approve(ref state, SPENDER(), TOKEN_ID); +//} // - -#[test] -#[available_gas(20000000)] -fn test_safe_transfer_from_to_account() { - let mut state = setup(); - let account = setup_account(); - let token_id = TOKEN_ID; - let owner = OWNER(); - - assert_state_before_transfer(owner, account, token_id); - - testing::set_caller_address(owner); - ERC721Impl::safe_transfer_from(ref state, owner, account, token_id, DATA(true)); - assert_event_transfer(owner, account, token_id); - - assert_state_after_transfer(owner, account, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test_safeTransferFrom_to_account() { - let mut state = setup(); - let account = setup_account(); - let token_id = TOKEN_ID; - let owner = OWNER(); - - assert_state_before_transfer(owner, account, token_id); - - testing::set_caller_address(owner); - ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, account, token_id, DATA(true)); - assert_event_transfer(owner, account, token_id); - - assert_state_after_transfer(owner, account, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test_safe_transfer_from_to_account_camel() { - let mut state = setup(); - let account = setup_camel_account(); - let token_id = TOKEN_ID; - let owner = OWNER(); - - assert_state_before_transfer(owner, account, token_id); - - testing::set_caller_address(owner); - ERC721Impl::safe_transfer_from(ref state, owner, account, token_id, DATA(true)); - assert_event_transfer(owner, account, token_id); - - assert_state_after_transfer(owner, account, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test_safeTransferFrom_to_account_camel() { - let mut state = setup(); - let account = setup_camel_account(); - let token_id = TOKEN_ID; - let owner = OWNER(); - - assert_state_before_transfer(owner, account, token_id); - - testing::set_caller_address(owner); - ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, account, token_id, DATA(true)); - assert_event_transfer(owner, account, token_id); - - assert_state_after_transfer(owner, account, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test_safe_transfer_from_to_receiver() { - let mut state = setup(); - let receiver = setup_receiver(); - let token_id = TOKEN_ID; - let owner = OWNER(); - - assert_state_before_transfer(owner, receiver, token_id); - - testing::set_caller_address(owner); - ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); - assert_event_transfer(owner, receiver, token_id); - - assert_state_after_transfer(owner, receiver, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test_safeTransferFrom_to_receiver() { - let mut state = setup(); - let receiver = setup_receiver(); - let token_id = TOKEN_ID; - let owner = OWNER(); - - assert_state_before_transfer(owner, receiver, token_id); - - testing::set_caller_address(owner); - ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); - assert_event_transfer(owner, receiver, token_id); - - assert_state_after_transfer(owner, receiver, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test_safe_transfer_from_to_receiver_camel() { - let mut state = setup(); - let receiver = setup_camel_receiver(); - let token_id = TOKEN_ID; - let owner = OWNER(); - - assert_state_before_transfer(owner, receiver, token_id); - - testing::set_caller_address(owner); - ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); - assert_event_transfer(owner, receiver, token_id); - - assert_state_after_transfer(owner, receiver, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test_safeTransferFrom_to_receiver_camel() { - let mut state = setup(); - let receiver = setup_camel_receiver(); - let token_id = TOKEN_ID; - let owner = OWNER(); - - assert_state_before_transfer(owner, receiver, token_id); - - testing::set_caller_address(owner); - ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); - assert_event_transfer(owner, receiver, token_id); - - assert_state_after_transfer(owner, receiver, token_id); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: safe transfer failed',))] -fn test_safe_transfer_from_to_receiver_failure() { - let mut state = setup(); - let receiver = setup_receiver(); - let token_id = TOKEN_ID; - let owner = OWNER(); - - testing::set_caller_address(owner); - ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(false)); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: safe transfer failed',))] -fn test_safeTransferFrom_to_receiver_failure() { - let mut state = setup(); - let receiver = setup_receiver(); - let token_id = TOKEN_ID; - let owner = OWNER(); - - testing::set_caller_address(owner); - ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(false)); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: safe transfer failed',))] -fn test_safe_transfer_from_to_receiver_failure_camel() { - let mut state = setup(); - let receiver = setup_camel_receiver(); - let token_id = TOKEN_ID; - let owner = OWNER(); - - testing::set_caller_address(owner); - ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(false)); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: safe transfer failed',))] -fn test_safeTransferFrom_to_receiver_failure_camel() { - let mut state = setup(); - let receiver = setup_camel_receiver(); - let token_id = TOKEN_ID; - let owner = OWNER(); - - testing::set_caller_address(owner); - ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(false)); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] -fn test_safe_transfer_from_to_non_receiver() { - let mut state = setup(); - let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); - let token_id = TOKEN_ID; - let owner = OWNER(); - - testing::set_caller_address(owner); - ERC721Impl::safe_transfer_from(ref state, owner, recipient, token_id, DATA(true)); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] -fn test_safeTransferFrom_to_non_receiver() { - let mut state = setup(); - let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); - let token_id = TOKEN_ID; - let owner = OWNER(); - - testing::set_caller_address(owner); - ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, recipient, token_id, DATA(true)); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID',))] -fn test_safe_transfer_from_nonexistent() { - let mut state = STATE(); - ERC721Impl::safe_transfer_from(ref state, ZERO(), RECIPIENT(), TOKEN_ID, DATA(true)); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID',))] -fn test_safeTransferFrom_nonexistent() { - let mut state = STATE(); - ERC721CamelOnlyImpl::safeTransferFrom(ref state, ZERO(), RECIPIENT(), TOKEN_ID, DATA(true)); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid receiver',))] -fn test_safe_transfer_from_to_zero() { - let mut state = setup(); - testing::set_caller_address(OWNER()); - ERC721Impl::safe_transfer_from(ref state, OWNER(), ZERO(), TOKEN_ID, DATA(true)); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid receiver',))] -fn test_safeTransferFrom_to_zero() { - let mut state = setup(); - testing::set_caller_address(OWNER()); - ERC721CamelOnlyImpl::safeTransferFrom(ref state, OWNER(), ZERO(), TOKEN_ID, DATA(true)); -} - -#[test] -#[available_gas(20000000)] -fn test_safe_transfer_from_to_owner() { - let mut state = STATE(); - let token_id = TOKEN_ID; - let owner = setup_receiver(); - InternalImpl::initializer(ref state, NAME, SYMBOL); - InternalImpl::_mint(ref state, owner, token_id); - utils::drop_event(ZERO()); - - assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); - assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); - - testing::set_caller_address(owner); - ERC721Impl::safe_transfer_from(ref state, owner, owner, token_id, DATA(true)); - assert_event_transfer(owner, owner, token_id); - - assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership after'); - assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner after'); -} - -#[test] -#[available_gas(20000000)] -fn test_safeTransferFrom_to_owner() { - let mut state = STATE(); - let token_id = TOKEN_ID; - let owner = setup_receiver(); - InternalImpl::initializer(ref state, NAME, SYMBOL); - InternalImpl::_mint(ref state, owner, token_id); - utils::drop_event(ZERO()); - - assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); - assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); - - testing::set_caller_address(owner); - ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, owner, token_id, DATA(true)); - assert_event_transfer(owner, owner, token_id); - - assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership after'); - assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner after'); -} - -#[test] -#[available_gas(20000000)] -fn test_safe_transfer_from_to_owner_camel() { - let mut state = STATE(); - let token_id = TOKEN_ID; - let owner = setup_camel_receiver(); - InternalImpl::initializer(ref state, NAME, SYMBOL); - InternalImpl::_mint(ref state, owner, token_id); - utils::drop_event(ZERO()); - - assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); - assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); - - testing::set_caller_address(owner); - ERC721Impl::safe_transfer_from(ref state, owner, owner, token_id, DATA(true)); - assert_event_transfer(owner, owner, token_id); - - assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership after'); - assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner after'); -} - -#[test] -#[available_gas(20000000)] -fn test_safeTransferFrom_to_owner_camel() { - let mut state = STATE(); - let token_id = TOKEN_ID; - let owner = setup_camel_receiver(); - InternalImpl::initializer(ref state, NAME, SYMBOL); - InternalImpl::_mint(ref state, owner, token_id); - utils::drop_event(ZERO()); - - assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); - assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); - - testing::set_caller_address(owner); - ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, owner, token_id, DATA(true)); - assert_event_transfer(owner, owner, token_id); - - assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership after'); - assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner after'); -} - -#[test] -#[available_gas(20000000)] -fn test_safe_transfer_from_approved() { - let mut state = setup(); - let receiver = setup_receiver(); - let token_id = TOKEN_ID; - let owner = OWNER(); - - assert_state_before_transfer(owner, receiver, token_id); - - testing::set_caller_address(owner); - ERC721Impl::approve(ref state, OPERATOR(), token_id); - utils::drop_event(ZERO()); - - testing::set_caller_address(OPERATOR()); - ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); - assert_event_transfer(owner, receiver, token_id); - - assert_state_after_transfer(owner, receiver, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test_safeTransferFrom_approved() { - let mut state = setup(); - let receiver = setup_receiver(); - let token_id = TOKEN_ID; - let owner = OWNER(); - - assert_state_before_transfer(owner, receiver, token_id); - - testing::set_caller_address(owner); - ERC721Impl::approve(ref state, OPERATOR(), token_id); - utils::drop_event(ZERO()); - - testing::set_caller_address(OPERATOR()); - ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); - assert_event_transfer(owner, receiver, token_id); - - assert_state_after_transfer(owner, receiver, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test_safe_transfer_from_approved_camel() { - let mut state = setup(); - let receiver = setup_camel_receiver(); - let token_id = TOKEN_ID; - let owner = OWNER(); - - assert_state_before_transfer(owner, receiver, token_id); - - testing::set_caller_address(owner); - ERC721Impl::approve(ref state, OPERATOR(), token_id); - utils::drop_event(ZERO()); - - testing::set_caller_address(OPERATOR()); - ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); - assert_event_transfer(owner, receiver, token_id); - - assert_state_after_transfer(owner, receiver, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test_safeTransferFrom_approved_camel() { - let mut state = setup(); - let receiver = setup_camel_receiver(); - let token_id = TOKEN_ID; - let owner = OWNER(); - - assert_state_before_transfer(owner, receiver, token_id); - - testing::set_caller_address(owner); - ERC721Impl::approve(ref state, OPERATOR(), token_id); - utils::drop_event(ZERO()); - - testing::set_caller_address(OPERATOR()); - ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); - assert_event_transfer(owner, receiver, token_id); - - assert_state_after_transfer(owner, receiver, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test_safe_transfer_from_approved_for_all() { - let mut state = setup(); - let receiver = setup_receiver(); - let token_id = TOKEN_ID; - let owner = OWNER(); - - assert_state_before_transfer(owner, receiver, token_id); - - testing::set_caller_address(owner); - ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); - utils::drop_event(ZERO()); - - testing::set_caller_address(OPERATOR()); - ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); - assert_event_transfer(owner, receiver, token_id); - - assert_state_after_transfer(owner, receiver, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test_safeTransferFrom_approved_for_all() { - let mut state = setup(); - let receiver = setup_receiver(); - let token_id = TOKEN_ID; - let owner = OWNER(); - - assert_state_before_transfer(owner, receiver, token_id); - - testing::set_caller_address(owner); - ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); - utils::drop_event(ZERO()); - - testing::set_caller_address(OPERATOR()); - ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); - assert_event_transfer(owner, receiver, token_id); - - assert_state_after_transfer(owner, receiver, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test_safe_transfer_from_approved_for_all_camel() { - let mut state = setup(); - let receiver = setup_camel_receiver(); - let token_id = TOKEN_ID; - let owner = OWNER(); - - assert_state_before_transfer(owner, receiver, token_id); - - testing::set_caller_address(owner); - ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); - utils::drop_event(ZERO()); - - testing::set_caller_address(OPERATOR()); - ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); - assert_event_transfer(owner, receiver, token_id); - - assert_state_after_transfer(owner, receiver, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test_safeTransferFrom_approved_for_all_camel() { - let mut state = setup(); - let receiver = setup_camel_receiver(); - let token_id = TOKEN_ID; - let owner = OWNER(); - - assert_state_before_transfer(owner, receiver, token_id); - - testing::set_caller_address(owner); - ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); - utils::drop_event(ZERO()); - - testing::set_caller_address(OPERATOR()); - ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); - assert_event_transfer(owner, receiver, token_id); - - assert_state_after_transfer(owner, receiver, token_id); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: unauthorized caller',))] -fn test_safe_transfer_from_unauthorized() { - let mut state = setup(); - testing::set_caller_address(OTHER()); - ERC721Impl::safe_transfer_from(ref state, OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: unauthorized caller',))] -fn test_safeTransferFrom_unauthorized() { - let mut state = setup(); - testing::set_caller_address(OTHER()); - ERC721CamelOnlyImpl::safeTransferFrom(ref state, OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); -} - +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: approval to owner',))] +//fn test_approve_to_owner() { +// let mut state = setup(); +// +// testing::set_caller_address(OWNER()); +// ERC721Impl::approve(ref state, OWNER(), TOKEN_ID); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: invalid token ID',))] +//fn test_approve_nonexistent() { +// let mut state = STATE(); +// ERC721Impl::approve(ref state, SPENDER(), TOKEN_ID); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test__approve() { +// let mut state = setup(); +// InternalImpl::_approve(ref state, SPENDER(), TOKEN_ID); +// assert_event_approval(OWNER(), SPENDER(), TOKEN_ID); +// +// assert( +// ERC721Impl::get_approved(@state, TOKEN_ID) == SPENDER(), 'Spender not approved correctly' +// ); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: approval to owner',))] +//fn test__approve_to_owner() { +// let mut state = setup(); +// InternalImpl::_approve(ref state, OWNER(), TOKEN_ID); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: invalid token ID',))] +//fn test__approve_nonexistent() { +// let mut state = STATE(); +// InternalImpl::_approve(ref state, SPENDER(), TOKEN_ID); +//} +// +//// +//// set_approval_for_all & _set_approval_for_all +//// +// +//#[test] +//#[available_gas(20000000)] +//fn test_set_approval_for_all() { +// let mut state = STATE(); +// testing::set_caller_address(OWNER()); +// +// assert(!ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), 'Invalid default value'); +// +// ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); +// assert_event_approval_for_all(OWNER(), OPERATOR(), true); +// +// assert( +// ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), +// 'Operator not approved correctly' +// ); +// +// ERC721Impl::set_approval_for_all(ref state, OPERATOR(), false); +// assert_event_approval_for_all(OWNER(), OPERATOR(), false); +// +// assert( +// !ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), +// 'Approval not revoked correctly' +// ); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: self approval',))] +//fn test_set_approval_for_all_owner_equal_operator_true() { +// let mut state = STATE(); +// testing::set_caller_address(OWNER()); +// ERC721Impl::set_approval_for_all(ref state, OWNER(), true); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: self approval',))] +//fn test_set_approval_for_all_owner_equal_operator_false() { +// let mut state = STATE(); +// testing::set_caller_address(OWNER()); +// ERC721Impl::set_approval_for_all(ref state, OWNER(), false); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test__set_approval_for_all() { +// let mut state = STATE(); +// assert(!ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), 'Invalid default value'); +// +// InternalImpl::_set_approval_for_all(ref state, OWNER(), OPERATOR(), true); +// assert_event_approval_for_all(OWNER(), OPERATOR(), true); +// +// assert( +// ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), +// 'Operator not approved correctly' +// ); +// +// InternalImpl::_set_approval_for_all(ref state, OWNER(), OPERATOR(), false); +// assert_event_approval_for_all(OWNER(), OPERATOR(), false); +// +// assert( +// !ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), +// 'Operator not approved correctly' +// ); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: self approval',))] +//fn test__set_approval_for_all_owner_equal_operator_true() { +// let mut state = STATE(); +// InternalImpl::_set_approval_for_all(ref state, OWNER(), OWNER(), true); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: self approval',))] +//fn test__set_approval_for_all_owner_equal_operator_false() { +// let mut state = STATE(); +// InternalImpl::_set_approval_for_all(ref state, OWNER(), OWNER(), false); +//} +// +//// +//// transfer_from & transferFrom +//// +// +//#[test] +//#[available_gas(20000000)] +//fn test_transfer_from_owner() { +// let mut state = setup(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// let recipient = RECIPIENT(); +// // set approval to check reset +// InternalImpl::_approve(ref state, OTHER(), token_id); +// utils::drop_event(ZERO()); +// +// assert_state_before_transfer(owner, recipient, token_id); +// assert(ERC721Impl::get_approved(@state, token_id) == OTHER(), 'Approval not implicitly reset'); +// +// testing::set_caller_address(owner); +// ERC721Impl::transfer_from(ref state, owner, recipient, token_id); +// assert_event_transfer(owner, recipient, token_id); +// +// assert_state_after_transfer(owner, recipient, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_transferFrom_owner() { +// let mut state = setup(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// let recipient = RECIPIENT(); +// // set approval to check reset +// InternalImpl::_approve(ref state, OTHER(), token_id); +// utils::drop_event(ZERO()); +// +// assert_state_before_transfer(owner, recipient, token_id); +// assert(ERC721Impl::get_approved(@state, token_id) == OTHER(), 'Approval not implicitly reset'); +// +// testing::set_caller_address(owner); +// ERC721CamelOnlyImpl::transferFrom(ref state, owner, recipient, token_id); +// assert_event_transfer(owner, recipient, token_id); +// +// assert_state_after_transfer(owner, recipient, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: invalid token ID',))] +//fn test_transfer_from_nonexistent() { +// let mut state = STATE(); +// ERC721Impl::transfer_from(ref state, ZERO(), RECIPIENT(), TOKEN_ID); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: invalid token ID',))] +//fn test_transferFrom_nonexistent() { +// let mut state = STATE(); +// ERC721CamelOnlyImpl::transferFrom(ref state, ZERO(), RECIPIENT(), TOKEN_ID); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: invalid receiver',))] +//fn test_transfer_from_to_zero() { +// let mut state = setup(); +// testing::set_caller_address(OWNER()); +// ERC721Impl::transfer_from(ref state, OWNER(), ZERO(), TOKEN_ID); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: invalid receiver',))] +//fn test_transferFrom_to_zero() { +// let mut state = setup(); +// +// testing::set_caller_address(OWNER()); +// ERC721CamelOnlyImpl::transferFrom(ref state, OWNER(), ZERO(), TOKEN_ID); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_transfer_from_to_owner() { +// let mut state = setup(); +// +// assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership before'); +// assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner before'); +// +// testing::set_caller_address(OWNER()); +// ERC721Impl::transfer_from(ref state, OWNER(), OWNER(), TOKEN_ID); +// assert_event_transfer(OWNER(), OWNER(), TOKEN_ID); +// +// assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership after'); +// assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner after'); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_transferFrom_to_owner() { +// let mut state = setup(); +// +// assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership before'); +// assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner before'); +// +// testing::set_caller_address(OWNER()); +// ERC721CamelOnlyImpl::transferFrom(ref state, OWNER(), OWNER(), TOKEN_ID); +// assert_event_transfer(OWNER(), OWNER(), TOKEN_ID); +// +// assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership after'); +// assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner after'); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_transfer_from_approved() { +// let mut state = setup(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// let recipient = RECIPIENT(); +// assert_state_before_transfer(owner, recipient, token_id); +// +// testing::set_caller_address(owner); +// ERC721Impl::approve(ref state, OPERATOR(), token_id); +// utils::drop_event(ZERO()); +// +// testing::set_caller_address(OPERATOR()); +// ERC721Impl::transfer_from(ref state, owner, recipient, token_id); +// assert_event_transfer(owner, recipient, token_id); +// +// assert_state_after_transfer(owner, recipient, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_transferFrom_approved() { +// let mut state = setup(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// let recipient = RECIPIENT(); +// assert_state_before_transfer(owner, recipient, token_id); +// +// testing::set_caller_address(owner); +// ERC721Impl::approve(ref state, OPERATOR(), token_id); +// utils::drop_event(ZERO()); +// +// testing::set_caller_address(OPERATOR()); +// ERC721CamelOnlyImpl::transferFrom(ref state, owner, recipient, token_id); +// assert_event_transfer(owner, recipient, token_id); +// +// assert_state_after_transfer(owner, recipient, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_transfer_from_approved_for_all() { +// let mut state = setup(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// let recipient = RECIPIENT(); +// +// assert_state_before_transfer(owner, recipient, token_id); +// +// testing::set_caller_address(owner); +// ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); +// utils::drop_event(ZERO()); +// +// testing::set_caller_address(OPERATOR()); +// ERC721Impl::transfer_from(ref state, owner, recipient, token_id); +// assert_event_transfer(owner, recipient, token_id); +// +// assert_state_after_transfer(owner, recipient, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_transferFrom_approved_for_all() { +// let mut state = setup(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// let recipient = RECIPIENT(); +// +// assert_state_before_transfer(owner, recipient, token_id); +// +// testing::set_caller_address(owner); +// ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); +// utils::drop_event(ZERO()); +// +// testing::set_caller_address(OPERATOR()); +// ERC721CamelOnlyImpl::transferFrom(ref state, owner, recipient, token_id); +// assert_event_transfer(owner, recipient, token_id); +// +// assert_state_after_transfer(owner, recipient, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: unauthorized caller',))] +//fn test_transfer_from_unauthorized() { +// let mut state = setup(); +// testing::set_caller_address(OTHER()); +// ERC721Impl::transfer_from(ref state, OWNER(), RECIPIENT(), TOKEN_ID); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: unauthorized caller',))] +//fn test_transferFrom_unauthorized() { +// let mut state = setup(); +// testing::set_caller_address(OTHER()); +// ERC721CamelOnlyImpl::transferFrom(ref state, OWNER(), RECIPIENT(), TOKEN_ID); +//} +// +//// +//// safe_transfer_from & safeTransferFrom +//// +// +//#[test] +//#[available_gas(20000000)] +//fn test_safe_transfer_from_to_account() { +// let mut state = setup(); +// let account = setup_account(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// +// assert_state_before_transfer(owner, account, token_id); +// +// testing::set_caller_address(owner); +// ERC721Impl::safe_transfer_from(ref state, owner, account, token_id, DATA(true)); +// assert_event_transfer(owner, account, token_id); +// +// assert_state_after_transfer(owner, account, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_safeTransferFrom_to_account() { +// let mut state = setup(); +// let account = setup_account(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// +// assert_state_before_transfer(owner, account, token_id); +// +// testing::set_caller_address(owner); +// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, account, token_id, DATA(true)); +// assert_event_transfer(owner, account, token_id); +// +// assert_state_after_transfer(owner, account, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_safe_transfer_from_to_account_camel() { +// let mut state = setup(); +// let account = setup_camel_account(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// +// assert_state_before_transfer(owner, account, token_id); +// +// testing::set_caller_address(owner); +// ERC721Impl::safe_transfer_from(ref state, owner, account, token_id, DATA(true)); +// assert_event_transfer(owner, account, token_id); +// +// assert_state_after_transfer(owner, account, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_safeTransferFrom_to_account_camel() { +// let mut state = setup(); +// let account = setup_camel_account(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// +// assert_state_before_transfer(owner, account, token_id); +// +// testing::set_caller_address(owner); +// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, account, token_id, DATA(true)); +// assert_event_transfer(owner, account, token_id); +// +// assert_state_after_transfer(owner, account, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_safe_transfer_from_to_receiver() { +// let mut state = setup(); +// let receiver = setup_receiver(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// +// assert_state_before_transfer(owner, receiver, token_id); +// +// testing::set_caller_address(owner); +// ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); +// assert_event_transfer(owner, receiver, token_id); +// +// assert_state_after_transfer(owner, receiver, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_safeTransferFrom_to_receiver() { +// let mut state = setup(); +// let receiver = setup_receiver(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// +// assert_state_before_transfer(owner, receiver, token_id); +// +// testing::set_caller_address(owner); +// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); +// assert_event_transfer(owner, receiver, token_id); +// +// assert_state_after_transfer(owner, receiver, token_id); +//} // -// _transfer +//#[test] +//#[available_gas(20000000)] +//fn test_safe_transfer_from_to_receiver_camel() { +// let mut state = setup(); +// let receiver = setup_camel_receiver(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); // - -#[test] -#[available_gas(20000000)] -fn test__transfer() { - let mut state = setup(); - let token_id = TOKEN_ID; - let owner = OWNER(); - let recipient = RECIPIENT(); - - assert_state_before_transfer(owner, recipient, token_id); - - InternalImpl::_transfer(ref state, owner, recipient, token_id); - assert_event_transfer(owner, recipient, token_id); - - assert_state_after_transfer(owner, recipient, token_id); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID',))] -fn test__transfer_nonexistent() { - let mut state = STATE(); - InternalImpl::_transfer(ref state, ZERO(), RECIPIENT(), TOKEN_ID); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid receiver',))] -fn test__transfer_to_zero() { - let mut state = setup(); - InternalImpl::_transfer(ref state, OWNER(), ZERO(), TOKEN_ID); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: wrong sender',))] -fn test__transfer_from_invalid_owner() { - let mut state = setup(); - InternalImpl::_transfer(ref state, RECIPIENT(), OWNER(), TOKEN_ID); -} - +// assert_state_before_transfer(owner, receiver, token_id); // -// _mint +// testing::set_caller_address(owner); +// ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); +// assert_event_transfer(owner, receiver, token_id); // - -#[test] -#[available_gas(20000000)] -fn test__mint() { - let mut state = STATE(); - let recipient = RECIPIENT(); - let token_id = TOKEN_ID; - - assert_state_before_mint(recipient); - InternalImpl::_mint(ref state, recipient, TOKEN_ID); - assert_event_transfer(ZERO(), recipient, token_id); - - assert_state_after_mint(recipient, token_id); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid receiver',))] -fn test__mint_to_zero() { - let mut state = STATE(); - InternalImpl::_mint(ref state, ZERO(), TOKEN_ID); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: token already minted',))] -fn test__mint_already_exist() { - let mut state = setup(); - InternalImpl::_mint(ref state, RECIPIENT(), TOKEN_ID); -} - +// assert_state_after_transfer(owner, receiver, token_id); +//} // -// _safe_mint +//#[test] +//#[available_gas(20000000)] +//fn test_safeTransferFrom_to_receiver_camel() { +// let mut state = setup(); +// let receiver = setup_camel_receiver(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); // - -#[test] -#[available_gas(20000000)] -fn test__safe_mint_to_receiver() { - let mut state = STATE(); - let recipient = setup_receiver(); - let token_id = TOKEN_ID; - - assert_state_before_mint(recipient); - InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(true)); - assert_event_transfer(ZERO(), recipient, token_id); - - assert_state_after_mint(recipient, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test__safe_mint_to_receiver_camel() { - let mut state = STATE(); - let recipient = setup_camel_receiver(); - let token_id = TOKEN_ID; - - assert_state_before_mint(recipient); - InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(true)); - assert_event_transfer(ZERO(), recipient, token_id); - - assert_state_after_mint(recipient, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test__safe_mint_to_account() { - let mut state = STATE(); - let account = setup_account(); - let token_id = TOKEN_ID; - - assert_state_before_mint(account); - InternalImpl::_safe_mint(ref state, account, token_id, DATA(true)); - assert_event_transfer(ZERO(), account, token_id); - - assert_state_after_mint(account, token_id); -} - -#[test] -#[available_gas(20000000)] -fn test__safe_mint_to_account_camel() { - let mut state = STATE(); - let account = setup_camel_account(); - let token_id = TOKEN_ID; - - assert_state_before_mint(account); - InternalImpl::_safe_mint(ref state, account, token_id, DATA(true)); - assert_event_transfer(ZERO(), account, token_id); - - assert_state_after_mint(account, token_id); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] -fn test__safe_mint_to_non_receiver() { - let mut state = STATE(); - let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); - let token_id = TOKEN_ID; - - assert_state_before_mint(recipient); - InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(true)); - assert_state_after_mint(recipient, token_id); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: safe mint failed',))] -fn test__safe_mint_to_receiver_failure() { - let mut state = STATE(); - let recipient = setup_receiver(); - let token_id = TOKEN_ID; - - assert_state_before_mint(recipient); - InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(false)); - assert_state_after_mint(recipient, token_id); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: safe mint failed',))] -fn test__safe_mint_to_receiver_failure_camel() { - let mut state = STATE(); - let recipient = setup_camel_receiver(); - let token_id = TOKEN_ID; - - assert_state_before_mint(recipient); - InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(false)); - assert_state_after_mint(recipient, token_id); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid receiver',))] -fn test__safe_mint_to_zero() { - let mut state = STATE(); - InternalImpl::_safe_mint(ref state, ZERO(), TOKEN_ID, DATA(true)); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: token already minted',))] -fn test__safe_mint_already_exist() { - let mut state = setup(); - InternalImpl::_safe_mint(ref state, RECIPIENT(), TOKEN_ID, DATA(true)); -} - +// assert_state_before_transfer(owner, receiver, token_id); // -// _burn +// testing::set_caller_address(owner); +// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); +// assert_event_transfer(owner, receiver, token_id); // - -#[test] -#[available_gas(20000000)] -fn test__burn() { - let mut state = setup(); - - InternalImpl::_approve(ref state, OTHER(), TOKEN_ID); - utils::drop_event(ZERO()); - - assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership before'); - assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner before'); - assert(ERC721Impl::get_approved(@state, TOKEN_ID) == OTHER(), 'Approval before'); - - InternalImpl::_burn(ref state, TOKEN_ID); - assert_event_transfer(OWNER(), ZERO(), TOKEN_ID); - - assert(state.ERC721_owners.read(TOKEN_ID) == ZERO(), 'Ownership after'); - assert(ERC721Impl::balance_of(@state, OWNER()) == 0, 'Balance of owner after'); - assert(state.ERC721_token_approvals.read(TOKEN_ID) == ZERO(), 'Approval after'); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID',))] -fn test__burn_nonexistent() { - let mut state = STATE(); - InternalImpl::_burn(ref state, TOKEN_ID); -} - +// assert_state_after_transfer(owner, receiver, token_id); +//} // -// _set_token_uri +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: safe transfer failed',))] +//fn test_safe_transfer_from_to_receiver_failure() { +// let mut state = setup(); +// let receiver = setup_receiver(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); // - -#[test] -#[available_gas(20000000)] -fn test__set_token_uri() { - let mut state = setup(); - - assert(ERC721MetadataImpl::token_uri(@state, TOKEN_ID) == 0, 'URI should be 0'); - InternalImpl::_set_token_uri(ref state, TOKEN_ID, URI); - assert(ERC721MetadataImpl::token_uri(@state, TOKEN_ID) == URI, 'URI should be set'); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC721: invalid token ID',))] -fn test__set_token_uri_nonexistent() { - let mut state = STATE(); - InternalImpl::_set_token_uri(ref state, TOKEN_ID, URI); -} +// testing::set_caller_address(owner); +// ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(false)); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: safe transfer failed',))] +//fn test_safeTransferFrom_to_receiver_failure() { +// let mut state = setup(); +// let receiver = setup_receiver(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// +// testing::set_caller_address(owner); +// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(false)); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: safe transfer failed',))] +//fn test_safe_transfer_from_to_receiver_failure_camel() { +// let mut state = setup(); +// let receiver = setup_camel_receiver(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// +// testing::set_caller_address(owner); +// ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(false)); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: safe transfer failed',))] +//fn test_safeTransferFrom_to_receiver_failure_camel() { +// let mut state = setup(); +// let receiver = setup_camel_receiver(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// +// testing::set_caller_address(owner); +// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(false)); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] +//fn test_safe_transfer_from_to_non_receiver() { +// let mut state = setup(); +// let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// +// testing::set_caller_address(owner); +// ERC721Impl::safe_transfer_from(ref state, owner, recipient, token_id, DATA(true)); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] +//fn test_safeTransferFrom_to_non_receiver() { +// let mut state = setup(); +// let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// +// testing::set_caller_address(owner); +// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, recipient, token_id, DATA(true)); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: invalid token ID',))] +//fn test_safe_transfer_from_nonexistent() { +// let mut state = STATE(); +// ERC721Impl::safe_transfer_from(ref state, ZERO(), RECIPIENT(), TOKEN_ID, DATA(true)); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: invalid token ID',))] +//fn test_safeTransferFrom_nonexistent() { +// let mut state = STATE(); +// ERC721CamelOnlyImpl::safeTransferFrom(ref state, ZERO(), RECIPIENT(), TOKEN_ID, DATA(true)); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: invalid receiver',))] +//fn test_safe_transfer_from_to_zero() { +// let mut state = setup(); +// testing::set_caller_address(OWNER()); +// ERC721Impl::safe_transfer_from(ref state, OWNER(), ZERO(), TOKEN_ID, DATA(true)); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: invalid receiver',))] +//fn test_safeTransferFrom_to_zero() { +// let mut state = setup(); +// testing::set_caller_address(OWNER()); +// ERC721CamelOnlyImpl::safeTransferFrom(ref state, OWNER(), ZERO(), TOKEN_ID, DATA(true)); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_safe_transfer_from_to_owner() { +// let mut state = STATE(); +// let token_id = TOKEN_ID; +// let owner = setup_receiver(); +// InternalImpl::initializer(ref state, NAME, SYMBOL); +// InternalImpl::_mint(ref state, owner, token_id); +// utils::drop_event(ZERO()); +// +// assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); +// assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); +// +// testing::set_caller_address(owner); +// ERC721Impl::safe_transfer_from(ref state, owner, owner, token_id, DATA(true)); +// assert_event_transfer(owner, owner, token_id); +// +// assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership after'); +// assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner after'); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_safeTransferFrom_to_owner() { +// let mut state = STATE(); +// let token_id = TOKEN_ID; +// let owner = setup_receiver(); +// InternalImpl::initializer(ref state, NAME, SYMBOL); +// InternalImpl::_mint(ref state, owner, token_id); +// utils::drop_event(ZERO()); +// +// assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); +// assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); +// +// testing::set_caller_address(owner); +// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, owner, token_id, DATA(true)); +// assert_event_transfer(owner, owner, token_id); +// +// assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership after'); +// assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner after'); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_safe_transfer_from_to_owner_camel() { +// let mut state = STATE(); +// let token_id = TOKEN_ID; +// let owner = setup_camel_receiver(); +// InternalImpl::initializer(ref state, NAME, SYMBOL); +// InternalImpl::_mint(ref state, owner, token_id); +// utils::drop_event(ZERO()); +// +// assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); +// assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); +// +// testing::set_caller_address(owner); +// ERC721Impl::safe_transfer_from(ref state, owner, owner, token_id, DATA(true)); +// assert_event_transfer(owner, owner, token_id); +// +// assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership after'); +// assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner after'); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_safeTransferFrom_to_owner_camel() { +// let mut state = STATE(); +// let token_id = TOKEN_ID; +// let owner = setup_camel_receiver(); +// InternalImpl::initializer(ref state, NAME, SYMBOL); +// InternalImpl::_mint(ref state, owner, token_id); +// utils::drop_event(ZERO()); +// +// assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); +// assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); +// +// testing::set_caller_address(owner); +// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, owner, token_id, DATA(true)); +// assert_event_transfer(owner, owner, token_id); +// +// assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership after'); +// assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner after'); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_safe_transfer_from_approved() { +// let mut state = setup(); +// let receiver = setup_receiver(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// +// assert_state_before_transfer(owner, receiver, token_id); +// +// testing::set_caller_address(owner); +// ERC721Impl::approve(ref state, OPERATOR(), token_id); +// utils::drop_event(ZERO()); +// +// testing::set_caller_address(OPERATOR()); +// ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); +// assert_event_transfer(owner, receiver, token_id); +// +// assert_state_after_transfer(owner, receiver, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_safeTransferFrom_approved() { +// let mut state = setup(); +// let receiver = setup_receiver(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// +// assert_state_before_transfer(owner, receiver, token_id); +// +// testing::set_caller_address(owner); +// ERC721Impl::approve(ref state, OPERATOR(), token_id); +// utils::drop_event(ZERO()); +// +// testing::set_caller_address(OPERATOR()); +// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); +// assert_event_transfer(owner, receiver, token_id); +// +// assert_state_after_transfer(owner, receiver, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_safe_transfer_from_approved_camel() { +// let mut state = setup(); +// let receiver = setup_camel_receiver(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// +// assert_state_before_transfer(owner, receiver, token_id); +// +// testing::set_caller_address(owner); +// ERC721Impl::approve(ref state, OPERATOR(), token_id); +// utils::drop_event(ZERO()); +// +// testing::set_caller_address(OPERATOR()); +// ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); +// assert_event_transfer(owner, receiver, token_id); +// +// assert_state_after_transfer(owner, receiver, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_safeTransferFrom_approved_camel() { +// let mut state = setup(); +// let receiver = setup_camel_receiver(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// +// assert_state_before_transfer(owner, receiver, token_id); +// +// testing::set_caller_address(owner); +// ERC721Impl::approve(ref state, OPERATOR(), token_id); +// utils::drop_event(ZERO()); +// +// testing::set_caller_address(OPERATOR()); +// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); +// assert_event_transfer(owner, receiver, token_id); +// +// assert_state_after_transfer(owner, receiver, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_safe_transfer_from_approved_for_all() { +// let mut state = setup(); +// let receiver = setup_receiver(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// +// assert_state_before_transfer(owner, receiver, token_id); +// +// testing::set_caller_address(owner); +// ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); +// utils::drop_event(ZERO()); +// +// testing::set_caller_address(OPERATOR()); +// ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); +// assert_event_transfer(owner, receiver, token_id); +// +// assert_state_after_transfer(owner, receiver, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_safeTransferFrom_approved_for_all() { +// let mut state = setup(); +// let receiver = setup_receiver(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// +// assert_state_before_transfer(owner, receiver, token_id); +// +// testing::set_caller_address(owner); +// ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); +// utils::drop_event(ZERO()); +// +// testing::set_caller_address(OPERATOR()); +// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); +// assert_event_transfer(owner, receiver, token_id); +// +// assert_state_after_transfer(owner, receiver, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_safe_transfer_from_approved_for_all_camel() { +// let mut state = setup(); +// let receiver = setup_camel_receiver(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// +// assert_state_before_transfer(owner, receiver, token_id); +// +// testing::set_caller_address(owner); +// ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); +// utils::drop_event(ZERO()); +// +// testing::set_caller_address(OPERATOR()); +// ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); +// assert_event_transfer(owner, receiver, token_id); +// +// assert_state_after_transfer(owner, receiver, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test_safeTransferFrom_approved_for_all_camel() { +// let mut state = setup(); +// let receiver = setup_camel_receiver(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// +// assert_state_before_transfer(owner, receiver, token_id); +// +// testing::set_caller_address(owner); +// ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); +// utils::drop_event(ZERO()); +// +// testing::set_caller_address(OPERATOR()); +// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); +// assert_event_transfer(owner, receiver, token_id); +// +// assert_state_after_transfer(owner, receiver, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: unauthorized caller',))] +//fn test_safe_transfer_from_unauthorized() { +// let mut state = setup(); +// testing::set_caller_address(OTHER()); +// ERC721Impl::safe_transfer_from(ref state, OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: unauthorized caller',))] +//fn test_safeTransferFrom_unauthorized() { +// let mut state = setup(); +// testing::set_caller_address(OTHER()); +// ERC721CamelOnlyImpl::safeTransferFrom(ref state, OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); +//} +// +//// +//// _transfer +//// +// +//#[test] +//#[available_gas(20000000)] +//fn test__transfer() { +// let mut state = setup(); +// let token_id = TOKEN_ID; +// let owner = OWNER(); +// let recipient = RECIPIENT(); +// +// assert_state_before_transfer(owner, recipient, token_id); +// +// InternalImpl::_transfer(ref state, owner, recipient, token_id); +// assert_event_transfer(owner, recipient, token_id); +// +// assert_state_after_transfer(owner, recipient, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: invalid token ID',))] +//fn test__transfer_nonexistent() { +// let mut state = STATE(); +// InternalImpl::_transfer(ref state, ZERO(), RECIPIENT(), TOKEN_ID); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: invalid receiver',))] +//fn test__transfer_to_zero() { +// let mut state = setup(); +// InternalImpl::_transfer(ref state, OWNER(), ZERO(), TOKEN_ID); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: wrong sender',))] +//fn test__transfer_from_invalid_owner() { +// let mut state = setup(); +// InternalImpl::_transfer(ref state, RECIPIENT(), OWNER(), TOKEN_ID); +//} +// +//// +//// _mint +//// +// +//#[test] +//#[available_gas(20000000)] +//fn test__mint() { +// let mut state = STATE(); +// let recipient = RECIPIENT(); +// let token_id = TOKEN_ID; +// +// assert_state_before_mint(recipient); +// InternalImpl::_mint(ref state, recipient, TOKEN_ID); +// assert_event_transfer(ZERO(), recipient, token_id); +// +// assert_state_after_mint(recipient, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: invalid receiver',))] +//fn test__mint_to_zero() { +// let mut state = STATE(); +// InternalImpl::_mint(ref state, ZERO(), TOKEN_ID); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: token already minted',))] +//fn test__mint_already_exist() { +// let mut state = setup(); +// InternalImpl::_mint(ref state, RECIPIENT(), TOKEN_ID); +//} +// +//// +//// _safe_mint +//// +// +//#[test] +//#[available_gas(20000000)] +//fn test__safe_mint_to_receiver() { +// let mut state = STATE(); +// let recipient = setup_receiver(); +// let token_id = TOKEN_ID; +// +// assert_state_before_mint(recipient); +// InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(true)); +// assert_event_transfer(ZERO(), recipient, token_id); +// +// assert_state_after_mint(recipient, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test__safe_mint_to_receiver_camel() { +// let mut state = STATE(); +// let recipient = setup_camel_receiver(); +// let token_id = TOKEN_ID; +// +// assert_state_before_mint(recipient); +// InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(true)); +// assert_event_transfer(ZERO(), recipient, token_id); +// +// assert_state_after_mint(recipient, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test__safe_mint_to_account() { +// let mut state = STATE(); +// let account = setup_account(); +// let token_id = TOKEN_ID; +// +// assert_state_before_mint(account); +// InternalImpl::_safe_mint(ref state, account, token_id, DATA(true)); +// assert_event_transfer(ZERO(), account, token_id); +// +// assert_state_after_mint(account, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//fn test__safe_mint_to_account_camel() { +// let mut state = STATE(); +// let account = setup_camel_account(); +// let token_id = TOKEN_ID; +// +// assert_state_before_mint(account); +// InternalImpl::_safe_mint(ref state, account, token_id, DATA(true)); +// assert_event_transfer(ZERO(), account, token_id); +// +// assert_state_after_mint(account, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] +//fn test__safe_mint_to_non_receiver() { +// let mut state = STATE(); +// let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); +// let token_id = TOKEN_ID; +// +// assert_state_before_mint(recipient); +// InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(true)); +// assert_state_after_mint(recipient, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: safe mint failed',))] +//fn test__safe_mint_to_receiver_failure() { +// let mut state = STATE(); +// let recipient = setup_receiver(); +// let token_id = TOKEN_ID; +// +// assert_state_before_mint(recipient); +// InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(false)); +// assert_state_after_mint(recipient, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: safe mint failed',))] +//fn test__safe_mint_to_receiver_failure_camel() { +// let mut state = STATE(); +// let recipient = setup_camel_receiver(); +// let token_id = TOKEN_ID; +// +// assert_state_before_mint(recipient); +// InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(false)); +// assert_state_after_mint(recipient, token_id); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: invalid receiver',))] +//fn test__safe_mint_to_zero() { +// let mut state = STATE(); +// InternalImpl::_safe_mint(ref state, ZERO(), TOKEN_ID, DATA(true)); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: token already minted',))] +//fn test__safe_mint_already_exist() { +// let mut state = setup(); +// InternalImpl::_safe_mint(ref state, RECIPIENT(), TOKEN_ID, DATA(true)); +//} +// +//// +//// _burn +//// +// +//#[test] +//#[available_gas(20000000)] +//fn test__burn() { +// let mut state = setup(); +// +// InternalImpl::_approve(ref state, OTHER(), TOKEN_ID); +// utils::drop_event(ZERO()); +// +// assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership before'); +// assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner before'); +// assert(ERC721Impl::get_approved(@state, TOKEN_ID) == OTHER(), 'Approval before'); +// +// InternalImpl::_burn(ref state, TOKEN_ID); +// assert_event_transfer(OWNER(), ZERO(), TOKEN_ID); +// +// assert(state.ERC721_owners.read(TOKEN_ID) == ZERO(), 'Ownership after'); +// assert(ERC721Impl::balance_of(@state, OWNER()) == 0, 'Balance of owner after'); +// assert(state.ERC721_token_approvals.read(TOKEN_ID) == ZERO(), 'Approval after'); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: invalid token ID',))] +//fn test__burn_nonexistent() { +// let mut state = STATE(); +// InternalImpl::_burn(ref state, TOKEN_ID); +//} +// +//// +//// _set_token_uri +//// +// +//#[test] +//#[available_gas(20000000)] +//fn test__set_token_uri() { +// let mut state = setup(); +// +// assert(ERC721MetadataImpl::token_uri(@state, TOKEN_ID) == 0, 'URI should be 0'); +// InternalImpl::_set_token_uri(ref state, TOKEN_ID, URI); +// assert(ERC721MetadataImpl::token_uri(@state, TOKEN_ID) == URI, 'URI should be set'); +//} +// +//#[test] +//#[available_gas(20000000)] +//#[should_panic(expected: ('ERC721: invalid token ID',))] +//fn test__set_token_uri_nonexistent() { +// let mut state = STATE(); +// InternalImpl::_set_token_uri(ref state, TOKEN_ID, URI); +//} // // Helpers From 1f29ac18ca7dedc95d962b1b649fed8170808be5 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 16 Oct 2023 10:58:48 -0400 Subject: [PATCH 190/246] add pop_log_comp_indexed --- src/tests/utils.cairo | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/tests/utils.cairo b/src/tests/utils.cairo index 2c429c5a3..1ac1b8b28 100644 --- a/src/tests/utils.cairo +++ b/src/tests/utils.cairo @@ -29,6 +29,25 @@ fn pop_log, impl TEvent: starknet::Event>( ret } +/// Similar to `pop_log` and should only be used for inspecting component events with +/// indexed parameters. Component events with indexed params set the first key as the +/// contract implementation ID and the second key as the event ID. This function +/// removes both keys from the event. +fn pop_log_comp_indexed, impl TEvent: starknet::Event>( + address: ContractAddress +) -> Option { + let (mut keys, mut data) = testing::pop_log_raw(address)?; + + // Remove the contract impl ID from keys + keys.pop_front(); + // Remove the event ID from keys + keys.pop_front(); + + let ret = starknet::Event::deserialize(ref keys, ref data); + assert(data.is_empty(), 'Event has extra data'); + ret +} + /// Asserts that `expected_keys` exactly matches the indexed keys from `event`. /// `expected_keys` must include all indexed event keys for `event` in the order /// that they're defined. From 8d14e0f7b7cda751fb59ebb1f40784a29296358b Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 16 Oct 2023 10:59:12 -0400 Subject: [PATCH 191/246] update tests --- src/tests/token/test_erc721.cairo | 2278 ++++++++++++++--------------- 1 file changed, 1139 insertions(+), 1139 deletions(-) diff --git a/src/tests/token/test_erc721.cairo b/src/tests/token/test_erc721.cairo index f9fdbec3f..e4e8c1046 100644 --- a/src/tests/token/test_erc721.cairo +++ b/src/tests/token/test_erc721.cairo @@ -188,1147 +188,1147 @@ fn test_approve_from_owner() { ); } -//#[test] -//#[available_gas(20000000)] -//fn test_approve_from_operator() { -// let mut state = setup(); -// -// testing::set_caller_address(OWNER()); -// ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); -// utils::drop_event(ZERO()); -// -// testing::set_caller_address(OPERATOR()); -// ERC721Impl::approve(ref state, SPENDER(), TOKEN_ID); -// assert_event_approval(OWNER(), SPENDER(), TOKEN_ID); -// -// assert( -// ERC721Impl::get_approved(@state, TOKEN_ID) == SPENDER(), 'Spender not approved correctly' -// ); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: unauthorized caller',))] -//fn test_approve_from_unauthorized() { -// let mut state = setup(); -// -// testing::set_caller_address(OTHER()); -// ERC721Impl::approve(ref state, SPENDER(), TOKEN_ID); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: approval to owner',))] -//fn test_approve_to_owner() { -// let mut state = setup(); -// -// testing::set_caller_address(OWNER()); -// ERC721Impl::approve(ref state, OWNER(), TOKEN_ID); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: invalid token ID',))] -//fn test_approve_nonexistent() { -// let mut state = STATE(); -// ERC721Impl::approve(ref state, SPENDER(), TOKEN_ID); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test__approve() { -// let mut state = setup(); -// InternalImpl::_approve(ref state, SPENDER(), TOKEN_ID); -// assert_event_approval(OWNER(), SPENDER(), TOKEN_ID); -// -// assert( -// ERC721Impl::get_approved(@state, TOKEN_ID) == SPENDER(), 'Spender not approved correctly' -// ); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: approval to owner',))] -//fn test__approve_to_owner() { -// let mut state = setup(); -// InternalImpl::_approve(ref state, OWNER(), TOKEN_ID); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: invalid token ID',))] -//fn test__approve_nonexistent() { -// let mut state = STATE(); -// InternalImpl::_approve(ref state, SPENDER(), TOKEN_ID); -//} -// -//// -//// set_approval_for_all & _set_approval_for_all -//// -// -//#[test] -//#[available_gas(20000000)] -//fn test_set_approval_for_all() { -// let mut state = STATE(); -// testing::set_caller_address(OWNER()); -// -// assert(!ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), 'Invalid default value'); -// -// ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); -// assert_event_approval_for_all(OWNER(), OPERATOR(), true); -// -// assert( -// ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), -// 'Operator not approved correctly' -// ); -// -// ERC721Impl::set_approval_for_all(ref state, OPERATOR(), false); -// assert_event_approval_for_all(OWNER(), OPERATOR(), false); -// -// assert( -// !ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), -// 'Approval not revoked correctly' -// ); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: self approval',))] -//fn test_set_approval_for_all_owner_equal_operator_true() { -// let mut state = STATE(); -// testing::set_caller_address(OWNER()); -// ERC721Impl::set_approval_for_all(ref state, OWNER(), true); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: self approval',))] -//fn test_set_approval_for_all_owner_equal_operator_false() { -// let mut state = STATE(); -// testing::set_caller_address(OWNER()); -// ERC721Impl::set_approval_for_all(ref state, OWNER(), false); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test__set_approval_for_all() { -// let mut state = STATE(); -// assert(!ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), 'Invalid default value'); -// -// InternalImpl::_set_approval_for_all(ref state, OWNER(), OPERATOR(), true); -// assert_event_approval_for_all(OWNER(), OPERATOR(), true); -// -// assert( -// ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), -// 'Operator not approved correctly' -// ); -// -// InternalImpl::_set_approval_for_all(ref state, OWNER(), OPERATOR(), false); -// assert_event_approval_for_all(OWNER(), OPERATOR(), false); -// -// assert( -// !ERC721Impl::is_approved_for_all(@state, OWNER(), OPERATOR()), -// 'Operator not approved correctly' -// ); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: self approval',))] -//fn test__set_approval_for_all_owner_equal_operator_true() { -// let mut state = STATE(); -// InternalImpl::_set_approval_for_all(ref state, OWNER(), OWNER(), true); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: self approval',))] -//fn test__set_approval_for_all_owner_equal_operator_false() { -// let mut state = STATE(); -// InternalImpl::_set_approval_for_all(ref state, OWNER(), OWNER(), false); -//} -// -//// -//// transfer_from & transferFrom -//// -// -//#[test] -//#[available_gas(20000000)] -//fn test_transfer_from_owner() { -// let mut state = setup(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// let recipient = RECIPIENT(); -// // set approval to check reset -// InternalImpl::_approve(ref state, OTHER(), token_id); -// utils::drop_event(ZERO()); -// -// assert_state_before_transfer(owner, recipient, token_id); -// assert(ERC721Impl::get_approved(@state, token_id) == OTHER(), 'Approval not implicitly reset'); -// -// testing::set_caller_address(owner); -// ERC721Impl::transfer_from(ref state, owner, recipient, token_id); -// assert_event_transfer(owner, recipient, token_id); +#[test] +#[available_gas(20000000)] +fn test_approve_from_operator() { + let mut state = setup(); + + testing::set_caller_address(OWNER()); + state.erc721.set_approval_for_all(OPERATOR(), true); + utils::drop_event(ZERO()); + + testing::set_caller_address(OPERATOR()); + state.erc721.approve(SPENDER(), TOKEN_ID); + assert_event_approval(OWNER(), SPENDER(), TOKEN_ID); + + assert( + state.erc721.get_approved(TOKEN_ID) == SPENDER(), 'Spender not approved correctly' + ); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: unauthorized caller',))] +fn test_approve_from_unauthorized() { + let mut state = setup(); + + testing::set_caller_address(OTHER()); + state.erc721.approve(SPENDER(), TOKEN_ID); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: approval to owner',))] +fn test_approve_to_owner() { + let mut state = setup(); + + testing::set_caller_address(OWNER()); + state.erc721.approve(OWNER(), TOKEN_ID); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: invalid token ID',))] +fn test_approve_nonexistent() { + let mut state = STATE(); + state.erc721.approve(SPENDER(), TOKEN_ID); +} + +#[test] +#[available_gas(20000000)] +fn test__approve() { + let mut state = setup(); + state.erc721._approve(SPENDER(), TOKEN_ID); + assert_event_approval(OWNER(), SPENDER(), TOKEN_ID); + + assert( + state.erc721.get_approved(TOKEN_ID) == SPENDER(), 'Spender not approved correctly' + ); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: approval to owner',))] +fn test__approve_to_owner() { + let mut state = setup(); + state.erc721._approve(OWNER(), TOKEN_ID); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: invalid token ID',))] +fn test__approve_nonexistent() { + let mut state = STATE(); + state.erc721._approve(SPENDER(), TOKEN_ID); +} + // -// assert_state_after_transfer(owner, recipient, token_id); -//} +// set_approval_for_all & _set_approval_for_all // -//#[test] -//#[available_gas(20000000)] -//fn test_transferFrom_owner() { -// let mut state = setup(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// let recipient = RECIPIENT(); -// // set approval to check reset -// InternalImpl::_approve(ref state, OTHER(), token_id); -// utils::drop_event(ZERO()); + +#[test] +#[available_gas(20000000)] +fn test_set_approval_for_all() { + let mut state = STATE(); + testing::set_caller_address(OWNER()); + + assert(!state.erc721.is_approved_for_all(OWNER(), OPERATOR()), 'Invalid default value'); + + state.erc721.set_approval_for_all(OPERATOR(), true); + assert_event_approval_for_all(OWNER(), OPERATOR(), true); + + assert( + state.erc721.is_approved_for_all(OWNER(), OPERATOR()), + 'Operator not approved correctly' + ); + + state.erc721.set_approval_for_all(OPERATOR(), false); + assert_event_approval_for_all(OWNER(), OPERATOR(), false); + + assert( + !state.erc721.is_approved_for_all(OWNER(), OPERATOR()), + 'Approval not revoked correctly' + ); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: self approval',))] +fn test_set_approval_for_all_owner_equal_operator_true() { + let mut state = STATE(); + testing::set_caller_address(OWNER()); + state.erc721.set_approval_for_all(OWNER(), true); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: self approval',))] +fn test_set_approval_for_all_owner_equal_operator_false() { + let mut state = STATE(); + testing::set_caller_address(OWNER()); + state.erc721.set_approval_for_all(OWNER(), false); +} + +#[test] +#[available_gas(20000000)] +fn test__set_approval_for_all() { + let mut state = STATE(); + assert(!state.erc721.is_approved_for_all(OWNER(), OPERATOR()), 'Invalid default value'); + + state.erc721._set_approval_for_all(OWNER(), OPERATOR(), true); + assert_event_approval_for_all(OWNER(), OPERATOR(), true); + + assert( + state.erc721.is_approved_for_all(OWNER(), OPERATOR()), + 'Operator not approved correctly' + ); + + state.erc721._set_approval_for_all(OWNER(), OPERATOR(), false); + assert_event_approval_for_all(OWNER(), OPERATOR(), false); + + assert( + !state.erc721.is_approved_for_all(OWNER(), OPERATOR()), + 'Operator not approved correctly' + ); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: self approval',))] +fn test__set_approval_for_all_owner_equal_operator_true() { + let mut state = STATE(); + state.erc721._set_approval_for_all(OWNER(), OWNER(), true); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: self approval',))] +fn test__set_approval_for_all_owner_equal_operator_false() { + let mut state = STATE(); + state.erc721._set_approval_for_all(OWNER(), OWNER(), false); +} + // -// assert_state_before_transfer(owner, recipient, token_id); -// assert(ERC721Impl::get_approved(@state, token_id) == OTHER(), 'Approval not implicitly reset'); +// transfer_from & transferFrom // -// testing::set_caller_address(owner); -// ERC721CamelOnlyImpl::transferFrom(ref state, owner, recipient, token_id); -// assert_event_transfer(owner, recipient, token_id); + +#[test] +#[available_gas(20000000)] +fn test_transfer_from_owner() { + let mut state = setup(); + let token_id = TOKEN_ID; + let owner = OWNER(); + let recipient = RECIPIENT(); + // set approval to check reset + state.erc721._approve(OTHER(), token_id); + utils::drop_event(ZERO()); + + assert_state_before_transfer(owner, recipient, token_id); + assert(state.erc721.get_approved(token_id) == OTHER(), 'Approval not implicitly reset'); + + testing::set_caller_address(owner); + state.erc721.transfer_from(owner, recipient, token_id); + assert_event_transfer(owner, recipient, token_id); + + assert_state_after_transfer(owner, recipient, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test_transferFrom_owner() { + let mut state = setup(); + let token_id = TOKEN_ID; + let owner = OWNER(); + let recipient = RECIPIENT(); + // set approval to check reset + state.erc721._approve(OTHER(), token_id); + utils::drop_event(ZERO()); + + assert_state_before_transfer(owner, recipient, token_id); + assert(state.erc721.get_approved(token_id) == OTHER(), 'Approval not implicitly reset'); + + testing::set_caller_address(owner); + state.erc721.transferFrom(owner, recipient, token_id); + assert_event_transfer(owner, recipient, token_id); + + assert_state_after_transfer(owner, recipient, token_id); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: invalid token ID',))] +fn test_transfer_from_nonexistent() { + let mut state = STATE(); + state.erc721.transfer_from(ZERO(), RECIPIENT(), TOKEN_ID); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: invalid token ID',))] +fn test_transferFrom_nonexistent() { + let mut state = STATE(); + state.erc721.transferFrom(ZERO(), RECIPIENT(), TOKEN_ID); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: invalid receiver',))] +fn test_transfer_from_to_zero() { + let mut state = setup(); + testing::set_caller_address(OWNER()); + state.erc721.transfer_from(OWNER(), ZERO(), TOKEN_ID); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: invalid receiver',))] +fn test_transferFrom_to_zero() { + let mut state = setup(); + + testing::set_caller_address(OWNER()); + state.erc721.transferFrom(OWNER(), ZERO(), TOKEN_ID); +} + +#[test] +#[available_gas(20000000)] +fn test_transfer_from_to_owner() { + let mut state = setup(); + + assert(state.erc721.owner_of(TOKEN_ID) == OWNER(), 'Ownership before'); + assert(state.erc721.balance_of(OWNER()) == 1, 'Balance of owner before'); + + testing::set_caller_address(OWNER()); + state.erc721.transfer_from(OWNER(), OWNER(), TOKEN_ID); + assert_event_transfer(OWNER(), OWNER(), TOKEN_ID); + + assert(state.erc721.owner_of(TOKEN_ID) == OWNER(), 'Ownership after'); + assert(state.erc721.balance_of(OWNER()) == 1, 'Balance of owner after'); +} + +#[test] +#[available_gas(20000000)] +fn test_transferFrom_to_owner() { + let mut state = setup(); + + assert(state.erc721.owner_of(TOKEN_ID) == OWNER(), 'Ownership before'); + assert(state.erc721.balance_of(OWNER()) == 1, 'Balance of owner before'); + + testing::set_caller_address(OWNER()); + state.erc721.transferFrom(OWNER(), OWNER(), TOKEN_ID); + assert_event_transfer(OWNER(), OWNER(), TOKEN_ID); + + assert(state.erc721.owner_of(TOKEN_ID) == OWNER(), 'Ownership after'); + assert(state.erc721.balance_of(OWNER()) == 1, 'Balance of owner after'); +} + +#[test] +#[available_gas(20000000)] +fn test_transfer_from_approved() { + let mut state = setup(); + let token_id = TOKEN_ID; + let owner = OWNER(); + let recipient = RECIPIENT(); + assert_state_before_transfer(owner, recipient, token_id); + + testing::set_caller_address(owner); + state.erc721.approve(OPERATOR(), token_id); + utils::drop_event(ZERO()); + + testing::set_caller_address(OPERATOR()); + state.erc721.transfer_from(owner, recipient, token_id); + assert_event_transfer(owner, recipient, token_id); + + assert_state_after_transfer(owner, recipient, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test_transferFrom_approved() { + let mut state = setup(); + let token_id = TOKEN_ID; + let owner = OWNER(); + let recipient = RECIPIENT(); + assert_state_before_transfer(owner, recipient, token_id); + + testing::set_caller_address(owner); + state.erc721.approve(OPERATOR(), token_id); + utils::drop_event(ZERO()); + + testing::set_caller_address(OPERATOR()); + state.erc721.transferFrom(owner, recipient, token_id); + assert_event_transfer(owner, recipient, token_id); + + assert_state_after_transfer(owner, recipient, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test_transfer_from_approved_for_all() { + let mut state = setup(); + let token_id = TOKEN_ID; + let owner = OWNER(); + let recipient = RECIPIENT(); + + assert_state_before_transfer(owner, recipient, token_id); + + testing::set_caller_address(owner); + state.erc721.set_approval_for_all(OPERATOR(), true); + utils::drop_event(ZERO()); + + testing::set_caller_address(OPERATOR()); + state.erc721.transfer_from(owner, recipient, token_id); + assert_event_transfer(owner, recipient, token_id); + + assert_state_after_transfer(owner, recipient, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test_transferFrom_approved_for_all() { + let mut state = setup(); + let token_id = TOKEN_ID; + let owner = OWNER(); + let recipient = RECIPIENT(); + + assert_state_before_transfer(owner, recipient, token_id); + + testing::set_caller_address(owner); + state.erc721.set_approval_for_all(OPERATOR(), true); + utils::drop_event(ZERO()); + + testing::set_caller_address(OPERATOR()); + state.erc721.transferFrom(owner, recipient, token_id); + assert_event_transfer(owner, recipient, token_id); + + assert_state_after_transfer(owner, recipient, token_id); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: unauthorized caller',))] +fn test_transfer_from_unauthorized() { + let mut state = setup(); + testing::set_caller_address(OTHER()); + state.erc721.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: unauthorized caller',))] +fn test_transferFrom_unauthorized() { + let mut state = setup(); + testing::set_caller_address(OTHER()); + state.erc721.transferFrom(OWNER(), RECIPIENT(), TOKEN_ID); +} + // -// assert_state_after_transfer(owner, recipient, token_id); -//} +// safe_transfer_from & safeTransferFrom // -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: invalid token ID',))] -//fn test_transfer_from_nonexistent() { -// let mut state = STATE(); -// ERC721Impl::transfer_from(ref state, ZERO(), RECIPIENT(), TOKEN_ID); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: invalid token ID',))] -//fn test_transferFrom_nonexistent() { -// let mut state = STATE(); -// ERC721CamelOnlyImpl::transferFrom(ref state, ZERO(), RECIPIENT(), TOKEN_ID); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: invalid receiver',))] -//fn test_transfer_from_to_zero() { -// let mut state = setup(); -// testing::set_caller_address(OWNER()); -// ERC721Impl::transfer_from(ref state, OWNER(), ZERO(), TOKEN_ID); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: invalid receiver',))] -//fn test_transferFrom_to_zero() { -// let mut state = setup(); -// -// testing::set_caller_address(OWNER()); -// ERC721CamelOnlyImpl::transferFrom(ref state, OWNER(), ZERO(), TOKEN_ID); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_transfer_from_to_owner() { -// let mut state = setup(); -// -// assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership before'); -// assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner before'); -// -// testing::set_caller_address(OWNER()); -// ERC721Impl::transfer_from(ref state, OWNER(), OWNER(), TOKEN_ID); -// assert_event_transfer(OWNER(), OWNER(), TOKEN_ID); -// -// assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership after'); -// assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner after'); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_transferFrom_to_owner() { -// let mut state = setup(); -// -// assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership before'); -// assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner before'); -// -// testing::set_caller_address(OWNER()); -// ERC721CamelOnlyImpl::transferFrom(ref state, OWNER(), OWNER(), TOKEN_ID); -// assert_event_transfer(OWNER(), OWNER(), TOKEN_ID); -// -// assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership after'); -// assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner after'); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_transfer_from_approved() { -// let mut state = setup(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// let recipient = RECIPIENT(); -// assert_state_before_transfer(owner, recipient, token_id); -// -// testing::set_caller_address(owner); -// ERC721Impl::approve(ref state, OPERATOR(), token_id); -// utils::drop_event(ZERO()); -// -// testing::set_caller_address(OPERATOR()); -// ERC721Impl::transfer_from(ref state, owner, recipient, token_id); -// assert_event_transfer(owner, recipient, token_id); -// -// assert_state_after_transfer(owner, recipient, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_transferFrom_approved() { -// let mut state = setup(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// let recipient = RECIPIENT(); -// assert_state_before_transfer(owner, recipient, token_id); -// -// testing::set_caller_address(owner); -// ERC721Impl::approve(ref state, OPERATOR(), token_id); -// utils::drop_event(ZERO()); -// -// testing::set_caller_address(OPERATOR()); -// ERC721CamelOnlyImpl::transferFrom(ref state, owner, recipient, token_id); -// assert_event_transfer(owner, recipient, token_id); -// -// assert_state_after_transfer(owner, recipient, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_transfer_from_approved_for_all() { -// let mut state = setup(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// let recipient = RECIPIENT(); -// -// assert_state_before_transfer(owner, recipient, token_id); -// -// testing::set_caller_address(owner); -// ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); -// utils::drop_event(ZERO()); -// -// testing::set_caller_address(OPERATOR()); -// ERC721Impl::transfer_from(ref state, owner, recipient, token_id); -// assert_event_transfer(owner, recipient, token_id); -// -// assert_state_after_transfer(owner, recipient, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_transferFrom_approved_for_all() { -// let mut state = setup(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// let recipient = RECIPIENT(); -// -// assert_state_before_transfer(owner, recipient, token_id); -// -// testing::set_caller_address(owner); -// ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); -// utils::drop_event(ZERO()); -// -// testing::set_caller_address(OPERATOR()); -// ERC721CamelOnlyImpl::transferFrom(ref state, owner, recipient, token_id); -// assert_event_transfer(owner, recipient, token_id); -// -// assert_state_after_transfer(owner, recipient, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: unauthorized caller',))] -//fn test_transfer_from_unauthorized() { -// let mut state = setup(); -// testing::set_caller_address(OTHER()); -// ERC721Impl::transfer_from(ref state, OWNER(), RECIPIENT(), TOKEN_ID); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: unauthorized caller',))] -//fn test_transferFrom_unauthorized() { -// let mut state = setup(); -// testing::set_caller_address(OTHER()); -// ERC721CamelOnlyImpl::transferFrom(ref state, OWNER(), RECIPIENT(), TOKEN_ID); -//} -// -//// -//// safe_transfer_from & safeTransferFrom -//// -// -//#[test] -//#[available_gas(20000000)] -//fn test_safe_transfer_from_to_account() { -// let mut state = setup(); -// let account = setup_account(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// assert_state_before_transfer(owner, account, token_id); -// -// testing::set_caller_address(owner); -// ERC721Impl::safe_transfer_from(ref state, owner, account, token_id, DATA(true)); -// assert_event_transfer(owner, account, token_id); -// -// assert_state_after_transfer(owner, account, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_safeTransferFrom_to_account() { -// let mut state = setup(); -// let account = setup_account(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// assert_state_before_transfer(owner, account, token_id); -// -// testing::set_caller_address(owner); -// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, account, token_id, DATA(true)); -// assert_event_transfer(owner, account, token_id); -// -// assert_state_after_transfer(owner, account, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_safe_transfer_from_to_account_camel() { -// let mut state = setup(); -// let account = setup_camel_account(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// assert_state_before_transfer(owner, account, token_id); -// -// testing::set_caller_address(owner); -// ERC721Impl::safe_transfer_from(ref state, owner, account, token_id, DATA(true)); -// assert_event_transfer(owner, account, token_id); -// -// assert_state_after_transfer(owner, account, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_safeTransferFrom_to_account_camel() { -// let mut state = setup(); -// let account = setup_camel_account(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// assert_state_before_transfer(owner, account, token_id); -// -// testing::set_caller_address(owner); -// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, account, token_id, DATA(true)); -// assert_event_transfer(owner, account, token_id); -// -// assert_state_after_transfer(owner, account, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_safe_transfer_from_to_receiver() { -// let mut state = setup(); -// let receiver = setup_receiver(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// assert_state_before_transfer(owner, receiver, token_id); -// -// testing::set_caller_address(owner); -// ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); -// assert_event_transfer(owner, receiver, token_id); -// -// assert_state_after_transfer(owner, receiver, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_safeTransferFrom_to_receiver() { -// let mut state = setup(); -// let receiver = setup_receiver(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// assert_state_before_transfer(owner, receiver, token_id); -// -// testing::set_caller_address(owner); -// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); -// assert_event_transfer(owner, receiver, token_id); -// -// assert_state_after_transfer(owner, receiver, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_safe_transfer_from_to_receiver_camel() { -// let mut state = setup(); -// let receiver = setup_camel_receiver(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// assert_state_before_transfer(owner, receiver, token_id); -// -// testing::set_caller_address(owner); -// ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); -// assert_event_transfer(owner, receiver, token_id); -// -// assert_state_after_transfer(owner, receiver, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_safeTransferFrom_to_receiver_camel() { -// let mut state = setup(); -// let receiver = setup_camel_receiver(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// assert_state_before_transfer(owner, receiver, token_id); -// -// testing::set_caller_address(owner); -// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); -// assert_event_transfer(owner, receiver, token_id); -// -// assert_state_after_transfer(owner, receiver, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: safe transfer failed',))] -//fn test_safe_transfer_from_to_receiver_failure() { -// let mut state = setup(); -// let receiver = setup_receiver(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// testing::set_caller_address(owner); -// ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(false)); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: safe transfer failed',))] -//fn test_safeTransferFrom_to_receiver_failure() { -// let mut state = setup(); -// let receiver = setup_receiver(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// testing::set_caller_address(owner); -// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(false)); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: safe transfer failed',))] -//fn test_safe_transfer_from_to_receiver_failure_camel() { -// let mut state = setup(); -// let receiver = setup_camel_receiver(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// testing::set_caller_address(owner); -// ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(false)); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: safe transfer failed',))] -//fn test_safeTransferFrom_to_receiver_failure_camel() { -// let mut state = setup(); -// let receiver = setup_camel_receiver(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// testing::set_caller_address(owner); -// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(false)); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] -//fn test_safe_transfer_from_to_non_receiver() { -// let mut state = setup(); -// let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// testing::set_caller_address(owner); -// ERC721Impl::safe_transfer_from(ref state, owner, recipient, token_id, DATA(true)); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] -//fn test_safeTransferFrom_to_non_receiver() { -// let mut state = setup(); -// let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// testing::set_caller_address(owner); -// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, recipient, token_id, DATA(true)); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: invalid token ID',))] -//fn test_safe_transfer_from_nonexistent() { -// let mut state = STATE(); -// ERC721Impl::safe_transfer_from(ref state, ZERO(), RECIPIENT(), TOKEN_ID, DATA(true)); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: invalid token ID',))] -//fn test_safeTransferFrom_nonexistent() { -// let mut state = STATE(); -// ERC721CamelOnlyImpl::safeTransferFrom(ref state, ZERO(), RECIPIENT(), TOKEN_ID, DATA(true)); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: invalid receiver',))] -//fn test_safe_transfer_from_to_zero() { -// let mut state = setup(); -// testing::set_caller_address(OWNER()); -// ERC721Impl::safe_transfer_from(ref state, OWNER(), ZERO(), TOKEN_ID, DATA(true)); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: invalid receiver',))] -//fn test_safeTransferFrom_to_zero() { -// let mut state = setup(); -// testing::set_caller_address(OWNER()); -// ERC721CamelOnlyImpl::safeTransferFrom(ref state, OWNER(), ZERO(), TOKEN_ID, DATA(true)); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_safe_transfer_from_to_owner() { -// let mut state = STATE(); -// let token_id = TOKEN_ID; -// let owner = setup_receiver(); -// InternalImpl::initializer(ref state, NAME, SYMBOL); -// InternalImpl::_mint(ref state, owner, token_id); -// utils::drop_event(ZERO()); -// -// assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); -// assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); -// -// testing::set_caller_address(owner); -// ERC721Impl::safe_transfer_from(ref state, owner, owner, token_id, DATA(true)); -// assert_event_transfer(owner, owner, token_id); -// -// assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership after'); -// assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner after'); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_safeTransferFrom_to_owner() { -// let mut state = STATE(); -// let token_id = TOKEN_ID; -// let owner = setup_receiver(); -// InternalImpl::initializer(ref state, NAME, SYMBOL); -// InternalImpl::_mint(ref state, owner, token_id); -// utils::drop_event(ZERO()); -// -// assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); -// assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); -// -// testing::set_caller_address(owner); -// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, owner, token_id, DATA(true)); -// assert_event_transfer(owner, owner, token_id); -// -// assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership after'); -// assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner after'); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_safe_transfer_from_to_owner_camel() { -// let mut state = STATE(); -// let token_id = TOKEN_ID; -// let owner = setup_camel_receiver(); -// InternalImpl::initializer(ref state, NAME, SYMBOL); -// InternalImpl::_mint(ref state, owner, token_id); -// utils::drop_event(ZERO()); -// -// assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); -// assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); -// -// testing::set_caller_address(owner); -// ERC721Impl::safe_transfer_from(ref state, owner, owner, token_id, DATA(true)); -// assert_event_transfer(owner, owner, token_id); -// -// assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership after'); -// assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner after'); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_safeTransferFrom_to_owner_camel() { -// let mut state = STATE(); -// let token_id = TOKEN_ID; -// let owner = setup_camel_receiver(); -// InternalImpl::initializer(ref state, NAME, SYMBOL); -// InternalImpl::_mint(ref state, owner, token_id); -// utils::drop_event(ZERO()); -// -// assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); -// assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); -// -// testing::set_caller_address(owner); -// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, owner, token_id, DATA(true)); -// assert_event_transfer(owner, owner, token_id); -// -// assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership after'); -// assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner after'); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_safe_transfer_from_approved() { -// let mut state = setup(); -// let receiver = setup_receiver(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// assert_state_before_transfer(owner, receiver, token_id); -// -// testing::set_caller_address(owner); -// ERC721Impl::approve(ref state, OPERATOR(), token_id); -// utils::drop_event(ZERO()); -// -// testing::set_caller_address(OPERATOR()); -// ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); -// assert_event_transfer(owner, receiver, token_id); -// -// assert_state_after_transfer(owner, receiver, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_safeTransferFrom_approved() { -// let mut state = setup(); -// let receiver = setup_receiver(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// assert_state_before_transfer(owner, receiver, token_id); -// -// testing::set_caller_address(owner); -// ERC721Impl::approve(ref state, OPERATOR(), token_id); -// utils::drop_event(ZERO()); -// -// testing::set_caller_address(OPERATOR()); -// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); -// assert_event_transfer(owner, receiver, token_id); -// -// assert_state_after_transfer(owner, receiver, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_safe_transfer_from_approved_camel() { -// let mut state = setup(); -// let receiver = setup_camel_receiver(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// assert_state_before_transfer(owner, receiver, token_id); -// -// testing::set_caller_address(owner); -// ERC721Impl::approve(ref state, OPERATOR(), token_id); -// utils::drop_event(ZERO()); -// -// testing::set_caller_address(OPERATOR()); -// ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); -// assert_event_transfer(owner, receiver, token_id); -// -// assert_state_after_transfer(owner, receiver, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_safeTransferFrom_approved_camel() { -// let mut state = setup(); -// let receiver = setup_camel_receiver(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// assert_state_before_transfer(owner, receiver, token_id); -// -// testing::set_caller_address(owner); -// ERC721Impl::approve(ref state, OPERATOR(), token_id); -// utils::drop_event(ZERO()); -// -// testing::set_caller_address(OPERATOR()); -// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); -// assert_event_transfer(owner, receiver, token_id); -// -// assert_state_after_transfer(owner, receiver, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_safe_transfer_from_approved_for_all() { -// let mut state = setup(); -// let receiver = setup_receiver(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// assert_state_before_transfer(owner, receiver, token_id); -// -// testing::set_caller_address(owner); -// ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); -// utils::drop_event(ZERO()); -// -// testing::set_caller_address(OPERATOR()); -// ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); -// assert_event_transfer(owner, receiver, token_id); -// -// assert_state_after_transfer(owner, receiver, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_safeTransferFrom_approved_for_all() { -// let mut state = setup(); -// let receiver = setup_receiver(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// assert_state_before_transfer(owner, receiver, token_id); -// -// testing::set_caller_address(owner); -// ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); -// utils::drop_event(ZERO()); -// -// testing::set_caller_address(OPERATOR()); -// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); -// assert_event_transfer(owner, receiver, token_id); -// -// assert_state_after_transfer(owner, receiver, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_safe_transfer_from_approved_for_all_camel() { -// let mut state = setup(); -// let receiver = setup_camel_receiver(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// assert_state_before_transfer(owner, receiver, token_id); -// -// testing::set_caller_address(owner); -// ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); -// utils::drop_event(ZERO()); -// -// testing::set_caller_address(OPERATOR()); -// ERC721Impl::safe_transfer_from(ref state, owner, receiver, token_id, DATA(true)); -// assert_event_transfer(owner, receiver, token_id); -// -// assert_state_after_transfer(owner, receiver, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test_safeTransferFrom_approved_for_all_camel() { -// let mut state = setup(); -// let receiver = setup_camel_receiver(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// -// assert_state_before_transfer(owner, receiver, token_id); -// -// testing::set_caller_address(owner); -// ERC721Impl::set_approval_for_all(ref state, OPERATOR(), true); -// utils::drop_event(ZERO()); -// -// testing::set_caller_address(OPERATOR()); -// ERC721CamelOnlyImpl::safeTransferFrom(ref state, owner, receiver, token_id, DATA(true)); -// assert_event_transfer(owner, receiver, token_id); -// -// assert_state_after_transfer(owner, receiver, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: unauthorized caller',))] -//fn test_safe_transfer_from_unauthorized() { -// let mut state = setup(); -// testing::set_caller_address(OTHER()); -// ERC721Impl::safe_transfer_from(ref state, OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: unauthorized caller',))] -//fn test_safeTransferFrom_unauthorized() { -// let mut state = setup(); -// testing::set_caller_address(OTHER()); -// ERC721CamelOnlyImpl::safeTransferFrom(ref state, OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); -//} -// -//// -//// _transfer -//// -// -//#[test] -//#[available_gas(20000000)] -//fn test__transfer() { -// let mut state = setup(); -// let token_id = TOKEN_ID; -// let owner = OWNER(); -// let recipient = RECIPIENT(); -// -// assert_state_before_transfer(owner, recipient, token_id); -// -// InternalImpl::_transfer(ref state, owner, recipient, token_id); -// assert_event_transfer(owner, recipient, token_id); -// -// assert_state_after_transfer(owner, recipient, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: invalid token ID',))] -//fn test__transfer_nonexistent() { -// let mut state = STATE(); -// InternalImpl::_transfer(ref state, ZERO(), RECIPIENT(), TOKEN_ID); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: invalid receiver',))] -//fn test__transfer_to_zero() { -// let mut state = setup(); -// InternalImpl::_transfer(ref state, OWNER(), ZERO(), TOKEN_ID); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: wrong sender',))] -//fn test__transfer_from_invalid_owner() { -// let mut state = setup(); -// InternalImpl::_transfer(ref state, RECIPIENT(), OWNER(), TOKEN_ID); -//} -// -//// -//// _mint -//// -// -//#[test] -//#[available_gas(20000000)] -//fn test__mint() { -// let mut state = STATE(); -// let recipient = RECIPIENT(); -// let token_id = TOKEN_ID; -// -// assert_state_before_mint(recipient); -// InternalImpl::_mint(ref state, recipient, TOKEN_ID); -// assert_event_transfer(ZERO(), recipient, token_id); -// -// assert_state_after_mint(recipient, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: invalid receiver',))] -//fn test__mint_to_zero() { -// let mut state = STATE(); -// InternalImpl::_mint(ref state, ZERO(), TOKEN_ID); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: token already minted',))] -//fn test__mint_already_exist() { -// let mut state = setup(); -// InternalImpl::_mint(ref state, RECIPIENT(), TOKEN_ID); -//} -// -//// -//// _safe_mint -//// -// -//#[test] -//#[available_gas(20000000)] -//fn test__safe_mint_to_receiver() { -// let mut state = STATE(); -// let recipient = setup_receiver(); -// let token_id = TOKEN_ID; -// -// assert_state_before_mint(recipient); -// InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(true)); -// assert_event_transfer(ZERO(), recipient, token_id); -// -// assert_state_after_mint(recipient, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test__safe_mint_to_receiver_camel() { -// let mut state = STATE(); -// let recipient = setup_camel_receiver(); -// let token_id = TOKEN_ID; -// -// assert_state_before_mint(recipient); -// InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(true)); -// assert_event_transfer(ZERO(), recipient, token_id); -// -// assert_state_after_mint(recipient, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test__safe_mint_to_account() { -// let mut state = STATE(); -// let account = setup_account(); -// let token_id = TOKEN_ID; -// -// assert_state_before_mint(account); -// InternalImpl::_safe_mint(ref state, account, token_id, DATA(true)); -// assert_event_transfer(ZERO(), account, token_id); -// -// assert_state_after_mint(account, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//fn test__safe_mint_to_account_camel() { -// let mut state = STATE(); -// let account = setup_camel_account(); -// let token_id = TOKEN_ID; -// -// assert_state_before_mint(account); -// InternalImpl::_safe_mint(ref state, account, token_id, DATA(true)); -// assert_event_transfer(ZERO(), account, token_id); -// -// assert_state_after_mint(account, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] -//fn test__safe_mint_to_non_receiver() { -// let mut state = STATE(); -// let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); -// let token_id = TOKEN_ID; -// -// assert_state_before_mint(recipient); -// InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(true)); -// assert_state_after_mint(recipient, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: safe mint failed',))] -//fn test__safe_mint_to_receiver_failure() { -// let mut state = STATE(); -// let recipient = setup_receiver(); -// let token_id = TOKEN_ID; -// -// assert_state_before_mint(recipient); -// InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(false)); -// assert_state_after_mint(recipient, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: safe mint failed',))] -//fn test__safe_mint_to_receiver_failure_camel() { -// let mut state = STATE(); -// let recipient = setup_camel_receiver(); -// let token_id = TOKEN_ID; -// -// assert_state_before_mint(recipient); -// InternalImpl::_safe_mint(ref state, recipient, token_id, DATA(false)); -// assert_state_after_mint(recipient, token_id); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: invalid receiver',))] -//fn test__safe_mint_to_zero() { -// let mut state = STATE(); -// InternalImpl::_safe_mint(ref state, ZERO(), TOKEN_ID, DATA(true)); -//} -// -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: token already minted',))] -//fn test__safe_mint_already_exist() { -// let mut state = setup(); -// InternalImpl::_safe_mint(ref state, RECIPIENT(), TOKEN_ID, DATA(true)); -//} -// -//// -//// _burn -//// + +#[test] +#[available_gas(20000000)] +fn test_safe_transfer_from_to_account() { + let mut state = setup(); + let account = setup_account(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(owner, account, token_id); + + testing::set_caller_address(owner); + state.erc721.safe_transfer_from(owner, account, token_id, DATA(true)); + assert_event_transfer(owner, account, token_id); + + assert_state_after_transfer(owner, account, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test_safeTransferFrom_to_account() { + let mut state = setup(); + let account = setup_account(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(owner, account, token_id); + + testing::set_caller_address(owner); + state.erc721.safeTransferFrom(owner, account, token_id, DATA(true)); + assert_event_transfer(owner, account, token_id); + + assert_state_after_transfer(owner, account, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test_safe_transfer_from_to_account_camel() { + let mut state = setup(); + let account = setup_camel_account(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(owner, account, token_id); + + testing::set_caller_address(owner); + state.erc721.safe_transfer_from(owner, account, token_id, DATA(true)); + assert_event_transfer(owner, account, token_id); + + assert_state_after_transfer(owner, account, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test_safeTransferFrom_to_account_camel() { + let mut state = setup(); + let account = setup_camel_account(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(owner, account, token_id); + + testing::set_caller_address(owner); + state.erc721.safeTransferFrom(owner, account, token_id, DATA(true)); + assert_event_transfer(owner, account, token_id); + + assert_state_after_transfer(owner, account, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test_safe_transfer_from_to_receiver() { + let mut state = setup(); + let receiver = setup_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(owner, receiver, token_id); + + testing::set_caller_address(owner); + state.erc721.safe_transfer_from(owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); + + assert_state_after_transfer(owner, receiver, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test_safeTransferFrom_to_receiver() { + let mut state = setup(); + let receiver = setup_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(owner, receiver, token_id); + + testing::set_caller_address(owner); + state.erc721.safeTransferFrom(owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); + + assert_state_after_transfer(owner, receiver, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test_safe_transfer_from_to_receiver_camel() { + let mut state = setup(); + let receiver = setup_camel_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(owner, receiver, token_id); + + testing::set_caller_address(owner); + state.erc721.safe_transfer_from(owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); + + assert_state_after_transfer(owner, receiver, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test_safeTransferFrom_to_receiver_camel() { + let mut state = setup(); + let receiver = setup_camel_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(owner, receiver, token_id); + + testing::set_caller_address(owner); + state.erc721.safeTransferFrom(owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); + + assert_state_after_transfer(owner, receiver, token_id); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: safe transfer failed',))] +fn test_safe_transfer_from_to_receiver_failure() { + let mut state = setup(); + let receiver = setup_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + testing::set_caller_address(owner); + state.erc721.safe_transfer_from(owner, receiver, token_id, DATA(false)); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: safe transfer failed',))] +fn test_safeTransferFrom_to_receiver_failure() { + let mut state = setup(); + let receiver = setup_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + testing::set_caller_address(owner); + state.erc721.safeTransferFrom(owner, receiver, token_id, DATA(false)); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: safe transfer failed',))] +fn test_safe_transfer_from_to_receiver_failure_camel() { + let mut state = setup(); + let receiver = setup_camel_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + testing::set_caller_address(owner); + state.erc721.safe_transfer_from(owner, receiver, token_id, DATA(false)); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: safe transfer failed',))] +fn test_safeTransferFrom_to_receiver_failure_camel() { + let mut state = setup(); + let receiver = setup_camel_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + testing::set_caller_address(owner); + state.erc721.safeTransferFrom(owner, receiver, token_id, DATA(false)); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] +fn test_safe_transfer_from_to_non_receiver() { + let mut state = setup(); + let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); + let token_id = TOKEN_ID; + let owner = OWNER(); + + testing::set_caller_address(owner); + state.erc721.safe_transfer_from(owner, recipient, token_id, DATA(true)); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] +fn test_safeTransferFrom_to_non_receiver() { + let mut state = setup(); + let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); + let token_id = TOKEN_ID; + let owner = OWNER(); + + testing::set_caller_address(owner); + state.erc721.safeTransferFrom(owner, recipient, token_id, DATA(true)); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: invalid token ID',))] +fn test_safe_transfer_from_nonexistent() { + let mut state = STATE(); + state.erc721.safe_transfer_from(ZERO(), RECIPIENT(), TOKEN_ID, DATA(true)); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: invalid token ID',))] +fn test_safeTransferFrom_nonexistent() { + let mut state = STATE(); + state.erc721.safeTransferFrom(ZERO(), RECIPIENT(), TOKEN_ID, DATA(true)); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: invalid receiver',))] +fn test_safe_transfer_from_to_zero() { + let mut state = setup(); + testing::set_caller_address(OWNER()); + state.erc721.safe_transfer_from(OWNER(), ZERO(), TOKEN_ID, DATA(true)); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: invalid receiver',))] +fn test_safeTransferFrom_to_zero() { + let mut state = setup(); + testing::set_caller_address(OWNER()); + state.erc721.safeTransferFrom(OWNER(), ZERO(), TOKEN_ID, DATA(true)); +} + +#[test] +#[available_gas(20000000)] +fn test_safe_transfer_from_to_owner() { + let mut state = STATE(); + let token_id = TOKEN_ID; + let owner = setup_receiver(); + state.erc721.initializer(NAME, SYMBOL); + state.erc721._mint(owner, token_id); + utils::drop_event(ZERO()); + + assert(state.erc721.owner_of(token_id) == owner, 'Ownership before'); + assert(state.erc721.balance_of(owner) == 1, 'Balance of owner before'); + + testing::set_caller_address(owner); + state.erc721.safe_transfer_from(owner, owner, token_id, DATA(true)); + assert_event_transfer(owner, owner, token_id); + + assert(state.erc721.owner_of(token_id) == owner, 'Ownership after'); + assert(state.erc721.balance_of(owner) == 1, 'Balance of owner after'); +} + +#[test] +#[available_gas(20000000)] +fn test_safeTransferFrom_to_owner() { + let mut state = STATE(); + let token_id = TOKEN_ID; + let owner = setup_receiver(); + state.erc721.initializer(NAME, SYMBOL); + state.erc721._mint(owner, token_id); + utils::drop_event(ZERO()); + + assert(state.erc721.owner_of(token_id) == owner, 'Ownership before'); + assert(state.erc721.balance_of(owner) == 1, 'Balance of owner before'); + + testing::set_caller_address(owner); + state.erc721.safeTransferFrom(owner, owner, token_id, DATA(true)); + assert_event_transfer(owner, owner, token_id); + + assert(state.erc721.owner_of(token_id) == owner, 'Ownership after'); + assert(state.erc721.balance_of(owner) == 1, 'Balance of owner after'); +} + +#[test] +#[available_gas(20000000)] +fn test_safe_transfer_from_to_owner_camel() { + let mut state = STATE(); + let token_id = TOKEN_ID; + let owner = setup_camel_receiver(); + state.erc721.initializer(NAME, SYMBOL); + state.erc721._mint(owner, token_id); + utils::drop_event(ZERO()); + + assert(state.erc721.owner_of(token_id) == owner, 'Ownership before'); + assert(state.erc721.balance_of(owner) == 1, 'Balance of owner before'); + + testing::set_caller_address(owner); + state.erc721.safe_transfer_from(owner, owner, token_id, DATA(true)); + assert_event_transfer(owner, owner, token_id); + + assert(state.erc721.owner_of(token_id) == owner, 'Ownership after'); + assert(state.erc721.balance_of(owner) == 1, 'Balance of owner after'); +} + +#[test] +#[available_gas(20000000)] +fn test_safeTransferFrom_to_owner_camel() { + let mut state = STATE(); + let token_id = TOKEN_ID; + let owner = setup_camel_receiver(); + state.erc721.initializer(NAME, SYMBOL); + state.erc721._mint(owner, token_id); + utils::drop_event(ZERO()); + + assert(state.erc721.owner_of(token_id) == owner, 'Ownership before'); + assert(state.erc721.balance_of(owner) == 1, 'Balance of owner before'); + + testing::set_caller_address(owner); + state.erc721.safeTransferFrom(owner, owner, token_id, DATA(true)); + assert_event_transfer(owner, owner, token_id); + + assert(state.erc721.owner_of(token_id) == owner, 'Ownership after'); + assert(state.erc721.balance_of(owner) == 1, 'Balance of owner after'); +} + +#[test] +#[available_gas(20000000)] +fn test_safe_transfer_from_approved() { + let mut state = setup(); + let receiver = setup_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(owner, receiver, token_id); + + testing::set_caller_address(owner); + state.erc721.approve(OPERATOR(), token_id); + utils::drop_event(ZERO()); + + testing::set_caller_address(OPERATOR()); + state.erc721.safe_transfer_from(owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); + + assert_state_after_transfer(owner, receiver, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test_safeTransferFrom_approved() { + let mut state = setup(); + let receiver = setup_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(owner, receiver, token_id); + + testing::set_caller_address(owner); + state.erc721.approve(OPERATOR(), token_id); + utils::drop_event(ZERO()); + + testing::set_caller_address(OPERATOR()); + state.erc721.safeTransferFrom(owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); + + assert_state_after_transfer(owner, receiver, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test_safe_transfer_from_approved_camel() { + let mut state = setup(); + let receiver = setup_camel_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(owner, receiver, token_id); + + testing::set_caller_address(owner); + state.erc721.approve(OPERATOR(), token_id); + utils::drop_event(ZERO()); + + testing::set_caller_address(OPERATOR()); + state.erc721.safe_transfer_from(owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); + + assert_state_after_transfer(owner, receiver, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test_safeTransferFrom_approved_camel() { + let mut state = setup(); + let receiver = setup_camel_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(owner, receiver, token_id); + + testing::set_caller_address(owner); + state.erc721.approve(OPERATOR(), token_id); + utils::drop_event(ZERO()); + + testing::set_caller_address(OPERATOR()); + state.erc721.safeTransferFrom(owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); + + assert_state_after_transfer(owner, receiver, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test_safe_transfer_from_approved_for_all() { + let mut state = setup(); + let receiver = setup_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(owner, receiver, token_id); + + testing::set_caller_address(owner); + state.erc721.set_approval_for_all(OPERATOR(), true); + utils::drop_event(ZERO()); + + testing::set_caller_address(OPERATOR()); + state.erc721.safe_transfer_from(owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); + + assert_state_after_transfer(owner, receiver, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test_safeTransferFrom_approved_for_all() { + let mut state = setup(); + let receiver = setup_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(owner, receiver, token_id); + + testing::set_caller_address(owner); + state.erc721.set_approval_for_all(OPERATOR(), true); + utils::drop_event(ZERO()); + + testing::set_caller_address(OPERATOR()); + state.erc721.safeTransferFrom(owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); + + assert_state_after_transfer(owner, receiver, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test_safe_transfer_from_approved_for_all_camel() { + let mut state = setup(); + let receiver = setup_camel_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(owner, receiver, token_id); + + testing::set_caller_address(owner); + state.erc721.set_approval_for_all(OPERATOR(), true); + utils::drop_event(ZERO()); + + testing::set_caller_address(OPERATOR()); + state.erc721.safe_transfer_from(owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); + + assert_state_after_transfer(owner, receiver, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test_safeTransferFrom_approved_for_all_camel() { + let mut state = setup(); + let receiver = setup_camel_receiver(); + let token_id = TOKEN_ID; + let owner = OWNER(); + + assert_state_before_transfer(owner, receiver, token_id); + + testing::set_caller_address(owner); + state.erc721.set_approval_for_all(OPERATOR(), true); + utils::drop_event(ZERO()); + + testing::set_caller_address(OPERATOR()); + state.erc721.safeTransferFrom(owner, receiver, token_id, DATA(true)); + assert_event_transfer(owner, receiver, token_id); + + assert_state_after_transfer(owner, receiver, token_id); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: unauthorized caller',))] +fn test_safe_transfer_from_unauthorized() { + let mut state = setup(); + testing::set_caller_address(OTHER()); + state.erc721.safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: unauthorized caller',))] +fn test_safeTransferFrom_unauthorized() { + let mut state = setup(); + testing::set_caller_address(OTHER()); + state.erc721.safeTransferFrom(OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); +} + // -//#[test] -//#[available_gas(20000000)] -//fn test__burn() { -// let mut state = setup(); +// _transfer // -// InternalImpl::_approve(ref state, OTHER(), TOKEN_ID); -// utils::drop_event(ZERO()); + +#[test] +#[available_gas(20000000)] +fn test__transfer() { + let mut state = setup(); + let token_id = TOKEN_ID; + let owner = OWNER(); + let recipient = RECIPIENT(); + + assert_state_before_transfer(owner, recipient, token_id); + + state.erc721._transfer(owner, recipient, token_id); + assert_event_transfer(owner, recipient, token_id); + + assert_state_after_transfer(owner, recipient, token_id); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: invalid token ID',))] +fn test__transfer_nonexistent() { + let mut state = STATE(); + state.erc721._transfer(ZERO(), RECIPIENT(), TOKEN_ID); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: invalid receiver',))] +fn test__transfer_to_zero() { + let mut state = setup(); + state.erc721._transfer(OWNER(), ZERO(), TOKEN_ID); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: wrong sender',))] +fn test__transfer_from_invalid_owner() { + let mut state = setup(); + state.erc721._transfer(RECIPIENT(), OWNER(), TOKEN_ID); +} + // -// assert(ERC721Impl::owner_of(@state, TOKEN_ID) == OWNER(), 'Ownership before'); -// assert(ERC721Impl::balance_of(@state, OWNER()) == 1, 'Balance of owner before'); -// assert(ERC721Impl::get_approved(@state, TOKEN_ID) == OTHER(), 'Approval before'); +// _mint // -// InternalImpl::_burn(ref state, TOKEN_ID); -// assert_event_transfer(OWNER(), ZERO(), TOKEN_ID); + +#[test] +#[available_gas(20000000)] +fn test__mint() { + let mut state = STATE(); + let recipient = RECIPIENT(); + let token_id = TOKEN_ID; + + assert_state_before_mint(recipient); + state.erc721._mint(recipient, TOKEN_ID); + assert_event_transfer(ZERO(), recipient, token_id); + + assert_state_after_mint(recipient, token_id); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: invalid receiver',))] +fn test__mint_to_zero() { + let mut state = STATE(); + state.erc721._mint(ZERO(), TOKEN_ID); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: token already minted',))] +fn test__mint_already_exist() { + let mut state = setup(); + state.erc721._mint(RECIPIENT(), TOKEN_ID); +} + // -// assert(state.ERC721_owners.read(TOKEN_ID) == ZERO(), 'Ownership after'); -// assert(ERC721Impl::balance_of(@state, OWNER()) == 0, 'Balance of owner after'); -// assert(state.ERC721_token_approvals.read(TOKEN_ID) == ZERO(), 'Approval after'); -//} +// _safe_mint // -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: invalid token ID',))] -//fn test__burn_nonexistent() { -// let mut state = STATE(); -// InternalImpl::_burn(ref state, TOKEN_ID); -//} + +#[test] +#[available_gas(20000000)] +fn test__safe_mint_to_receiver() { + let mut state = STATE(); + let recipient = setup_receiver(); + let token_id = TOKEN_ID; + + assert_state_before_mint(recipient); + state.erc721._safe_mint(recipient, token_id, DATA(true)); + assert_event_transfer(ZERO(), recipient, token_id); + + assert_state_after_mint(recipient, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test__safe_mint_to_receiver_camel() { + let mut state = STATE(); + let recipient = setup_camel_receiver(); + let token_id = TOKEN_ID; + + assert_state_before_mint(recipient); + state.erc721._safe_mint(recipient, token_id, DATA(true)); + assert_event_transfer(ZERO(), recipient, token_id); + + assert_state_after_mint(recipient, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test__safe_mint_to_account() { + let mut state = STATE(); + let account = setup_account(); + let token_id = TOKEN_ID; + + assert_state_before_mint(account); + state.erc721._safe_mint(account, token_id, DATA(true)); + assert_event_transfer(ZERO(), account, token_id); + + assert_state_after_mint(account, token_id); +} + +#[test] +#[available_gas(20000000)] +fn test__safe_mint_to_account_camel() { + let mut state = STATE(); + let account = setup_camel_account(); + let token_id = TOKEN_ID; + + assert_state_before_mint(account); + state.erc721._safe_mint(account, token_id, DATA(true)); + assert_event_transfer(ZERO(), account, token_id); + + assert_state_after_mint(account, token_id); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] +fn test__safe_mint_to_non_receiver() { + let mut state = STATE(); + let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); + let token_id = TOKEN_ID; + + assert_state_before_mint(recipient); + state.erc721._safe_mint(recipient, token_id, DATA(true)); + assert_state_after_mint(recipient, token_id); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: safe mint failed',))] +fn test__safe_mint_to_receiver_failure() { + let mut state = STATE(); + let recipient = setup_receiver(); + let token_id = TOKEN_ID; + + assert_state_before_mint(recipient); + state.erc721._safe_mint(recipient, token_id, DATA(false)); + assert_state_after_mint(recipient, token_id); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: safe mint failed',))] +fn test__safe_mint_to_receiver_failure_camel() { + let mut state = STATE(); + let recipient = setup_camel_receiver(); + let token_id = TOKEN_ID; + + assert_state_before_mint(recipient); + state.erc721._safe_mint(recipient, token_id, DATA(false)); + assert_state_after_mint(recipient, token_id); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: invalid receiver',))] +fn test__safe_mint_to_zero() { + let mut state = STATE(); + state.erc721._safe_mint(ZERO(), TOKEN_ID, DATA(true)); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: token already minted',))] +fn test__safe_mint_already_exist() { + let mut state = setup(); + state.erc721._safe_mint(RECIPIENT(), TOKEN_ID, DATA(true)); +} + // -//// -//// _set_token_uri -//// +// _burn // -//#[test] -//#[available_gas(20000000)] -//fn test__set_token_uri() { -// let mut state = setup(); + +#[test] +#[available_gas(20000000)] +fn test__burn() { + let mut state = setup(); + + state.erc721._approve(OTHER(), TOKEN_ID); + utils::drop_event(ZERO()); + + assert(state.erc721.owner_of(TOKEN_ID) == OWNER(), 'Ownership before'); + assert(state.erc721.balance_of(OWNER()) == 1, 'Balance of owner before'); + assert(state.erc721.get_approved(TOKEN_ID) == OTHER(), 'Approval before'); + + state.erc721._burn(TOKEN_ID); + assert_event_transfer(OWNER(), ZERO(), TOKEN_ID); + + assert(state.erc721.ERC721_owners.read(TOKEN_ID) == ZERO(), 'Ownership after'); + assert(state.erc721.balance_of(OWNER()) == 0, 'Balance of owner after'); + assert(state.erc721.ERC721_token_approvals.read(TOKEN_ID) == ZERO(), 'Approval after'); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: invalid token ID',))] +fn test__burn_nonexistent() { + let mut state = STATE(); + state.erc721._burn(TOKEN_ID); +} + // -// assert(ERC721MetadataImpl::token_uri(@state, TOKEN_ID) == 0, 'URI should be 0'); -// InternalImpl::_set_token_uri(ref state, TOKEN_ID, URI); -// assert(ERC721MetadataImpl::token_uri(@state, TOKEN_ID) == URI, 'URI should be set'); -//} +// _set_token_uri // -//#[test] -//#[available_gas(20000000)] -//#[should_panic(expected: ('ERC721: invalid token ID',))] -//fn test__set_token_uri_nonexistent() { -// let mut state = STATE(); -// InternalImpl::_set_token_uri(ref state, TOKEN_ID, URI); -//} + +#[test] +#[available_gas(20000000)] +fn test__set_token_uri() { + let mut state = setup(); + + assert(state.erc721.token_uri(TOKEN_ID) == 0, 'URI should be 0'); + state.erc721._set_token_uri(TOKEN_ID, URI); + assert(state.erc721.token_uri(TOKEN_ID) == URI, 'URI should be set'); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC721: invalid token ID',))] +fn test__set_token_uri_nonexistent() { + let mut state = STATE(); + state.erc721._set_token_uri(TOKEN_ID, URI); +} // // Helpers @@ -1338,35 +1338,35 @@ fn assert_state_before_transfer( owner: ContractAddress, recipient: ContractAddress, token_id: u256 ) { let state = STATE(); - assert(ERC721Impl::owner_of(@state, token_id) == owner, 'Ownership before'); - assert(ERC721Impl::balance_of(@state, owner) == 1, 'Balance of owner before'); - assert(ERC721Impl::balance_of(@state, recipient) == 0, 'Balance of recipient before'); + assert(state.erc721.owner_of(token_id) == owner, 'Ownership before'); + assert(state.erc721.balance_of(owner) == 1, 'Balance of owner before'); + assert(state.erc721.balance_of(recipient) == 0, 'Balance of recipient before'); } fn assert_state_after_transfer(owner: ContractAddress, recipient: ContractAddress, token_id: u256) { let state = STATE(); - assert(ERC721Impl::owner_of(@state, token_id) == recipient, 'Ownership after'); - assert(ERC721Impl::balance_of(@state, owner) == 0, 'Balance of owner after'); - assert(ERC721Impl::balance_of(@state, recipient) == 1, 'Balance of recipient after'); - assert(ERC721Impl::get_approved(@state, token_id) == ZERO(), 'Approval not implicitly reset'); + assert(state.erc721.owner_of(token_id) == recipient, 'Ownership after'); + assert(state.erc721.balance_of(owner) == 0, 'Balance of owner after'); + assert(state.erc721.balance_of(recipient) == 1, 'Balance of recipient after'); + assert(state.erc721.get_approved(token_id) == ZERO(), 'Approval not implicitly reset'); } fn assert_state_before_mint(recipient: ContractAddress) { let state = STATE(); - assert(ERC721Impl::balance_of(@state, recipient) == 0, 'Balance of recipient before'); + assert(state.erc721.balance_of(recipient) == 0, 'Balance of recipient before'); } fn assert_state_after_mint(recipient: ContractAddress, token_id: u256) { let state = STATE(); - assert(ERC721Impl::owner_of(@state, token_id) == recipient, 'Ownership after'); - assert(ERC721Impl::balance_of(@state, recipient) == 1, 'Balance of recipient after'); - assert(ERC721Impl::get_approved(@state, token_id) == ZERO(), 'Approval implicitly set'); + assert(state.erc721.owner_of(token_id) == recipient, 'Ownership after'); + assert(state.erc721.balance_of(recipient) == 1, 'Balance of recipient after'); + assert(state.erc721.get_approved(token_id) == ZERO(), 'Approval implicitly set'); } fn assert_event_approval_for_all( owner: ContractAddress, operator: ContractAddress, approved: bool ) { - let event = utils::pop_log::(ZERO()).unwrap(); + let event = utils::pop_log_comp_indexed::(ZERO()).unwrap(); assert(event.owner == owner, 'Invalid `owner`'); assert(event.operator == operator, 'Invalid `operator`'); assert(event.approved == approved, 'Invalid `approved`'); @@ -1380,7 +1380,7 @@ fn assert_event_approval_for_all( } fn assert_event_approval(owner: ContractAddress, approved: ContractAddress, token_id: u256) { - let event = utils::pop_log::(ZERO()).unwrap(); + let event = utils::pop_log_comp_indexed::(ZERO()).unwrap(); assert(event.owner == owner, 'Invalid `owner`'); assert(event.approved == approved, 'Invalid `approved`'); assert(event.token_id == token_id, 'Invalid `token_id`'); @@ -1395,7 +1395,7 @@ fn assert_event_approval(owner: ContractAddress, approved: ContractAddress, toke } fn assert_event_transfer(from: ContractAddress, to: ContractAddress, token_id: u256) { - let event = utils::pop_log::(ZERO()).unwrap(); + let event = utils::pop_log_comp_indexed::(ZERO()).unwrap(); assert(event.from == from, 'Invalid `from`'); assert(event.to == to, 'Invalid `to`'); assert(event.token_id == token_id, 'Invalid `token_id`'); From 18f5f23984671693c9e707ca0d7fda3cee8a5c4e Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 16 Oct 2023 13:27:38 -0400 Subject: [PATCH 192/246] fix formatting --- src/tests/mocks/erc721_mocks.cairo | 20 ++++++++-------- src/tests/token/test_dual721.cairo | 6 ++--- src/tests/token/test_erc721.cairo | 37 +++++++++++------------------- src/token/erc721/erc721.cairo | 35 +++++++++++++++++++--------- 4 files changed, 50 insertions(+), 48 deletions(-) diff --git a/src/tests/mocks/erc721_mocks.cairo b/src/tests/mocks/erc721_mocks.cairo index 313ec160b..8cb8f2fbb 100644 --- a/src/tests/mocks/erc721_mocks.cairo +++ b/src/tests/mocks/erc721_mocks.cairo @@ -1,7 +1,7 @@ #[starknet::contract] mod DualCaseERC721Mock { - use openzeppelin::token::erc721::ERC721 as erc721_component; use openzeppelin::introspection::src5::SRC5 as src5_component; + use openzeppelin::token::erc721::ERC721 as erc721_component; use starknet::ContractAddress; use starknet::get_caller_address; @@ -15,10 +15,10 @@ mod DualCaseERC721Mock { #[abi(embed_v0)] impl ERC721MetadataImpl = erc721_component::ERC721MetadataImpl; #[abi(embed_v0)] - impl ERC721CamelOnly = - erc721_component::ERC721CamelOnlyImpl; + impl ERC721CamelOnly = erc721_component::ERC721CamelOnlyImpl; #[abi(embed_v0)] - impl ERC721MetadataCamelOnly = erc721_component::ERC721MetadataCamelOnlyImpl; + impl ERC721MetadataCamelOnly = + erc721_component::ERC721MetadataCamelOnlyImpl; impl ERC721InternalImpl = erc721_component::InternalImpl; #[storage] @@ -48,8 +48,8 @@ mod DualCaseERC721Mock { #[starknet::contract] mod SnakeERC721Mock { - use openzeppelin::token::erc721::ERC721 as erc721_component; use openzeppelin::introspection::src5::SRC5 as src5_component; + use openzeppelin::token::erc721::ERC721 as erc721_component; use starknet::ContractAddress; use starknet::get_caller_address; @@ -91,9 +91,9 @@ mod SnakeERC721Mock { #[starknet::contract] mod CamelERC721Mock { - use openzeppelin::token::erc721::ERC721::{ERC721Impl, ERC721MetadataImpl}; - use openzeppelin::token::erc721::ERC721 as erc721_component; use openzeppelin::introspection::src5::SRC5 as src5_component; + use openzeppelin::token::erc721::ERC721 as erc721_component; + use openzeppelin::token::erc721::ERC721::{ERC721Impl, ERC721MetadataImpl}; use starknet::ContractAddress; use starknet::get_caller_address; @@ -101,12 +101,12 @@ mod CamelERC721Mock { component!(path: src5_component, storage: src5, event: SRC5Event); #[abi(embed_v0)] - impl ERC721CamelOnly = - erc721_component::ERC721CamelOnlyImpl; + impl ERC721CamelOnly = erc721_component::ERC721CamelOnlyImpl; #[abi(embed_v0)] impl SRC5Impl = src5_component::SRC5Impl; #[abi(embed_v0)] - impl ERC721MetadataCamelOnly = erc721_component::ERC721MetadataCamelOnlyImpl; + impl ERC721MetadataCamelOnly = + erc721_component::ERC721MetadataCamelOnlyImpl; impl InternalImpl = erc721_component::InternalImpl; #[storage] diff --git a/src/tests/token/test_dual721.cairo b/src/tests/token/test_dual721.cairo index 265ad1429..d11504b6d 100644 --- a/src/tests/token/test_dual721.cairo +++ b/src/tests/token/test_dual721.cairo @@ -1,19 +1,19 @@ +use openzeppelin::tests::mocks::erc721_mocks::SnakeERC721Mock; use openzeppelin::tests::mocks::erc721_mocks::{ CamelERC721Mock, CamelERC721PanicMock, SnakeERC721PanicMock }; use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; -use openzeppelin::tests::mocks::erc721_mocks::SnakeERC721Mock; use openzeppelin::tests::utils::constants::{ DATA, OWNER, RECIPIENT, SPENDER, OPERATOR, OTHER, NAME, SYMBOL, URI, TOKEN_ID }; use openzeppelin::tests::utils; use openzeppelin::token::erc721::dual721::DualCaseERC721; use openzeppelin::token::erc721::dual721::DualCaseERC721Trait; -use openzeppelin::token::erc721::interface::IERC721CamelOnlyDispatcher; -use openzeppelin::token::erc721::interface::IERC721CamelOnlyDispatcherTrait; use openzeppelin::token::erc721::interface::ERC721ABIDispatcher; use openzeppelin::token::erc721::interface::ERC721ABIDispatcherTrait; +use openzeppelin::token::erc721::interface::IERC721CamelOnlyDispatcher; +use openzeppelin::token::erc721::interface::IERC721CamelOnlyDispatcherTrait; use openzeppelin::token::erc721::interface::IERC721Dispatcher; use openzeppelin::token::erc721::interface::IERC721DispatcherTrait; use openzeppelin::token::erc721::interface::IERC721_ID; diff --git a/src/tests/token/test_erc721.cairo b/src/tests/token/test_erc721.cairo index e4e8c1046..d83be5fff 100644 --- a/src/tests/token/test_erc721.cairo +++ b/src/tests/token/test_erc721.cairo @@ -1,12 +1,12 @@ use integer::u256; use integer::u256_from_felt252; use openzeppelin::account::Account; -use openzeppelin::introspection; -use openzeppelin::introspection::src5; use openzeppelin::introspection::src5::SRC5::SRC5Impl; -use openzeppelin::tests::mocks::erc721_mocks::DualCaseERC721Mock; +use openzeppelin::introspection::src5; +use openzeppelin::introspection; use openzeppelin::tests::mocks::camel_account_mock::CamelAccountMock; use openzeppelin::tests::mocks::dual721_receiver_mocks::CamelERC721ReceiverMock; +use openzeppelin::tests::mocks::erc721_mocks::DualCaseERC721Mock; use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils::constants::{ @@ -73,11 +73,10 @@ fn test_initialize() { assert(state.erc721.symbol() == SYMBOL, 'Symbol should be SYMBOL'); assert(state.erc721.balance_of(OWNER()) == 0, 'Balance should be zero'); + assert(state.src5.supports_interface(erc721::interface::IERC721_ID), 'Missing interface ID'); assert( - state.src5.supports_interface(erc721::interface::IERC721_ID), 'Missing interface ID' - ); - assert( - state.src5.supports_interface(erc721::interface::IERC721_METADATA_ID), 'Missing interface ID' + state.src5.supports_interface(erc721::interface::IERC721_METADATA_ID), + 'Missing interface ID' ); assert( state.src5.supports_interface(introspection::interface::ISRC5_ID), 'Missing interface ID' @@ -183,9 +182,7 @@ fn test_approve_from_owner() { state.erc721.approve(SPENDER(), TOKEN_ID); assert_event_approval(OWNER(), SPENDER(), TOKEN_ID); - assert( - state.erc721.get_approved(TOKEN_ID) == SPENDER(), 'Spender not approved correctly' - ); + assert(state.erc721.get_approved(TOKEN_ID) == SPENDER(), 'Spender not approved correctly'); } #[test] @@ -201,9 +198,7 @@ fn test_approve_from_operator() { state.erc721.approve(SPENDER(), TOKEN_ID); assert_event_approval(OWNER(), SPENDER(), TOKEN_ID); - assert( - state.erc721.get_approved(TOKEN_ID) == SPENDER(), 'Spender not approved correctly' - ); + assert(state.erc721.get_approved(TOKEN_ID) == SPENDER(), 'Spender not approved correctly'); } #[test] @@ -241,9 +236,7 @@ fn test__approve() { state.erc721._approve(SPENDER(), TOKEN_ID); assert_event_approval(OWNER(), SPENDER(), TOKEN_ID); - assert( - state.erc721.get_approved(TOKEN_ID) == SPENDER(), 'Spender not approved correctly' - ); + assert(state.erc721.get_approved(TOKEN_ID) == SPENDER(), 'Spender not approved correctly'); } #[test] @@ -278,16 +271,14 @@ fn test_set_approval_for_all() { assert_event_approval_for_all(OWNER(), OPERATOR(), true); assert( - state.erc721.is_approved_for_all(OWNER(), OPERATOR()), - 'Operator not approved correctly' + state.erc721.is_approved_for_all(OWNER(), OPERATOR()), 'Operator not approved correctly' ); state.erc721.set_approval_for_all(OPERATOR(), false); assert_event_approval_for_all(OWNER(), OPERATOR(), false); assert( - !state.erc721.is_approved_for_all(OWNER(), OPERATOR()), - 'Approval not revoked correctly' + !state.erc721.is_approved_for_all(OWNER(), OPERATOR()), 'Approval not revoked correctly' ); } @@ -319,16 +310,14 @@ fn test__set_approval_for_all() { assert_event_approval_for_all(OWNER(), OPERATOR(), true); assert( - state.erc721.is_approved_for_all(OWNER(), OPERATOR()), - 'Operator not approved correctly' + state.erc721.is_approved_for_all(OWNER(), OPERATOR()), 'Operator not approved correctly' ); state.erc721._set_approval_for_all(OWNER(), OPERATOR(), false); assert_event_approval_for_all(OWNER(), OPERATOR(), false); assert( - !state.erc721.is_approved_for_all(OWNER(), OPERATOR()), - 'Operator not approved correctly' + !state.erc721.is_approved_for_all(OWNER(), OPERATOR()), 'Operator not approved correctly' ); } diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 5e126d520..2d7dd40b1 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -111,8 +111,7 @@ mod ERC721 { let caller = get_caller_address(); assert( - owner == caller || self.is_approved_for_all(owner, caller), - Errors::UNAUTHORIZED + owner == caller || self.is_approved_for_all(owner, caller), Errors::UNAUTHORIZED ); self._approve(to, token_id); } @@ -124,7 +123,10 @@ mod ERC721 { } fn transfer_from( - ref self: ComponentState, from: ContractAddress, to: ContractAddress, token_id: u256 + ref self: ComponentState, + from: ContractAddress, + to: ContractAddress, + token_id: u256 ) { assert( self._is_approved_or_owner(get_caller_address(), token_id), Errors::UNAUTHORIZED @@ -192,12 +194,17 @@ mod ERC721 { self.is_approved_for_all(owner, operator) } - fn setApprovalForAll(ref self: ComponentState, operator: ContractAddress, approved: bool) { + fn setApprovalForAll( + ref self: ComponentState, operator: ContractAddress, approved: bool + ) { self.set_approval_for_all(operator, approved) } fn transferFrom( - ref self: ComponentState, from: ContractAddress, to: ContractAddress, tokenId: u256 + ref self: ComponentState, + from: ContractAddress, + to: ContractAddress, + tokenId: u256 ) { self.transfer_from(from, to, tokenId) } @@ -266,9 +273,7 @@ mod ERC721 { ) -> bool { let owner = self._owner_of(token_id); let is_approved_for_all = self.is_approved_for_all(owner, spender); - owner == spender - || is_approved_for_all - || spender == self.get_approved(token_id) + owner == spender || is_approved_for_all || spender == self.get_approved(token_id) } fn _approve(ref self: ComponentState, to: ContractAddress, token_id: u256) { @@ -301,7 +306,10 @@ mod ERC721 { } fn _transfer( - ref self: ComponentState, from: ContractAddress, to: ContractAddress, token_id: u256 + ref self: ComponentState, + from: ContractAddress, + to: ContractAddress, + token_id: u256 ) { assert(!to.is_zero(), Errors::INVALID_RECEIVER); let owner = self._owner_of(token_id); @@ -330,7 +338,10 @@ mod ERC721 { } fn _safe_mint( - ref self: ComponentState, to: ContractAddress, token_id: u256, data: Span + ref self: ComponentState, + to: ContractAddress, + token_id: u256, + data: Span ) { self._mint(to, token_id); assert( @@ -352,7 +363,9 @@ mod ERC721 { ); } - fn _set_token_uri(ref self: ComponentState, token_id: u256, token_uri: felt252) { + fn _set_token_uri( + ref self: ComponentState, token_id: u256, token_uri: felt252 + ) { assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); self.ERC721_token_uri.write(token_id, token_uri) } From 3b2259bb434264eb56d24ce82dac33fb66b8577b Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 16 Oct 2023 16:59:47 -0400 Subject: [PATCH 193/246] add erc721camelabi --- src/tests/token/test_dual721.cairo | 14 ++++++-------- src/token/erc721/interface.cairo | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/tests/token/test_dual721.cairo b/src/tests/token/test_dual721.cairo index d11504b6d..07e7f6ab4 100644 --- a/src/tests/token/test_dual721.cairo +++ b/src/tests/token/test_dual721.cairo @@ -12,10 +12,8 @@ use openzeppelin::token::erc721::dual721::DualCaseERC721; use openzeppelin::token::erc721::dual721::DualCaseERC721Trait; use openzeppelin::token::erc721::interface::ERC721ABIDispatcher; use openzeppelin::token::erc721::interface::ERC721ABIDispatcherTrait; -use openzeppelin::token::erc721::interface::IERC721CamelOnlyDispatcher; -use openzeppelin::token::erc721::interface::IERC721CamelOnlyDispatcherTrait; -use openzeppelin::token::erc721::interface::IERC721Dispatcher; -use openzeppelin::token::erc721::interface::IERC721DispatcherTrait; +use openzeppelin::token::erc721::interface::ERC721CamelABIDispatcher; +use openzeppelin::token::erc721::interface::ERC721CamelABIDispatcherTrait; use openzeppelin::token::erc721::interface::IERC721_ID; use openzeppelin::utils::serde::SerializedAppend; use starknet::ContractAddress; @@ -26,7 +24,7 @@ use starknet::testing::set_contract_address; // Setup // -fn setup_snake() -> (DualCaseERC721, IERC721Dispatcher) { +fn setup_snake() -> (DualCaseERC721, ERC721ABIDispatcher) { let mut calldata = array![]; calldata.append_serde(NAME); calldata.append_serde(SYMBOL); @@ -34,10 +32,10 @@ fn setup_snake() -> (DualCaseERC721, IERC721Dispatcher) { calldata.append_serde(URI); set_contract_address(OWNER()); let target = utils::deploy(SnakeERC721Mock::TEST_CLASS_HASH, calldata); - (DualCaseERC721 { contract_address: target }, IERC721Dispatcher { contract_address: target }) + (DualCaseERC721 { contract_address: target }, ERC721ABIDispatcher { contract_address: target }) } -fn setup_camel() -> (DualCaseERC721, IERC721CamelOnlyDispatcher) { +fn setup_camel() -> (DualCaseERC721, ERC721CamelABIDispatcher) { let mut calldata = array![]; calldata.append_serde(NAME); calldata.append_serde(SYMBOL); @@ -47,7 +45,7 @@ fn setup_camel() -> (DualCaseERC721, IERC721CamelOnlyDispatcher) { let target = utils::deploy(CamelERC721Mock::TEST_CLASS_HASH, calldata); ( DualCaseERC721 { contract_address: target }, - IERC721CamelOnlyDispatcher { contract_address: target } + ERC721CamelABIDispatcher { contract_address: target } ) } diff --git a/src/token/erc721/interface.cairo b/src/token/erc721/interface.cairo index f9436fdbc..ceb1dc8f5 100644 --- a/src/token/erc721/interface.cairo +++ b/src/token/erc721/interface.cairo @@ -141,3 +141,27 @@ trait ERC721ABI { // IERC721MetadataCamelOnly fn tokenURI(self: @TState, tokenId: u256) -> felt252; } + +#[starknet::interface] +trait ERC721CamelABI { + fn balanceOf(self: @TState, account: ContractAddress) -> u256; + fn ownerOf(self: @TState, tokenId: u256) -> ContractAddress; + fn safeTransferFrom( + ref self: TState, + from: ContractAddress, + to: ContractAddress, + tokenId: u256, + data: Span + ); + fn transferFrom(ref self: TState, from: ContractAddress, to: ContractAddress, tokenId: u256); + fn approve(ref self: TState, to: ContractAddress, token_id: u256); + fn setApprovalForAll(ref self: TState, operator: ContractAddress, approved: bool); + fn getApproved(self: @TState, tokenId: u256) -> ContractAddress; + fn isApprovedForAll(self: @TState, owner: ContractAddress, operator: ContractAddress) -> bool; + + fn supportsInterface(self: @TState, interfaceId: felt252) -> bool; + + fn name(self: @TState) -> felt252; + fn symbol(self: @TState) -> felt252; + fn tokenURI(self: @TState, tokenId: u256) -> felt252; +} From 4396dccb3bf14969acd0a0b42505acc8a31b57e9 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 17 Oct 2023 10:40:15 -0400 Subject: [PATCH 194/246] remove unnecessary trait --- src/tests/token/test_dual721.cairo | 16 ++++++++-------- src/token/erc721/interface.cairo | 24 ------------------------ 2 files changed, 8 insertions(+), 32 deletions(-) diff --git a/src/tests/token/test_dual721.cairo b/src/tests/token/test_dual721.cairo index 07e7f6ab4..c93835300 100644 --- a/src/tests/token/test_dual721.cairo +++ b/src/tests/token/test_dual721.cairo @@ -10,10 +10,10 @@ use openzeppelin::tests::utils::constants::{ use openzeppelin::tests::utils; use openzeppelin::token::erc721::dual721::DualCaseERC721; use openzeppelin::token::erc721::dual721::DualCaseERC721Trait; -use openzeppelin::token::erc721::interface::ERC721ABIDispatcher; -use openzeppelin::token::erc721::interface::ERC721ABIDispatcherTrait; -use openzeppelin::token::erc721::interface::ERC721CamelABIDispatcher; -use openzeppelin::token::erc721::interface::ERC721CamelABIDispatcherTrait; +use openzeppelin::token::erc721::interface::IERC721CamelOnlyDispatcher; +use openzeppelin::token::erc721::interface::IERC721CamelOnlyDispatcherTrait; +use openzeppelin::token::erc721::interface::IERC721Dispatcher; +use openzeppelin::token::erc721::interface::IERC721DispatcherTrait; use openzeppelin::token::erc721::interface::IERC721_ID; use openzeppelin::utils::serde::SerializedAppend; use starknet::ContractAddress; @@ -24,7 +24,7 @@ use starknet::testing::set_contract_address; // Setup // -fn setup_snake() -> (DualCaseERC721, ERC721ABIDispatcher) { +fn setup_snake() -> (DualCaseERC721, IERC721Dispatcher) { let mut calldata = array![]; calldata.append_serde(NAME); calldata.append_serde(SYMBOL); @@ -32,10 +32,10 @@ fn setup_snake() -> (DualCaseERC721, ERC721ABIDispatcher) { calldata.append_serde(URI); set_contract_address(OWNER()); let target = utils::deploy(SnakeERC721Mock::TEST_CLASS_HASH, calldata); - (DualCaseERC721 { contract_address: target }, ERC721ABIDispatcher { contract_address: target }) + (DualCaseERC721 { contract_address: target }, IERC721Dispatcher { contract_address: target }) } -fn setup_camel() -> (DualCaseERC721, ERC721CamelABIDispatcher) { +fn setup_camel() -> (DualCaseERC721, IERC721CamelOnlyDispatcher) { let mut calldata = array![]; calldata.append_serde(NAME); calldata.append_serde(SYMBOL); @@ -45,7 +45,7 @@ fn setup_camel() -> (DualCaseERC721, ERC721CamelABIDispatcher) { let target = utils::deploy(CamelERC721Mock::TEST_CLASS_HASH, calldata); ( DualCaseERC721 { contract_address: target }, - ERC721CamelABIDispatcher { contract_address: target } + IERC721CamelOnlyDispatcher { contract_address: target } ) } diff --git a/src/token/erc721/interface.cairo b/src/token/erc721/interface.cairo index ceb1dc8f5..f9436fdbc 100644 --- a/src/token/erc721/interface.cairo +++ b/src/token/erc721/interface.cairo @@ -141,27 +141,3 @@ trait ERC721ABI { // IERC721MetadataCamelOnly fn tokenURI(self: @TState, tokenId: u256) -> felt252; } - -#[starknet::interface] -trait ERC721CamelABI { - fn balanceOf(self: @TState, account: ContractAddress) -> u256; - fn ownerOf(self: @TState, tokenId: u256) -> ContractAddress; - fn safeTransferFrom( - ref self: TState, - from: ContractAddress, - to: ContractAddress, - tokenId: u256, - data: Span - ); - fn transferFrom(ref self: TState, from: ContractAddress, to: ContractAddress, tokenId: u256); - fn approve(ref self: TState, to: ContractAddress, token_id: u256); - fn setApprovalForAll(ref self: TState, operator: ContractAddress, approved: bool); - fn getApproved(self: @TState, tokenId: u256) -> ContractAddress; - fn isApprovedForAll(self: @TState, owner: ContractAddress, operator: ContractAddress) -> bool; - - fn supportsInterface(self: @TState, interfaceId: felt252) -> bool; - - fn name(self: @TState) -> felt252; - fn symbol(self: @TState) -> felt252; - fn tokenURI(self: @TState, tokenId: u256) -> felt252; -} From 5cbb3b11369430a5a7aacb7a5b214c2459d9cdba Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 17 Oct 2023 16:26:38 -0400 Subject: [PATCH 195/246] remove code comments --- src/token/erc721/erc721.cairo | 157 ---------------------------------- 1 file changed, 157 deletions(-) diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index c8f6f7b73..baa9765cd 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -1,10 +1,6 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/erc721.cairo) -/// # ERC721 Contract and Implementation -/// -/// This ERC721 contract includes both a library and a basic preset implementation -/// which includes the IERC721Metadata implementation. #[starknet::contract] mod ERC721 { use openzeppelin::account; @@ -46,7 +42,6 @@ mod ERC721 { SRC5Event: src5_component::Event } - /// Emitted when `token_id` token is transferred from `from` to `to`. #[derive(Drop, starknet::Event)] struct Transfer { #[key] @@ -57,7 +52,6 @@ mod ERC721 { token_id: u256 } - /// Emitted when `owner` enables `approved` to manage the `token_id` token. #[derive(Drop, starknet::Event)] struct Approval { #[key] @@ -68,8 +62,6 @@ mod ERC721 { token_id: u256 } - /// Emitted when `owner` enables or disables (`approved`) `operator` to manage - /// all of its assets. #[derive(Drop, starknet::Event)] struct ApprovalForAll { #[key] @@ -92,8 +84,6 @@ mod ERC721 { const SAFE_TRANSFER_FAILED: felt252 = 'ERC721: safe transfer failed'; } - /// Initializes the state of the ERC721 contract. This includes setting both the - /// NFT name and symbol as well as minting `token_id` to `recipient`. #[constructor] fn constructor( ref self: ContractState, @@ -112,26 +102,20 @@ mod ERC721 { #[external(v0)] impl ERC721MetadataImpl of interface::IERC721Metadata { - /// Returns the NFT name. fn name(self: @ContractState) -> felt252 { self.ERC721_name.read() } - /// Returns the NFT symbol. fn symbol(self: @ContractState) -> felt252 { self.ERC721_symbol.read() } - /// Returns the Uniform Resource Identifier (URI) for the `token_id` token. - /// - /// If the URI is not set for the `token_id`, the return value will be `0`. fn token_uri(self: @ContractState, token_id: u256) -> felt252 { assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); self.ERC721_token_uri.read(token_id) } } - /// Adds camelCase support for `IERC721Metadata`. #[external(v0)] impl ERC721MetadataCamelOnlyImpl of interface::IERC721MetadataCamelOnly { fn tokenURI(self: @ContractState, tokenId: u256) -> felt252 { @@ -142,47 +126,26 @@ mod ERC721 { #[external(v0)] impl ERC721Impl of interface::IERC721 { - /// Returns the number of NFTs owned by `account`. fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { assert(!account.is_zero(), Errors::INVALID_ACCOUNT); self.ERC721_balances.read(account) } - /// Returns the owner address of `token_id`. - /// - /// Requirements: - /// - /// - `token_id` exists. fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress { self._owner_of(token_id) } - /// Returns the address approved for `token_id`. - /// - /// Requirements: - /// - /// - `token_id` exists. fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); self.ERC721_token_approvals.read(token_id) } - /// Query if `operator` is an authorized operator for `owner`. fn is_approved_for_all( self: @ContractState, owner: ContractAddress, operator: ContractAddress ) -> bool { self.ERC721_operator_approvals.read((owner, operator)) } - /// Change or reaffirm the approved address for an NFT. - /// - /// Requirements: - /// - /// - The caller is either an approved operator or the `token_id` owner. - /// - `to` cannot be the token owner zero address. - /// - `token_id` exists. - /// - /// Emits an `Approval` event. fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) { let owner = self._owner_of(token_id); @@ -194,30 +157,12 @@ mod ERC721 { self._approve(to, token_id); } - /// Enable or disable approval for `operator` to manage all of the - /// caller's assets. - /// - /// Requirements: - /// - /// - `operator` cannot be the caller. - /// - /// Emits an `Approval` event. fn set_approval_for_all( ref self: ContractState, operator: ContractAddress, approved: bool ) { self._set_approval_for_all(get_caller_address(), operator, approved) } - /// Transfer ownership of `token_id` from `from` to `to`. - /// - /// Requirements: - /// - /// - Caller is either approved or the `token_id` owner. - /// - `to` is not the zero address. - /// - `from` is not the zero address. - /// - `token_id` exists. - /// - /// Emits a `Transfer` event. fn transfer_from( ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 ) { @@ -227,17 +172,6 @@ mod ERC721 { self._transfer(from, to, token_id); } - /// Safely transfer ownership of `token_id` from `from` to `to`. - /// - /// Requirements: - /// - /// - Caller is either approved or the `token_id` owner. - /// - `to` is not the zero address. - /// - `from` is not the zero address. - /// - `token_id` exists. - /// - `to` is either an account contract or supports the `IERC721Receiver` interface. - /// - /// Emits a `Transfer` event. fn safe_transfer_from( ref self: ContractState, from: ContractAddress, @@ -252,7 +186,6 @@ mod ERC721 { } } - /// Adds camelCase support for `IERC721`. #[external(v0)] impl ERC721CamelOnlyImpl of interface::IERC721CamelOnly { fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 { @@ -300,8 +233,6 @@ mod ERC721 { #[generate_trait] impl InternalImpl of InternalTrait { - /// Initializes the contract by setting the token name and symbol. - /// This should be used inside the contract's constructor. fn initializer(ref self: ContractState, name: felt252, symbol: felt252) { self.ERC721_name.write(name); self.ERC721_symbol.write(symbol); @@ -310,11 +241,6 @@ mod ERC721 { self.src5.register_interface(interface::IERC721_METADATA_ID); } - /// Returns the owner address of `token_id`. - /// - /// Requirements: - /// - /// - `token_id` exists. fn _owner_of(self: @ContractState, token_id: u256) -> ContractAddress { let owner = self.ERC721_owners.read(token_id); match owner.is_zero() { @@ -323,16 +249,10 @@ mod ERC721 { } } - /// Returns whether `token_id` exists. fn _exists(self: @ContractState, token_id: u256) -> bool { !self.ERC721_owners.read(token_id).is_zero() } - /// Returns whether `spender` is allowed to manage `token_id`. - /// - /// Requirements: - /// - /// - `token_id` exists. fn _is_approved_or_owner( self: @ContractState, spender: ContractAddress, token_id: u256 ) -> bool { @@ -343,16 +263,6 @@ mod ERC721 { || spender == ERC721Impl::get_approved(self, token_id) } - /// Changes or reaffirms the approved address for an NFT. - /// - /// Internal function without access restriction. - /// - /// Requirements: - /// - /// - `token_id` exists. - /// - `to` is not the current token owner. - /// - /// Emits an `Approval` event. fn _approve(ref self: ContractState, to: ContractAddress, token_id: u256) { let owner = self._owner_of(token_id); assert(owner != to, Errors::APPROVAL_TO_OWNER); @@ -361,14 +271,6 @@ mod ERC721 { self.emit(Approval { owner, approved: to, token_id }); } - /// Enables or disables approval for `operator` to manage - /// all of the `owner` assets. - /// - /// Requirements: - /// - /// - `operator` cannot be the caller. - /// - /// Emits an `Approval` event. fn _set_approval_for_all( ref self: ContractState, owner: ContractAddress, @@ -380,11 +282,6 @@ mod ERC721 { self.emit(ApprovalForAll { owner, operator, approved }); } - /// Mints `token_id` and transfers it to `to`. - /// - /// Internal function without access restriction. - /// - /// Emits a `Transfer` event. fn _mint(ref self: ContractState, to: ContractAddress, token_id: u256) { assert(!to.is_zero(), Errors::INVALID_RECEIVER); assert(!self._exists(token_id), Errors::ALREADY_MINTED); @@ -395,17 +292,6 @@ mod ERC721 { self.emit(Transfer { from: Zeroable::zero(), to, token_id }); } - /// Transfers `token_id` from `from` to `to`. - /// - /// Internal function without access restriction. - /// - /// Requirements: - /// - /// - `to` is not the zero address. - /// - `from` is the token owner. - /// - `token_id` exists. - /// - /// Emits a `Transfer` event. fn _transfer( ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 ) { @@ -423,17 +309,6 @@ mod ERC721 { self.emit(Transfer { from, to, token_id }); } - /// Internal function that destroys `token_id`. The approval is cleared when - /// the token is burned. - /// - /// This internal function does not check if the sender is authorized - /// to operate on the token. - /// - /// Requirements: - /// - /// - `token_id` exists. - /// - /// Emits a `Transfer` event. fn _burn(ref self: ContractState, token_id: u256) { let owner = self._owner_of(token_id); @@ -446,17 +321,6 @@ mod ERC721 { self.emit(Transfer { from: owner, to: Zeroable::zero(), token_id }); } - /// Internal function that safely mints `token_id` and transfers it to `to`. - /// - /// If `to` is not an account contract, `to` must support IERC721Receiver; - /// otherwise, the transaction will fail. - /// - /// Requirements: - /// - /// - `token_id` exists. - /// - `to` is either an account contract or supports the `IERC721Receiver` interface. - /// - /// Emits a `Transfer` event. fn _safe_mint( ref self: ContractState, to: ContractAddress, token_id: u256, data: Span ) { @@ -467,19 +331,6 @@ mod ERC721 { ); } - /// Internal function that safely transfers `token_id` token from `from` to `to`. - /// - /// If `to` is not an account contract, `to` must support IERC721Receiver; - /// otherwise, the transaction will fail. - /// - /// Requirements: - /// - /// - `to` cannot be the zero address. - /// - `from` must be the token owner. - /// - `token_id` exists. - /// - `to` is either an account contract or supports the `IERC721Receiver` interface. - /// - /// Emits a `Transfer` event. fn _safe_transfer( ref self: ContractState, from: ContractAddress, @@ -493,20 +344,12 @@ mod ERC721 { ); } - /// Sets the `token_uri` of `token_id`. - /// - /// Requirements: - /// - /// - `token_id` exists. fn _set_token_uri(ref self: ContractState, token_id: u256, token_uri: felt252) { assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); self.ERC721_token_uri.write(token_id, token_uri) } } - /// Internal function that checks if `to` either is an account contract or - /// has registered support for the `IERC721Receiver` interface through SRC-5. - /// The transaction will fail if both cases are false. fn _check_on_erc721_received( from: ContractAddress, to: ContractAddress, token_id: u256, data: Span ) -> bool { From c820965122e9a4be4a95ee956138820211270a06 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 17 Oct 2023 18:44:46 -0400 Subject: [PATCH 196/246] add code comments --- src/token/erc721/erc721.cairo | 154 ++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 2d7dd40b1..6c54dd6f2 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -1,6 +1,10 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.7.0 (token/erc721/erc721.cairo) +/// # ERC721 Component +/// +/// This ERC721 component provides implementations for both the IERC721 interface +/// and the IERC721Metadata interface. #[starknet::component] mod ERC721 { use openzeppelin::account; @@ -33,6 +37,7 @@ mod ERC721 { ApprovalForAll: ApprovalForAll, } + /// Emitted when `token_id` token is transferred from `from` to `to`. #[derive(Drop, starknet::Event)] struct Transfer { #[key] @@ -43,6 +48,7 @@ mod ERC721 { token_id: u256 } + /// Emitted when `owner` enables `approved` to manage the `token_id` token. #[derive(Drop, starknet::Event)] struct Approval { #[key] @@ -53,6 +59,8 @@ mod ERC721 { token_id: u256 } + /// Emitted when `owner` enables or disables (`approved`) `operator` to manage + /// all of its assets. #[derive(Drop, starknet::Event)] struct ApprovalForAll { #[key] @@ -86,26 +94,47 @@ mod ERC721 { +SRC5::HasComponent, +Drop > of interface::IERC721> { + /// Returns the number of NFTs owned by `account`. fn balance_of(self: @ComponentState, account: ContractAddress) -> u256 { assert(!account.is_zero(), Errors::INVALID_ACCOUNT); self.ERC721_balances.read(account) } + /// Returns the owner address of `token_id`. + /// + /// Requirements: + /// + /// - `token_id` exists. fn owner_of(self: @ComponentState, token_id: u256) -> ContractAddress { self._owner_of(token_id) } + /// Returns the address approved for `token_id`. + /// + /// Requirements: + /// + /// - `token_id` exists. fn get_approved(self: @ComponentState, token_id: u256) -> ContractAddress { assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); self.ERC721_token_approvals.read(token_id) } + /// Query if `operator` is an authorized operator for `owner`. fn is_approved_for_all( self: @ComponentState, owner: ContractAddress, operator: ContractAddress ) -> bool { self.ERC721_operator_approvals.read((owner, operator)) } + /// Change or reaffirm the approved address for an NFT. + /// + /// Requirements: + /// + /// - The caller is either an approved operator or the `token_id` owner. + /// - `to` cannot be the token owner zero address. + /// - `token_id` exists. + /// + /// Emits an `Approval` event. fn approve(ref self: ComponentState, to: ContractAddress, token_id: u256) { let owner = self._owner_of(token_id); @@ -116,12 +145,30 @@ mod ERC721 { self._approve(to, token_id); } + /// Enable or disable approval for `operator` to manage all of the + /// caller's assets. + /// + /// Requirements: + /// + /// - `operator` cannot be the caller. + /// + /// Emits an `Approval` event. fn set_approval_for_all( ref self: ComponentState, operator: ContractAddress, approved: bool ) { self._set_approval_for_all(get_caller_address(), operator, approved) } + /// Transfer ownership of `token_id` from `from` to `to`. + /// + /// Requirements: + /// + /// - Caller is either approved or the `token_id` owner. + /// - `to` is not the zero address. + /// - `from` is not the zero address. + /// - `token_id` exists. + /// + /// Emits a `Transfer` event. fn transfer_from( ref self: ComponentState, from: ContractAddress, @@ -134,6 +181,17 @@ mod ERC721 { self._transfer(from, to, token_id); } + /// Safely transfer ownership of `token_id` from `from` to `to`. + /// + /// Requirements: + /// + /// - Caller is either approved or the `token_id` owner. + /// - `to` is not the zero address. + /// - `from` is not the zero address. + /// - `token_id` exists. + /// - `to` is either an account contract or supports the `IERC721Receiver` interface. + /// + /// Emits a `Transfer` event. fn safe_transfer_from( ref self: ComponentState, from: ContractAddress, @@ -155,20 +213,26 @@ mod ERC721 { +SRC5::HasComponent, +Drop > of interface::IERC721Metadata> { + /// Returns the NFT name. fn name(self: @ComponentState) -> felt252 { self.ERC721_name.read() } + /// Returns the NFT symbol. fn symbol(self: @ComponentState) -> felt252 { self.ERC721_symbol.read() } + /// Returns the Uniform Resource Identifier (URI) for the `token_id` token. + /// + /// If the URI is not set for the `token_id`, the return value will be `0`. fn token_uri(self: @ComponentState, token_id: u256) -> felt252 { assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); self.ERC721_token_uri.read(token_id) } } + /// Adds camelCase support for `IERC721`. #[embeddable_as(ERC721CamelOnlyImpl)] impl ERC721CamelOnly< TContractState, @@ -220,6 +284,7 @@ mod ERC721 { } } + /// Adds camelCase support for `IERC721Metadata`. #[embeddable_as(ERC721MetadataCamelOnlyImpl)] impl ERC721MetadataCamelOnly< TContractState, @@ -244,6 +309,8 @@ mod ERC721 { +SRC5::HasComponent, +Drop > of InternalTrait { + /// Initializes the contract by setting the token name and symbol. + /// This should be used inside the contract's constructor. fn initializer(ref self: ComponentState, name: felt252, symbol: felt252) { self.ERC721_name.write(name); self.ERC721_symbol.write(symbol); @@ -256,6 +323,11 @@ mod ERC721 { src5_component.register_interface(interface::IERC721_METADATA_ID); } + /// Returns the owner address of `token_id`. + /// + /// Requirements: + /// + /// - `token_id` exists. fn _owner_of(self: @ComponentState, token_id: u256) -> ContractAddress { let owner = self.ERC721_owners.read(token_id); match owner.is_zero() { @@ -264,10 +336,16 @@ mod ERC721 { } } + /// Returns whether `token_id` exists. fn _exists(self: @ComponentState, token_id: u256) -> bool { !self.ERC721_owners.read(token_id).is_zero() } + /// Returns whether `spender` is allowed to manage `token_id`. + /// + /// Requirements: + /// + /// - `token_id` exists. fn _is_approved_or_owner( self: @ComponentState, spender: ContractAddress, token_id: u256 ) -> bool { @@ -276,6 +354,16 @@ mod ERC721 { owner == spender || is_approved_for_all || spender == self.get_approved(token_id) } + /// Changes or reaffirms the approved address for an NFT. + /// + /// Internal function without access restriction. + /// + /// Requirements: + /// + /// - `token_id` exists. + /// - `to` is not the current token owner. + /// + /// Emits an `Approval` event. fn _approve(ref self: ComponentState, to: ContractAddress, token_id: u256) { let owner = self._owner_of(token_id); assert(owner != to, Errors::APPROVAL_TO_OWNER); @@ -284,6 +372,14 @@ mod ERC721 { self.emit(Approval { owner, approved: to, token_id }); } + /// Enables or disables approval for `operator` to manage + /// all of the `owner` assets. + /// + /// Requirements: + /// + /// - `operator` cannot be the caller. + /// + /// Emits an `Approval` event. fn _set_approval_for_all( ref self: ComponentState, owner: ContractAddress, @@ -295,6 +391,11 @@ mod ERC721 { self.emit(ApprovalForAll { owner, operator, approved }); } + /// Mints `token_id` and transfers it to `to`. + /// + /// Internal function without access restriction. + /// + /// Emits a `Transfer` event. fn _mint(ref self: ComponentState, to: ContractAddress, token_id: u256) { assert(!to.is_zero(), Errors::INVALID_RECEIVER); assert(!self._exists(token_id), Errors::ALREADY_MINTED); @@ -305,6 +406,17 @@ mod ERC721 { self.emit(Transfer { from: Zeroable::zero(), to, token_id }); } + /// Transfers `token_id` from `from` to `to`. + /// + /// Internal function without access restriction. + /// + /// Requirements: + /// + /// - `to` is not the zero address. + /// - `from` is the token owner. + /// - `token_id` exists. + /// + /// Emits a `Transfer` event. fn _transfer( ref self: ComponentState, from: ContractAddress, @@ -325,6 +437,16 @@ mod ERC721 { self.emit(Transfer { from, to, token_id }); } + /// Destroys `token_id`. The approval is cleared when the token is burned. + /// + /// This internal function does not check if the sender is authorized + /// to operate on the token. + /// + /// Requirements: + /// + /// - `token_id` exists. + /// + /// Emits a `Transfer` event. fn _burn(ref self: ComponentState, token_id: u256) { let owner = self._owner_of(token_id); @@ -337,6 +459,17 @@ mod ERC721 { self.emit(Transfer { from: owner, to: Zeroable::zero(), token_id }); } + /// Safely mints `token_id` and transfers it to `to`. + /// + /// If `to` is not an account contract, `to` must support IERC721Receiver; + /// otherwise, the transaction will fail. + /// + /// Requirements: + /// + /// - `token_id` exists. + /// - `to` is either an account contract or supports the `IERC721Receiver` interface. + /// + /// Emits a `Transfer` event. fn _safe_mint( ref self: ComponentState, to: ContractAddress, @@ -350,6 +483,19 @@ mod ERC721 { ); } + /// Safely transfers `token_id` token from `from` to `to`. + /// + /// If `to` is not an account contract, `to` must support IERC721Receiver; + /// otherwise, the transaction will fail. + /// + /// Requirements: + /// + /// - `to` cannot be the zero address. + /// - `from` must be the token owner. + /// - `token_id` exists. + /// - `to` is either an account contract or supports the `IERC721Receiver` interface. + /// + /// Emits a `Transfer` event. fn _safe_transfer( ref self: ComponentState, from: ContractAddress, @@ -363,6 +509,11 @@ mod ERC721 { ); } + /// Sets the `token_uri` of `token_id`. + /// + /// Requirements: + /// + /// - `token_id` exists. fn _set_token_uri( ref self: ComponentState, token_id: u256, token_uri: felt252 ) { @@ -371,6 +522,9 @@ mod ERC721 { } } + /// Checks if `to` either is an account contract or has registered support + /// for the `IERC721Receiver` interface through SRC-5. The transaction will + /// fail if both cases are false. fn _check_on_erc721_received( from: ContractAddress, to: ContractAddress, token_id: u256, data: Span ) -> bool { From b42744b7fee264d4913854437bb5c6adde34149b Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 17 Oct 2023 18:46:48 -0400 Subject: [PATCH 197/246] change import style --- src/token/erc721/erc721.cairo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 6c54dd6f2..bbb42d5a1 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -8,12 +8,12 @@ #[starknet::component] mod ERC721 { use openzeppelin::account; - use openzeppelin::introspection::dual_src5::DualCaseSRC5; - use openzeppelin::introspection::dual_src5::DualCaseSRC5Trait; + use openzeppelin::introspection::dual_src5::{DualCaseSRC5, DualCaseSRC5Trait}; use openzeppelin::introspection::src5::SRC5::InternalTrait as SRC5InternalTrait; use openzeppelin::introspection::src5::SRC5; - use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721Receiver; - use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721ReceiverTrait; + use openzeppelin::token::erc721::dual721_receiver::{ + DualCaseERC721Receiver, DualCaseERC721ReceiverTrait + }; use openzeppelin::token::erc721::interface; use starknet::ContractAddress; use starknet::get_caller_address; From 816a28aefb152e9f07941e138987dd5031919d91 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Thu, 19 Oct 2023 19:52:53 -0400 Subject: [PATCH 198/246] Update src/token/erc721/erc721.cairo Co-authored-by: Eric Nordelo --- src/token/erc721/erc721.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index bbb42d5a1..e7d2f9479 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -3,7 +3,7 @@ /// # ERC721 Component /// -/// This ERC721 component provides implementations for both the IERC721 interface +/// The ERC721 component provides implementations for both the IERC721 interface /// and the IERC721Metadata interface. #[starknet::component] mod ERC721 { From 682887c2fc1e6b7ad18cc6afbd13d780f3f03409 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 19 Oct 2023 19:55:57 -0400 Subject: [PATCH 199/246] remove unnecessary fn --- src/tests/utils.cairo | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/tests/utils.cairo b/src/tests/utils.cairo index 1ac1b8b28..2c429c5a3 100644 --- a/src/tests/utils.cairo +++ b/src/tests/utils.cairo @@ -29,25 +29,6 @@ fn pop_log, impl TEvent: starknet::Event>( ret } -/// Similar to `pop_log` and should only be used for inspecting component events with -/// indexed parameters. Component events with indexed params set the first key as the -/// contract implementation ID and the second key as the event ID. This function -/// removes both keys from the event. -fn pop_log_comp_indexed, impl TEvent: starknet::Event>( - address: ContractAddress -) -> Option { - let (mut keys, mut data) = testing::pop_log_raw(address)?; - - // Remove the contract impl ID from keys - keys.pop_front(); - // Remove the event ID from keys - keys.pop_front(); - - let ret = starknet::Event::deserialize(ref keys, ref data); - assert(data.is_empty(), 'Event has extra data'); - ret -} - /// Asserts that `expected_keys` exactly matches the indexed keys from `event`. /// `expected_keys` must include all indexed event keys for `event` in the order /// that they're defined. From 4499d249f85d529ce165c80aa1f0062703c6a962 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 19 Oct 2023 19:56:26 -0400 Subject: [PATCH 200/246] change back to pop_log --- src/tests/token/test_erc721.cairo | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tests/token/test_erc721.cairo b/src/tests/token/test_erc721.cairo index d83be5fff..c8ff19673 100644 --- a/src/tests/token/test_erc721.cairo +++ b/src/tests/token/test_erc721.cairo @@ -1355,7 +1355,7 @@ fn assert_state_after_mint(recipient: ContractAddress, token_id: u256) { fn assert_event_approval_for_all( owner: ContractAddress, operator: ContractAddress, approved: bool ) { - let event = utils::pop_log_comp_indexed::(ZERO()).unwrap(); + let event = utils::pop_log::(ZERO()).unwrap(); assert(event.owner == owner, 'Invalid `owner`'); assert(event.operator == operator, 'Invalid `operator`'); assert(event.approved == approved, 'Invalid `approved`'); @@ -1369,7 +1369,7 @@ fn assert_event_approval_for_all( } fn assert_event_approval(owner: ContractAddress, approved: ContractAddress, token_id: u256) { - let event = utils::pop_log_comp_indexed::(ZERO()).unwrap(); + let event = utils::pop_log::(ZERO()).unwrap(); assert(event.owner == owner, 'Invalid `owner`'); assert(event.approved == approved, 'Invalid `approved`'); assert(event.token_id == token_id, 'Invalid `token_id`'); @@ -1384,7 +1384,7 @@ fn assert_event_approval(owner: ContractAddress, approved: ContractAddress, toke } fn assert_event_transfer(from: ContractAddress, to: ContractAddress, token_id: u256) { - let event = utils::pop_log_comp_indexed::(ZERO()).unwrap(); + let event = utils::pop_log::(ZERO()).unwrap(); assert(event.from == from, 'Invalid `from`'); assert(event.to == to, 'Invalid `to`'); assert(event.token_id == token_id, 'Invalid `token_id`'); From 0c9427b021d48f2b6650f0b4227598aaacb11995 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 19 Oct 2023 19:56:40 -0400 Subject: [PATCH 201/246] flatten event in mock --- src/tests/mocks/erc721_mocks.cairo | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tests/mocks/erc721_mocks.cairo b/src/tests/mocks/erc721_mocks.cairo index 8cb8f2fbb..d732a00c9 100644 --- a/src/tests/mocks/erc721_mocks.cairo +++ b/src/tests/mocks/erc721_mocks.cairo @@ -32,6 +32,7 @@ mod DualCaseERC721Mock { #[event] #[derive(Drop, starknet::Event)] enum Event { + #[flat] ERC721Event: erc721_component::Event, SRC5Event: src5_component::Event } From 4e588634d63be3dab0ac4905f14c5ff7768af7dd Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 26 Oct 2023 14:17:51 -0400 Subject: [PATCH 202/246] flatten events in mocks --- src/tests/mocks/dual721_receiver_mocks.cairo | 2 ++ src/tests/mocks/erc721_mocks.cairo | 5 +++++ src/tests/mocks/erc721_receiver.cairo | 1 + 3 files changed, 8 insertions(+) diff --git a/src/tests/mocks/dual721_receiver_mocks.cairo b/src/tests/mocks/dual721_receiver_mocks.cairo index ff0d65f3e..b703f17e6 100644 --- a/src/tests/mocks/dual721_receiver_mocks.cairo +++ b/src/tests/mocks/dual721_receiver_mocks.cairo @@ -24,6 +24,7 @@ mod SnakeERC721ReceiverMock { #[event] #[derive(Drop, starknet::Event)] enum Event { + #[flat] SRC5Event: src5_component::Event } @@ -67,6 +68,7 @@ mod CamelERC721ReceiverMock { #[event] #[derive(Drop, starknet::Event)] enum Event { + #[flat] SRC5Event: src5_component::Event } diff --git a/src/tests/mocks/erc721_mocks.cairo b/src/tests/mocks/erc721_mocks.cairo index d732a00c9..509e51d49 100644 --- a/src/tests/mocks/erc721_mocks.cairo +++ b/src/tests/mocks/erc721_mocks.cairo @@ -34,6 +34,7 @@ mod DualCaseERC721Mock { enum Event { #[flat] ERC721Event: erc721_component::Event, + #[flat] SRC5Event: src5_component::Event } @@ -76,7 +77,9 @@ mod SnakeERC721Mock { #[event] #[derive(Drop, starknet::Event)] enum Event { + #[flat] ERC721Event: erc721_component::Event, + #[flat] SRC5Event: src5_component::Event } @@ -121,7 +124,9 @@ mod CamelERC721Mock { #[event] #[derive(Drop, starknet::Event)] enum Event { + #[flat] ERC721Event: erc721_component::Event, + #[flat] SRC5Event: src5_component::Event } diff --git a/src/tests/mocks/erc721_receiver.cairo b/src/tests/mocks/erc721_receiver.cairo index 13f3037d4..d716b25d9 100644 --- a/src/tests/mocks/erc721_receiver.cairo +++ b/src/tests/mocks/erc721_receiver.cairo @@ -28,6 +28,7 @@ mod ERC721Receiver { #[event] #[derive(Drop, starknet::Event)] enum Event { + #[flat] SRC5Event: src5_component::Event } From 4e459f9baa5cc0cb3e182bf71abd1079c159cf2e Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 27 Oct 2023 17:27:22 -0400 Subject: [PATCH 203/246] change receiver to component --- src/tests/mocks.cairo | 3 +- src/tests/mocks/dual721_receiver_mocks.cairo | 131 ----------- src/tests/mocks/erc721_receiver.cairo | 89 -------- src/tests/mocks/erc721_receiver_mocks.cairo | 220 +++++++++++++++++++ src/tests/token/test_dual721.cairo | 4 +- src/tests/token/test_dual721_receiver.cairo | 15 +- src/tests/token/test_erc721.cairo | 5 +- src/token/erc721.cairo | 2 + src/token/erc721/erc721_receiver.cairo | 67 ++++++ 9 files changed, 299 insertions(+), 237 deletions(-) delete mode 100644 src/tests/mocks/dual721_receiver_mocks.cairo delete mode 100644 src/tests/mocks/erc721_receiver.cairo create mode 100644 src/tests/mocks/erc721_receiver_mocks.cairo create mode 100644 src/token/erc721/erc721_receiver.cairo diff --git a/src/tests/mocks.cairo b/src/tests/mocks.cairo index 52092e365..c6c43ab52 100644 --- a/src/tests/mocks.cairo +++ b/src/tests/mocks.cairo @@ -2,10 +2,9 @@ mod accesscontrol_mocks; mod account_panic_mock; mod camel20_mock; mod camel_account_mock; -mod dual721_receiver_mocks; mod erc20_panic; mod erc721_mocks; -mod erc721_receiver; +mod erc721_receiver_mocks; mod initializable_mock; mod non_implementing_mock; mod ownable_mocks; diff --git a/src/tests/mocks/dual721_receiver_mocks.cairo b/src/tests/mocks/dual721_receiver_mocks.cairo deleted file mode 100644 index b703f17e6..000000000 --- a/src/tests/mocks/dual721_receiver_mocks.cairo +++ /dev/null @@ -1,131 +0,0 @@ -use openzeppelin::introspection::src5::SRC5; -use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver::IERC721_RECEIVER_ID; -use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; - -#[starknet::contract] -mod SnakeERC721ReceiverMock { - use openzeppelin::introspection::src5::SRC5 as src5_component; - use starknet::ContractAddress; - use super::ERC721Receiver; - use super::IERC721_RECEIVER_ID; - - component!(path: src5_component, storage: src5, event: SRC5Event); - - #[abi(embed_v0)] - impl SRC5Impl = src5_component::SRC5Impl; - impl InternalImpl = src5_component::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - src5: src5_component::Storage, - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - SRC5Event: src5_component::Event - } - - #[constructor] - fn constructor(ref self: ContractState) { - self.src5.register_interface(IERC721_RECEIVER_ID); - } - - #[external(v0)] - fn on_erc721_received( - self: @ContractState, - operator: ContractAddress, - from: ContractAddress, - token_id: u256, - data: Span - ) -> felt252 { - let unsafe_state = ERC721Receiver::unsafe_new_contract_state(); - ERC721Receiver::on_erc721_received(@unsafe_state, operator, from, token_id, data) - } -} - -#[starknet::contract] -mod CamelERC721ReceiverMock { - use openzeppelin::introspection::src5::SRC5 as src5_component; - use starknet::ContractAddress; - use super::ERC721Receiver; - use super::IERC721_RECEIVER_ID; - - component!(path: src5_component, storage: src5, event: SRC5Event); - - #[abi(embed_v0)] - impl SRC5CamelImpl = src5_component::SRC5CamelImpl; - impl InternalImpl = src5_component::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - src5: src5_component::Storage, - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - SRC5Event: src5_component::Event - } - - #[constructor] - fn constructor(ref self: ContractState) { - self.src5.register_interface(IERC721_RECEIVER_ID); - } - - #[external(v0)] - fn onERC721Received( - self: @ContractState, - operator: ContractAddress, - from: ContractAddress, - tokenId: u256, - data: Span - ) -> felt252 { - let unsafe_state = ERC721Receiver::unsafe_new_contract_state(); - ERC721Receiver::on_erc721_received(@unsafe_state, operator, from, tokenId, data) - } -} - -#[starknet::contract] -mod SnakeERC721ReceiverPanicMock { - use starknet::ContractAddress; - - #[storage] - struct Storage {} - - #[external(v0)] - fn on_erc721_received( - self: @ContractState, - operator: ContractAddress, - from: ContractAddress, - token_id: u256, - data: Span - ) -> felt252 { - panic_with_felt252('Some error'); - 3 - } -} - -#[starknet::contract] -mod CamelERC721ReceiverPanicMock { - use starknet::ContractAddress; - - #[storage] - struct Storage {} - - #[external(v0)] - fn onERC721Received( - self: @ContractState, - operator: ContractAddress, - from: ContractAddress, - tokenId: u256, - data: Span - ) -> felt252 { - panic_with_felt252('Some error'); - 3 - } -} diff --git a/src/tests/mocks/erc721_receiver.cairo b/src/tests/mocks/erc721_receiver.cairo deleted file mode 100644 index d716b25d9..000000000 --- a/src/tests/mocks/erc721_receiver.cairo +++ /dev/null @@ -1,89 +0,0 @@ -use openzeppelin::tests::utils::constants::{FAILURE, SUCCESS}; - -#[starknet::contract] -mod ERC721Receiver { - use openzeppelin::introspection::interface::ISRC5; - use openzeppelin::introspection::interface::ISRC5Camel; - use openzeppelin::introspection::src5::SRC5 as src5_component; - use openzeppelin::introspection::src5::SRC5; - use openzeppelin::token::erc721::interface::IERC721Receiver; - use openzeppelin::token::erc721::interface::IERC721ReceiverCamel; - use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; - use starknet::ContractAddress; - - component!(path: src5_component, storage: src5, event: SRC5Event); - - #[abi(embed_v0)] - impl SRC5Impl = src5_component::SRC5Impl; - #[abi(embed_v0)] - impl SRC5CamelImpl = src5_component::SRC5CamelImpl; - impl InternalImpl = src5_component::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - src5: src5_component::Storage, - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - SRC5Event: src5_component::Event - } - - #[constructor] - fn constructor(ref self: ContractState) { - self.src5.register_interface(IERC721_RECEIVER_ID); - } - - impl ERC721ReceiverImpl of IERC721Receiver { - fn on_erc721_received( - self: @ContractState, - operator: ContractAddress, - from: ContractAddress, - token_id: u256, - data: Span - ) -> felt252 { - if *data.at(0) == super::SUCCESS { - IERC721_RECEIVER_ID - } else { - 0 - } - } - } - - impl ERC721ReceiverCamelImpl of IERC721ReceiverCamel { - fn onERC721Received( - self: @ContractState, - operator: ContractAddress, - from: ContractAddress, - tokenId: u256, - data: Span - ) -> felt252 { - ERC721ReceiverImpl::on_erc721_received(self, operator, from, tokenId, data) - } - } - - #[external(v0)] - fn on_erc721_received( - self: @ContractState, - operator: ContractAddress, - from: ContractAddress, - token_id: u256, - data: Span - ) -> felt252 { - ERC721ReceiverImpl::on_erc721_received(self, operator, from, token_id, data) - } - - #[external(v0)] - fn onERC721Received( - self: @ContractState, - operator: ContractAddress, - from: ContractAddress, - tokenId: u256, - data: Span - ) -> felt252 { - ERC721ReceiverCamelImpl::onERC721Received(self, operator, from, tokenId, data) - } -} diff --git a/src/tests/mocks/erc721_receiver_mocks.cairo b/src/tests/mocks/erc721_receiver_mocks.cairo new file mode 100644 index 000000000..f098b7e74 --- /dev/null +++ b/src/tests/mocks/erc721_receiver_mocks.cairo @@ -0,0 +1,220 @@ +use openzeppelin::tests::utils::constants::SUCCESS; + +#[starknet::contract] +mod DualCaseERC721ReceiverMock { + use openzeppelin::introspection::src5::SRC5 as src5_component; + use openzeppelin::token::erc721::ERC721Receiver as erc721_receiver_component; + use starknet::ContractAddress; + + component!(path: erc721_receiver_component, storage: erc721_receiver, event: ERC721ReceiverEvent); + component!(path: src5_component, storage: src5, event: SRC5Event); + + // ERC721Receiver + impl ERC721ReceiverImpl = erc721_receiver_component::ERC721ReceiverImpl; + impl ERC721ReceiverInternalImpl = erc721_receiver_component::InternalImpl; + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = src5_component::SRC5Impl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc721_receiver: erc721_receiver_component::Storage, + #[substorage(v0)] + src5: src5_component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC721ReceiverEvent: erc721_receiver_component::Event, + #[flat] + SRC5Event: src5_component::Event + } + + #[constructor] + fn constructor(ref self: ContractState) { + self.erc721_receiver.initializer(); + } + + #[external(v0)] + fn on_erc721_received( + self: @ContractState, + operator: ContractAddress, + from: ContractAddress, + token_id: u256, + data: Span + ) -> felt252 { + if *data.at(0) == super::SUCCESS { + self.erc721_receiver.on_erc721_received(operator, from, token_id, data) + } else { + 0 + } + } + + #[external(v0)] + fn onERC721Received( + self: @ContractState, + operator: ContractAddress, + from: ContractAddress, + tokenId: u256, + data: Span + ) -> felt252 { + self.on_erc721_received(operator, from, tokenId, data) + } +} + +#[starknet::contract] +mod SnakeERC721ReceiverMock { + use openzeppelin::introspection::src5::SRC5 as src5_component; + use openzeppelin::token::erc721::ERC721Receiver as erc721_receiver_component; + use starknet::ContractAddress; + + component!(path: erc721_receiver_component, storage: erc721_receiver, event: ERC721ReceiverEvent); + component!(path: src5_component, storage: src5, event: SRC5Event); + + // ERC721Receiver + impl ERC721ReceiverImpl = erc721_receiver_component::ERC721ReceiverImpl; + impl ERC721ReceiverInternalImpl = erc721_receiver_component::InternalImpl; + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = src5_component::SRC5Impl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc721_receiver: erc721_receiver_component::Storage, + #[substorage(v0)] + src5: src5_component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC721ReceiverEvent: erc721_receiver_component::Event, + #[flat] + SRC5Event: src5_component::Event + } + + #[constructor] + fn constructor(ref self: ContractState) { + self.erc721_receiver.initializer(); + } + + #[external(v0)] + fn on_erc721_received( + self: @ContractState, + operator: ContractAddress, + from: ContractAddress, + token_id: u256, + data: Span + ) -> felt252 { + if *data.at(0) == super::SUCCESS { + self.erc721_receiver.on_erc721_received(operator, from, token_id, data) + } else { + 0 + } + } +} + +#[starknet::contract] +mod CamelERC721ReceiverMock { + use openzeppelin::introspection::src5::SRC5 as src5_component; + use openzeppelin::token::erc721::ERC721Receiver as erc721_receiver_component; + use starknet::ContractAddress; + + component!(path: erc721_receiver_component, storage: erc721_receiver, event: ERC721ReceiverEvent); + component!(path: src5_component, storage: src5, event: SRC5Event); + + // ERC721Receiver + impl ERC721ReceiverCamelImpl = erc721_receiver_component::ERC721ReceiverCamelImpl; + impl ERC721ReceiverInternalImpl = erc721_receiver_component::InternalImpl; + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = src5_component::SRC5Impl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc721_receiver: erc721_receiver_component::Storage, + #[substorage(v0)] + src5: src5_component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC721ReceiverEvent: erc721_receiver_component::Event, + #[flat] + SRC5Event: src5_component::Event + } + + #[constructor] + fn constructor(ref self: ContractState) { + self.erc721_receiver.initializer(); + } + + #[external(v0)] + fn onERC721Received( + self: @ContractState, + operator: ContractAddress, + from: ContractAddress, + tokenId: u256, + data: Span + ) -> felt252 { + if *data.at(0) == super::SUCCESS { + self.erc721_receiver.onERC721Received(operator, from, tokenId, data) + } else { + 0 + } + } +} + +#[starknet::contract] +mod SnakeERC721ReceiverPanicMock { + use starknet::ContractAddress; + + #[storage] + struct Storage {} + + #[external(v0)] + fn on_erc721_received( + self: @ContractState, + operator: ContractAddress, + from: ContractAddress, + token_id: u256, + data: Span + ) -> felt252 { + panic_with_felt252('Some error'); + 3 + } +} + +#[starknet::contract] +mod CamelERC721ReceiverPanicMock { + use starknet::ContractAddress; + + #[storage] + struct Storage {} + + #[external(v0)] + fn onERC721Received( + self: @ContractState, + operator: ContractAddress, + from: ContractAddress, + tokenId: u256, + data: Span + ) -> felt252 { + panic_with_felt252('Some error'); + 3 + } +} + + + diff --git a/src/tests/token/test_dual721.cairo b/src/tests/token/test_dual721.cairo index c93835300..61862ac6e 100644 --- a/src/tests/token/test_dual721.cairo +++ b/src/tests/token/test_dual721.cairo @@ -2,7 +2,7 @@ use openzeppelin::tests::mocks::erc721_mocks::SnakeERC721Mock; use openzeppelin::tests::mocks::erc721_mocks::{ CamelERC721Mock, CamelERC721PanicMock, SnakeERC721PanicMock }; -use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; +use openzeppelin::tests::mocks::erc721_receiver_mocks::DualCaseERC721ReceiverMock; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils::constants::{ DATA, OWNER, RECIPIENT, SPENDER, OPERATOR, OTHER, NAME, SYMBOL, URI, TOKEN_ID @@ -65,7 +65,7 @@ fn setup_erc721_panic() -> (DualCaseERC721, DualCaseERC721) { } fn setup_receiver() -> ContractAddress { - utils::deploy(ERC721Receiver::TEST_CLASS_HASH, array![]) + utils::deploy(DualCaseERC721ReceiverMock::TEST_CLASS_HASH, array![]) } // diff --git a/src/tests/token/test_dual721_receiver.cairo b/src/tests/token/test_dual721_receiver.cairo index 84644dcf2..39e805874 100644 --- a/src/tests/token/test_dual721_receiver.cairo +++ b/src/tests/token/test_dual721_receiver.cairo @@ -1,16 +1,11 @@ -use openzeppelin::tests::mocks::dual721_receiver_mocks::CamelERC721ReceiverMock; -use openzeppelin::tests::mocks::dual721_receiver_mocks::CamelERC721ReceiverPanicMock; -use openzeppelin::tests::mocks::dual721_receiver_mocks::SnakeERC721ReceiverMock; -use openzeppelin::tests::mocks::dual721_receiver_mocks::SnakeERC721ReceiverPanicMock; +use openzeppelin::tests::mocks::erc721_receiver_mocks::{CamelERC721ReceiverMock, CamelERC721ReceiverPanicMock}; +use openzeppelin::tests::mocks::erc721_receiver_mocks::{SnakeERC721ReceiverMock, SnakeERC721ReceiverPanicMock}; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils::constants::{DATA, OPERATOR, OWNER, TOKEN_ID}; use openzeppelin::tests::utils; -use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721Receiver; -use openzeppelin::token::erc721::dual721_receiver::DualCaseERC721ReceiverTrait; -use openzeppelin::token::erc721::interface::IERC721ReceiverCamelDispatcher; -use openzeppelin::token::erc721::interface::IERC721ReceiverCamelDispatcherTrait; -use openzeppelin::token::erc721::interface::IERC721ReceiverDispatcher; -use openzeppelin::token::erc721::interface::IERC721ReceiverDispatcherTrait; +use openzeppelin::token::erc721::dual721_receiver::{DualCaseERC721Receiver, DualCaseERC721ReceiverTrait}; +use openzeppelin::token::erc721::interface::{IERC721ReceiverCamelDispatcher, IERC721ReceiverCamelDispatcherTrait}; +use openzeppelin::token::erc721::interface::{IERC721ReceiverDispatcher, IERC721ReceiverDispatcherTrait}; use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; // diff --git a/src/tests/token/test_erc721.cairo b/src/tests/token/test_erc721.cairo index c8ff19673..6f79fb721 100644 --- a/src/tests/token/test_erc721.cairo +++ b/src/tests/token/test_erc721.cairo @@ -5,9 +5,8 @@ use openzeppelin::introspection::src5::SRC5::SRC5Impl; use openzeppelin::introspection::src5; use openzeppelin::introspection; use openzeppelin::tests::mocks::camel_account_mock::CamelAccountMock; -use openzeppelin::tests::mocks::dual721_receiver_mocks::CamelERC721ReceiverMock; +use openzeppelin::tests::mocks::erc721_receiver_mocks::{CamelERC721ReceiverMock, SnakeERC721ReceiverMock}; use openzeppelin::tests::mocks::erc721_mocks::DualCaseERC721Mock; -use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils::constants::{ DATA, ZERO, OWNER, RECIPIENT, SPENDER, OPERATOR, OTHER, NAME, SYMBOL, URI, TOKEN_ID, PUBKEY, @@ -42,7 +41,7 @@ fn setup() -> DualCaseERC721Mock::ContractState { } fn setup_receiver() -> ContractAddress { - utils::deploy(ERC721Receiver::TEST_CLASS_HASH, array![]) + utils::deploy(SnakeERC721ReceiverMock::TEST_CLASS_HASH, array![]) } fn setup_camel_receiver() -> ContractAddress { diff --git a/src/token/erc721.cairo b/src/token/erc721.cairo index 591703ecc..017bed7f9 100644 --- a/src/token/erc721.cairo +++ b/src/token/erc721.cairo @@ -1,6 +1,8 @@ mod dual721; mod dual721_receiver; mod erc721; +mod erc721_receiver; mod interface; use erc721::ERC721; +use erc721_receiver::ERC721Receiver; diff --git a/src/token/erc721/erc721_receiver.cairo b/src/token/erc721/erc721_receiver.cairo new file mode 100644 index 000000000..449d7b2b1 --- /dev/null +++ b/src/token/erc721/erc721_receiver.cairo @@ -0,0 +1,67 @@ +#[starknet::component] +mod ERC721Receiver { + use openzeppelin::introspection::src5::SRC5::InternalTrait as SRC5InternalTrait; + use openzeppelin::introspection::src5::SRC5; + use openzeppelin::token::erc721::interface::{IERC721Receiver, IERC721ReceiverCamel}; + use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; + use starknet::ContractAddress; + + #[storage] + struct Storage {} + + #[event] + #[derive(Drop, starknet::Event)] + enum Event {} + + #[embeddable_as(ERC721ReceiverImpl)] + impl ERC721Receiver< + TContractState, + +HasComponent, + +SRC5::HasComponent, + +Drop + > of IERC721Receiver> { + fn on_erc721_received( + self: @ComponentState, + operator: ContractAddress, + from: ContractAddress, + token_id: u256, + data: Span + ) -> felt252 { + IERC721_RECEIVER_ID + } + } + + #[embeddable_as(ERC721ReceiverCamelImpl)] + impl ERC721ReceiverCamel< + TContractState, + +HasComponent, + +SRC5::HasComponent, + +Drop + > of IERC721ReceiverCamel> { + fn onERC721Received( + self: @ComponentState, + operator: ContractAddress, + from: ContractAddress, + tokenId: u256, + data: Span + ) -> felt252 { + self.on_erc721_received(operator, from, tokenId, data) + } + } + + #[generate_trait] + impl InternalImpl< + TContractState, + +HasComponent, + +SRC5::HasComponent, + +Drop + > of InternalTrait { + fn initializer(ref self: ComponentState) { + let mut contract = self.get_contract_mut(); + let mut src5_component = SRC5::HasComponent::< + TContractState + >::get_component_mut(ref contract); + src5_component.register_interface(IERC721_RECEIVER_ID); + } + } +} From c01445f83c8007f028362c8433dbdd383356d87d Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 27 Oct 2023 17:27:40 -0400 Subject: [PATCH 204/246] fix formatting --- src/tests/mocks/erc721_receiver_mocks.cairo | 17 +++++++++++------ src/tests/token/test_dual721_receiver.cairo | 20 +++++++++++++++----- src/tests/token/test_erc721.cairo | 4 +++- src/token/erc721/erc721_receiver.cairo | 2 +- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/tests/mocks/erc721_receiver_mocks.cairo b/src/tests/mocks/erc721_receiver_mocks.cairo index f098b7e74..9bef94f31 100644 --- a/src/tests/mocks/erc721_receiver_mocks.cairo +++ b/src/tests/mocks/erc721_receiver_mocks.cairo @@ -6,7 +6,9 @@ mod DualCaseERC721ReceiverMock { use openzeppelin::token::erc721::ERC721Receiver as erc721_receiver_component; use starknet::ContractAddress; - component!(path: erc721_receiver_component, storage: erc721_receiver, event: ERC721ReceiverEvent); + component!( + path: erc721_receiver_component, storage: erc721_receiver, event: ERC721ReceiverEvent + ); component!(path: src5_component, storage: src5, event: SRC5Event); // ERC721Receiver @@ -72,7 +74,9 @@ mod SnakeERC721ReceiverMock { use openzeppelin::token::erc721::ERC721Receiver as erc721_receiver_component; use starknet::ContractAddress; - component!(path: erc721_receiver_component, storage: erc721_receiver, event: ERC721ReceiverEvent); + component!( + path: erc721_receiver_component, storage: erc721_receiver, event: ERC721ReceiverEvent + ); component!(path: src5_component, storage: src5, event: SRC5Event); // ERC721Receiver @@ -127,11 +131,14 @@ mod CamelERC721ReceiverMock { use openzeppelin::token::erc721::ERC721Receiver as erc721_receiver_component; use starknet::ContractAddress; - component!(path: erc721_receiver_component, storage: erc721_receiver, event: ERC721ReceiverEvent); + component!( + path: erc721_receiver_component, storage: erc721_receiver, event: ERC721ReceiverEvent + ); component!(path: src5_component, storage: src5, event: SRC5Event); // ERC721Receiver - impl ERC721ReceiverCamelImpl = erc721_receiver_component::ERC721ReceiverCamelImpl; + impl ERC721ReceiverCamelImpl = + erc721_receiver_component::ERC721ReceiverCamelImpl; impl ERC721ReceiverInternalImpl = erc721_receiver_component::InternalImpl; // SRC5 @@ -216,5 +223,3 @@ mod CamelERC721ReceiverPanicMock { } } - - diff --git a/src/tests/token/test_dual721_receiver.cairo b/src/tests/token/test_dual721_receiver.cairo index 39e805874..0ef522c9e 100644 --- a/src/tests/token/test_dual721_receiver.cairo +++ b/src/tests/token/test_dual721_receiver.cairo @@ -1,12 +1,22 @@ -use openzeppelin::tests::mocks::erc721_receiver_mocks::{CamelERC721ReceiverMock, CamelERC721ReceiverPanicMock}; -use openzeppelin::tests::mocks::erc721_receiver_mocks::{SnakeERC721ReceiverMock, SnakeERC721ReceiverPanicMock}; +use openzeppelin::tests::mocks::erc721_receiver_mocks::{ + CamelERC721ReceiverMock, CamelERC721ReceiverPanicMock +}; +use openzeppelin::tests::mocks::erc721_receiver_mocks::{ + SnakeERC721ReceiverMock, SnakeERC721ReceiverPanicMock +}; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils::constants::{DATA, OPERATOR, OWNER, TOKEN_ID}; use openzeppelin::tests::utils; -use openzeppelin::token::erc721::dual721_receiver::{DualCaseERC721Receiver, DualCaseERC721ReceiverTrait}; -use openzeppelin::token::erc721::interface::{IERC721ReceiverCamelDispatcher, IERC721ReceiverCamelDispatcherTrait}; -use openzeppelin::token::erc721::interface::{IERC721ReceiverDispatcher, IERC721ReceiverDispatcherTrait}; +use openzeppelin::token::erc721::dual721_receiver::{ + DualCaseERC721Receiver, DualCaseERC721ReceiverTrait +}; use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; +use openzeppelin::token::erc721::interface::{ + IERC721ReceiverCamelDispatcher, IERC721ReceiverCamelDispatcherTrait +}; +use openzeppelin::token::erc721::interface::{ + IERC721ReceiverDispatcher, IERC721ReceiverDispatcherTrait +}; // // Setup diff --git a/src/tests/token/test_erc721.cairo b/src/tests/token/test_erc721.cairo index 6f79fb721..f3b6cbb42 100644 --- a/src/tests/token/test_erc721.cairo +++ b/src/tests/token/test_erc721.cairo @@ -5,8 +5,10 @@ use openzeppelin::introspection::src5::SRC5::SRC5Impl; use openzeppelin::introspection::src5; use openzeppelin::introspection; use openzeppelin::tests::mocks::camel_account_mock::CamelAccountMock; -use openzeppelin::tests::mocks::erc721_receiver_mocks::{CamelERC721ReceiverMock, SnakeERC721ReceiverMock}; use openzeppelin::tests::mocks::erc721_mocks::DualCaseERC721Mock; +use openzeppelin::tests::mocks::erc721_receiver_mocks::{ + CamelERC721ReceiverMock, SnakeERC721ReceiverMock +}; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils::constants::{ DATA, ZERO, OWNER, RECIPIENT, SPENDER, OPERATOR, OTHER, NAME, SYMBOL, URI, TOKEN_ID, PUBKEY, diff --git a/src/token/erc721/erc721_receiver.cairo b/src/token/erc721/erc721_receiver.cairo index 449d7b2b1..592cfb470 100644 --- a/src/token/erc721/erc721_receiver.cairo +++ b/src/token/erc721/erc721_receiver.cairo @@ -2,8 +2,8 @@ mod ERC721Receiver { use openzeppelin::introspection::src5::SRC5::InternalTrait as SRC5InternalTrait; use openzeppelin::introspection::src5::SRC5; - use openzeppelin::token::erc721::interface::{IERC721Receiver, IERC721ReceiverCamel}; use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; + use openzeppelin::token::erc721::interface::{IERC721Receiver, IERC721ReceiverCamel}; use starknet::ContractAddress; #[storage] From 04be9e912bcc1e2d504530b06d2478261befbb99 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 27 Oct 2023 17:34:50 -0400 Subject: [PATCH 205/246] simplify camel return id --- src/token/erc721/erc721_receiver.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/token/erc721/erc721_receiver.cairo b/src/token/erc721/erc721_receiver.cairo index 592cfb470..b2c072311 100644 --- a/src/token/erc721/erc721_receiver.cairo +++ b/src/token/erc721/erc721_receiver.cairo @@ -45,7 +45,7 @@ mod ERC721Receiver { tokenId: u256, data: Span ) -> felt252 { - self.on_erc721_received(operator, from, tokenId, data) + IERC721_RECEIVER_ID } } From d89af432d5b4aa69a0298ef6cf5bae040ba24299 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 30 Oct 2023 10:47:58 -0400 Subject: [PATCH 206/246] fix imports --- src/tests/token/test_erc721.cairo | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/tests/token/test_erc721.cairo b/src/tests/token/test_erc721.cairo index f3b6cbb42..ba6f95288 100644 --- a/src/tests/token/test_erc721.cairo +++ b/src/tests/token/test_erc721.cairo @@ -1,4 +1,3 @@ -use integer::u256; use integer::u256_from_felt252; use openzeppelin::account::Account; use openzeppelin::introspection::src5::SRC5::SRC5Impl; @@ -14,10 +13,9 @@ use openzeppelin::tests::utils::constants::{ DATA, ZERO, OWNER, RECIPIENT, SPENDER, OPERATOR, OTHER, NAME, SYMBOL, URI, TOKEN_ID, PUBKEY, }; use openzeppelin::tests::utils; -use openzeppelin::token::erc721::ERC721::{ - Approval, ApprovalForAll, ERC721CamelOnlyImpl, ERC721Impl, ERC721MetadataCamelOnlyImpl, - ERC721MetadataImpl, InternalImpl, Transfer, -}; +use openzeppelin::token::erc721::ERC721::{Approval, ApprovalForAll, Transfer}; +use openzeppelin::token::erc721::ERC721::{ERC721CamelOnlyImpl, ERC721MetadataCamelOnlyImpl}; +use openzeppelin::token::erc721::ERC721::{ERC721Impl, ERC721MetadataImpl, InternalImpl}; use openzeppelin::token::erc721::ERC721; use openzeppelin::token::erc721; use openzeppelin::utils::serde::SerializedAppend; From 3f5c77addb25a9aaba3c995b55f489e8b23a433a Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 30 Oct 2023 10:51:00 -0400 Subject: [PATCH 207/246] fix imports --- src/tests/token/test_dual721.cairo | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/tests/token/test_dual721.cairo b/src/tests/token/test_dual721.cairo index 61862ac6e..efd3dc7bc 100644 --- a/src/tests/token/test_dual721.cairo +++ b/src/tests/token/test_dual721.cairo @@ -1,20 +1,17 @@ -use openzeppelin::tests::mocks::erc721_mocks::SnakeERC721Mock; -use openzeppelin::tests::mocks::erc721_mocks::{ - CamelERC721Mock, CamelERC721PanicMock, SnakeERC721PanicMock -}; +use openzeppelin::tests::mocks::erc721_mocks::{CamelERC721Mock, SnakeERC721Mock}; +use openzeppelin::tests::mocks::erc721_mocks::{CamelERC721PanicMock, SnakeERC721PanicMock}; use openzeppelin::tests::mocks::erc721_receiver_mocks::DualCaseERC721ReceiverMock; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils::constants::{ DATA, OWNER, RECIPIENT, SPENDER, OPERATOR, OTHER, NAME, SYMBOL, URI, TOKEN_ID }; use openzeppelin::tests::utils; -use openzeppelin::token::erc721::dual721::DualCaseERC721; -use openzeppelin::token::erc721::dual721::DualCaseERC721Trait; -use openzeppelin::token::erc721::interface::IERC721CamelOnlyDispatcher; -use openzeppelin::token::erc721::interface::IERC721CamelOnlyDispatcherTrait; -use openzeppelin::token::erc721::interface::IERC721Dispatcher; -use openzeppelin::token::erc721::interface::IERC721DispatcherTrait; +use openzeppelin::token::erc721::dual721::{DualCaseERC721, DualCaseERC721Trait}; use openzeppelin::token::erc721::interface::IERC721_ID; +use openzeppelin::token::erc721::interface::{ + IERC721CamelOnlyDispatcher, IERC721CamelOnlyDispatcherTrait +}; +use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait}; use openzeppelin::utils::serde::SerializedAppend; use starknet::ContractAddress; use starknet::testing::set_caller_address; From ab72343a15cc2d78989d4c5a4be21da9a86aabf6 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 30 Oct 2023 17:28:14 -0400 Subject: [PATCH 208/246] fix tokenURI --- src/token/erc721/erc721.cairo | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 0578f4d29..678acfd30 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -293,8 +293,7 @@ mod ERC721 { +Drop > of interface::IERC721MetadataCamelOnly> { fn tokenURI(self: @ComponentState, tokenId: u256) -> felt252 { - assert(self._exists(tokenId), Errors::INVALID_TOKEN_ID); - self.ERC721_token_uri.read(tokenId) + self.token_uri(tokenId) } } From 2a853c489b2cfed9f024502fb3aa741e19071a8d Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 31 Oct 2023 19:41:03 -0400 Subject: [PATCH 209/246] add Component suffix --- src/tests/mocks/erc721_mocks.cairo | 48 +++++++++++++++--------------- src/tests/token/test_erc721.cairo | 8 ++--- src/token/erc721.cairo | 2 +- src/token/erc721/erc721.cairo | 2 +- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/tests/mocks/erc721_mocks.cairo b/src/tests/mocks/erc721_mocks.cairo index 509e51d49..68142b29e 100644 --- a/src/tests/mocks/erc721_mocks.cairo +++ b/src/tests/mocks/erc721_mocks.cairo @@ -1,30 +1,30 @@ #[starknet::contract] mod DualCaseERC721Mock { use openzeppelin::introspection::src5::SRC5 as src5_component; - use openzeppelin::token::erc721::ERC721 as erc721_component; + use openzeppelin::token::erc721::ERC721Component; use starknet::ContractAddress; use starknet::get_caller_address; - component!(path: erc721_component, storage: erc721, event: ERC721Event); + component!(path: ERC721Component, storage: erc721, event: ERC721Event); component!(path: src5_component, storage: src5, event: SRC5Event); #[abi(embed_v0)] - impl ERC721Impl = erc721_component::ERC721Impl; + impl ERC721Impl = ERC721Component::ERC721Impl; #[abi(embed_v0)] impl SRC5Impl = src5_component::SRC5Impl; #[abi(embed_v0)] - impl ERC721MetadataImpl = erc721_component::ERC721MetadataImpl; + impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl; #[abi(embed_v0)] - impl ERC721CamelOnly = erc721_component::ERC721CamelOnlyImpl; + impl ERC721CamelOnly = ERC721Component::ERC721CamelOnlyImpl; #[abi(embed_v0)] impl ERC721MetadataCamelOnly = - erc721_component::ERC721MetadataCamelOnlyImpl; - impl ERC721InternalImpl = erc721_component::InternalImpl; + ERC721Component::ERC721MetadataCamelOnlyImpl; + impl ERC721InternalImpl = ERC721Component::InternalImpl; #[storage] struct Storage { #[substorage(v0)] - erc721: erc721_component::Storage, + erc721: ERC721Component::Storage, #[substorage(v0)] src5: src5_component::Storage } @@ -33,7 +33,7 @@ mod DualCaseERC721Mock { #[derive(Drop, starknet::Event)] enum Event { #[flat] - ERC721Event: erc721_component::Event, + ERC721Event: ERC721Component::Event, #[flat] SRC5Event: src5_component::Event } @@ -51,25 +51,25 @@ mod DualCaseERC721Mock { #[starknet::contract] mod SnakeERC721Mock { use openzeppelin::introspection::src5::SRC5 as src5_component; - use openzeppelin::token::erc721::ERC721 as erc721_component; + use openzeppelin::token::erc721::ERC721Component; use starknet::ContractAddress; use starknet::get_caller_address; - component!(path: erc721_component, storage: erc721, event: ERC721Event); + component!(path: ERC721Component, storage: erc721, event: ERC721Event); component!(path: src5_component, storage: src5, event: SRC5Event); #[abi(embed_v0)] - impl ERC721Impl = erc721_component::ERC721Impl; + impl ERC721Impl = ERC721Component::ERC721Impl; #[abi(embed_v0)] impl SRC5Impl = src5_component::SRC5Impl; #[abi(embed_v0)] - impl ERC721MetadataImpl = erc721_component::ERC721MetadataImpl; - impl InternalImpl = erc721_component::InternalImpl; + impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl; + impl InternalImpl = ERC721Component::InternalImpl; #[storage] struct Storage { #[substorage(v0)] - erc721: erc721_component::Storage, + erc721: ERC721Component::Storage, #[substorage(v0)] src5: src5_component::Storage } @@ -78,7 +78,7 @@ mod SnakeERC721Mock { #[derive(Drop, starknet::Event)] enum Event { #[flat] - ERC721Event: erc721_component::Event, + ERC721Event: ERC721Component::Event, #[flat] SRC5Event: src5_component::Event } @@ -96,27 +96,27 @@ mod SnakeERC721Mock { #[starknet::contract] mod CamelERC721Mock { use openzeppelin::introspection::src5::SRC5 as src5_component; - use openzeppelin::token::erc721::ERC721 as erc721_component; - use openzeppelin::token::erc721::ERC721::{ERC721Impl, ERC721MetadataImpl}; + use openzeppelin::token::erc721::ERC721Component; + use openzeppelin::token::erc721::ERC721Component::{ERC721Impl, ERC721MetadataImpl}; use starknet::ContractAddress; use starknet::get_caller_address; - component!(path: erc721_component, storage: erc721, event: ERC721Event); + component!(path: ERC721Component, storage: erc721, event: ERC721Event); component!(path: src5_component, storage: src5, event: SRC5Event); #[abi(embed_v0)] - impl ERC721CamelOnly = erc721_component::ERC721CamelOnlyImpl; + impl ERC721CamelOnly = ERC721Component::ERC721CamelOnlyImpl; #[abi(embed_v0)] impl SRC5Impl = src5_component::SRC5Impl; #[abi(embed_v0)] impl ERC721MetadataCamelOnly = - erc721_component::ERC721MetadataCamelOnlyImpl; - impl InternalImpl = erc721_component::InternalImpl; + ERC721Component::ERC721MetadataCamelOnlyImpl; + impl InternalImpl = ERC721Component::InternalImpl; #[storage] struct Storage { #[substorage(v0)] - erc721: erc721_component::Storage, + erc721: ERC721Component::Storage, #[substorage(v0)] src5: src5_component::Storage } @@ -125,7 +125,7 @@ mod CamelERC721Mock { #[derive(Drop, starknet::Event)] enum Event { #[flat] - ERC721Event: erc721_component::Event, + ERC721Event: ERC721Component::Event, #[flat] SRC5Event: src5_component::Event } diff --git a/src/tests/token/test_erc721.cairo b/src/tests/token/test_erc721.cairo index ba6f95288..86b646aea 100644 --- a/src/tests/token/test_erc721.cairo +++ b/src/tests/token/test_erc721.cairo @@ -13,10 +13,10 @@ use openzeppelin::tests::utils::constants::{ DATA, ZERO, OWNER, RECIPIENT, SPENDER, OPERATOR, OTHER, NAME, SYMBOL, URI, TOKEN_ID, PUBKEY, }; use openzeppelin::tests::utils; -use openzeppelin::token::erc721::ERC721::{Approval, ApprovalForAll, Transfer}; -use openzeppelin::token::erc721::ERC721::{ERC721CamelOnlyImpl, ERC721MetadataCamelOnlyImpl}; -use openzeppelin::token::erc721::ERC721::{ERC721Impl, ERC721MetadataImpl, InternalImpl}; -use openzeppelin::token::erc721::ERC721; +use openzeppelin::token::erc721::ERC721Component::{Approval, ApprovalForAll, Transfer}; +use openzeppelin::token::erc721::ERC721Component::{ERC721CamelOnlyImpl, ERC721MetadataCamelOnlyImpl}; +use openzeppelin::token::erc721::ERC721Component::{ERC721Impl, ERC721MetadataImpl, InternalImpl}; +use openzeppelin::token::erc721::ERC721Component; use openzeppelin::token::erc721; use openzeppelin::utils::serde::SerializedAppend; use starknet::ContractAddress; diff --git a/src/token/erc721.cairo b/src/token/erc721.cairo index 017bed7f9..9ca25def0 100644 --- a/src/token/erc721.cairo +++ b/src/token/erc721.cairo @@ -4,5 +4,5 @@ mod erc721; mod erc721_receiver; mod interface; -use erc721::ERC721; +use erc721::ERC721Component; use erc721_receiver::ERC721Receiver; diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 678acfd30..62f67de07 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -6,7 +6,7 @@ /// The ERC721 component provides implementations for both the IERC721 interface /// and the IERC721Metadata interface. #[starknet::component] -mod ERC721 { +mod ERC721Component { use openzeppelin::account; use openzeppelin::introspection::dual_src5::{DualCaseSRC5, DualCaseSRC5Trait}; use openzeppelin::introspection::src5::SRC5::InternalTrait as SRC5InternalTrait; From 4893df98a772cdd7ef2e42cf6391153137d634bd Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 31 Oct 2023 19:44:56 -0400 Subject: [PATCH 210/246] add Component suffix to receiver --- src/tests/mocks/erc721_receiver_mocks.cairo | 36 ++++++++++----------- src/token/erc721.cairo | 2 +- src/token/erc721/erc721_receiver.cairo | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/tests/mocks/erc721_receiver_mocks.cairo b/src/tests/mocks/erc721_receiver_mocks.cairo index 9bef94f31..fc04f5b8a 100644 --- a/src/tests/mocks/erc721_receiver_mocks.cairo +++ b/src/tests/mocks/erc721_receiver_mocks.cairo @@ -3,17 +3,17 @@ use openzeppelin::tests::utils::constants::SUCCESS; #[starknet::contract] mod DualCaseERC721ReceiverMock { use openzeppelin::introspection::src5::SRC5 as src5_component; - use openzeppelin::token::erc721::ERC721Receiver as erc721_receiver_component; + use openzeppelin::token::erc721::ERC721ReceiverComponent; use starknet::ContractAddress; component!( - path: erc721_receiver_component, storage: erc721_receiver, event: ERC721ReceiverEvent + path: ERC721ReceiverComponent, storage: erc721_receiver, event: ERC721ReceiverEvent ); component!(path: src5_component, storage: src5, event: SRC5Event); // ERC721Receiver - impl ERC721ReceiverImpl = erc721_receiver_component::ERC721ReceiverImpl; - impl ERC721ReceiverInternalImpl = erc721_receiver_component::InternalImpl; + impl ERC721ReceiverImpl = ERC721ReceiverComponent::ERC721ReceiverImpl; + impl ERC721ReceiverInternalImpl = ERC721ReceiverComponent::InternalImpl; // SRC5 #[abi(embed_v0)] @@ -22,7 +22,7 @@ mod DualCaseERC721ReceiverMock { #[storage] struct Storage { #[substorage(v0)] - erc721_receiver: erc721_receiver_component::Storage, + erc721_receiver: ERC721ReceiverComponent::Storage, #[substorage(v0)] src5: src5_component::Storage } @@ -31,7 +31,7 @@ mod DualCaseERC721ReceiverMock { #[derive(Drop, starknet::Event)] enum Event { #[flat] - ERC721ReceiverEvent: erc721_receiver_component::Event, + ERC721ReceiverEvent: ERC721ReceiverComponent::Event, #[flat] SRC5Event: src5_component::Event } @@ -71,17 +71,17 @@ mod DualCaseERC721ReceiverMock { #[starknet::contract] mod SnakeERC721ReceiverMock { use openzeppelin::introspection::src5::SRC5 as src5_component; - use openzeppelin::token::erc721::ERC721Receiver as erc721_receiver_component; + use openzeppelin::token::erc721::ERC721ReceiverComponent; use starknet::ContractAddress; component!( - path: erc721_receiver_component, storage: erc721_receiver, event: ERC721ReceiverEvent + path: ERC721ReceiverComponent, storage: erc721_receiver, event: ERC721ReceiverEvent ); component!(path: src5_component, storage: src5, event: SRC5Event); // ERC721Receiver - impl ERC721ReceiverImpl = erc721_receiver_component::ERC721ReceiverImpl; - impl ERC721ReceiverInternalImpl = erc721_receiver_component::InternalImpl; + impl ERC721ReceiverImpl = ERC721ReceiverComponent::ERC721ReceiverImpl; + impl ERC721ReceiverInternalImpl = ERC721ReceiverComponent::InternalImpl; // SRC5 #[abi(embed_v0)] @@ -90,7 +90,7 @@ mod SnakeERC721ReceiverMock { #[storage] struct Storage { #[substorage(v0)] - erc721_receiver: erc721_receiver_component::Storage, + erc721_receiver: ERC721ReceiverComponent::Storage, #[substorage(v0)] src5: src5_component::Storage } @@ -99,7 +99,7 @@ mod SnakeERC721ReceiverMock { #[derive(Drop, starknet::Event)] enum Event { #[flat] - ERC721ReceiverEvent: erc721_receiver_component::Event, + ERC721ReceiverEvent: ERC721ReceiverComponent::Event, #[flat] SRC5Event: src5_component::Event } @@ -128,18 +128,18 @@ mod SnakeERC721ReceiverMock { #[starknet::contract] mod CamelERC721ReceiverMock { use openzeppelin::introspection::src5::SRC5 as src5_component; - use openzeppelin::token::erc721::ERC721Receiver as erc721_receiver_component; + use openzeppelin::token::erc721::ERC721ReceiverComponent; use starknet::ContractAddress; component!( - path: erc721_receiver_component, storage: erc721_receiver, event: ERC721ReceiverEvent + path: ERC721ReceiverComponent, storage: erc721_receiver, event: ERC721ReceiverEvent ); component!(path: src5_component, storage: src5, event: SRC5Event); // ERC721Receiver impl ERC721ReceiverCamelImpl = - erc721_receiver_component::ERC721ReceiverCamelImpl; - impl ERC721ReceiverInternalImpl = erc721_receiver_component::InternalImpl; + ERC721ReceiverComponent::ERC721ReceiverCamelImpl; + impl ERC721ReceiverInternalImpl = ERC721ReceiverComponent::InternalImpl; // SRC5 #[abi(embed_v0)] @@ -148,7 +148,7 @@ mod CamelERC721ReceiverMock { #[storage] struct Storage { #[substorage(v0)] - erc721_receiver: erc721_receiver_component::Storage, + erc721_receiver: ERC721ReceiverComponent::Storage, #[substorage(v0)] src5: src5_component::Storage } @@ -157,7 +157,7 @@ mod CamelERC721ReceiverMock { #[derive(Drop, starknet::Event)] enum Event { #[flat] - ERC721ReceiverEvent: erc721_receiver_component::Event, + ERC721ReceiverEvent: ERC721ReceiverComponent::Event, #[flat] SRC5Event: src5_component::Event } diff --git a/src/token/erc721.cairo b/src/token/erc721.cairo index 9ca25def0..823b4c561 100644 --- a/src/token/erc721.cairo +++ b/src/token/erc721.cairo @@ -5,4 +5,4 @@ mod erc721_receiver; mod interface; use erc721::ERC721Component; -use erc721_receiver::ERC721Receiver; +use erc721_receiver::ERC721ReceiverComponent; diff --git a/src/token/erc721/erc721_receiver.cairo b/src/token/erc721/erc721_receiver.cairo index b2c072311..3ddb9dd59 100644 --- a/src/token/erc721/erc721_receiver.cairo +++ b/src/token/erc721/erc721_receiver.cairo @@ -1,5 +1,5 @@ #[starknet::component] -mod ERC721Receiver { +mod ERC721ReceiverComponent { use openzeppelin::introspection::src5::SRC5::InternalTrait as SRC5InternalTrait; use openzeppelin::introspection::src5::SRC5; use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; From 3cf49409bc874443b9ff502d22ee9f016d5b0b1a Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 31 Oct 2023 19:45:16 -0400 Subject: [PATCH 211/246] fix formatting --- src/tests/mocks/erc721_mocks.cairo | 2 +- src/tests/mocks/erc721_receiver_mocks.cairo | 15 ++++----------- src/tests/token/test_erc721.cairo | 4 +++- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/tests/mocks/erc721_mocks.cairo b/src/tests/mocks/erc721_mocks.cairo index 68142b29e..edcd7f118 100644 --- a/src/tests/mocks/erc721_mocks.cairo +++ b/src/tests/mocks/erc721_mocks.cairo @@ -96,8 +96,8 @@ mod SnakeERC721Mock { #[starknet::contract] mod CamelERC721Mock { use openzeppelin::introspection::src5::SRC5 as src5_component; - use openzeppelin::token::erc721::ERC721Component; use openzeppelin::token::erc721::ERC721Component::{ERC721Impl, ERC721MetadataImpl}; + use openzeppelin::token::erc721::ERC721Component; use starknet::ContractAddress; use starknet::get_caller_address; diff --git a/src/tests/mocks/erc721_receiver_mocks.cairo b/src/tests/mocks/erc721_receiver_mocks.cairo index fc04f5b8a..749dbdc94 100644 --- a/src/tests/mocks/erc721_receiver_mocks.cairo +++ b/src/tests/mocks/erc721_receiver_mocks.cairo @@ -6,9 +6,7 @@ mod DualCaseERC721ReceiverMock { use openzeppelin::token::erc721::ERC721ReceiverComponent; use starknet::ContractAddress; - component!( - path: ERC721ReceiverComponent, storage: erc721_receiver, event: ERC721ReceiverEvent - ); + component!(path: ERC721ReceiverComponent, storage: erc721_receiver, event: ERC721ReceiverEvent); component!(path: src5_component, storage: src5, event: SRC5Event); // ERC721Receiver @@ -74,9 +72,7 @@ mod SnakeERC721ReceiverMock { use openzeppelin::token::erc721::ERC721ReceiverComponent; use starknet::ContractAddress; - component!( - path: ERC721ReceiverComponent, storage: erc721_receiver, event: ERC721ReceiverEvent - ); + component!(path: ERC721ReceiverComponent, storage: erc721_receiver, event: ERC721ReceiverEvent); component!(path: src5_component, storage: src5, event: SRC5Event); // ERC721Receiver @@ -131,14 +127,11 @@ mod CamelERC721ReceiverMock { use openzeppelin::token::erc721::ERC721ReceiverComponent; use starknet::ContractAddress; - component!( - path: ERC721ReceiverComponent, storage: erc721_receiver, event: ERC721ReceiverEvent - ); + component!(path: ERC721ReceiverComponent, storage: erc721_receiver, event: ERC721ReceiverEvent); component!(path: src5_component, storage: src5, event: SRC5Event); // ERC721Receiver - impl ERC721ReceiverCamelImpl = - ERC721ReceiverComponent::ERC721ReceiverCamelImpl; + impl ERC721ReceiverCamelImpl = ERC721ReceiverComponent::ERC721ReceiverCamelImpl; impl ERC721ReceiverInternalImpl = ERC721ReceiverComponent::InternalImpl; // SRC5 diff --git a/src/tests/token/test_erc721.cairo b/src/tests/token/test_erc721.cairo index 86b646aea..a1bd04ff5 100644 --- a/src/tests/token/test_erc721.cairo +++ b/src/tests/token/test_erc721.cairo @@ -14,7 +14,9 @@ use openzeppelin::tests::utils::constants::{ }; use openzeppelin::tests::utils; use openzeppelin::token::erc721::ERC721Component::{Approval, ApprovalForAll, Transfer}; -use openzeppelin::token::erc721::ERC721Component::{ERC721CamelOnlyImpl, ERC721MetadataCamelOnlyImpl}; +use openzeppelin::token::erc721::ERC721Component::{ + ERC721CamelOnlyImpl, ERC721MetadataCamelOnlyImpl +}; use openzeppelin::token::erc721::ERC721Component::{ERC721Impl, ERC721MetadataImpl, InternalImpl}; use openzeppelin::token::erc721::ERC721Component; use openzeppelin::token::erc721; From 74ce94ef9e49b58444db2970c19b413901a52f9a Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 31 Oct 2023 19:45:46 -0400 Subject: [PATCH 212/246] fix formatting --- src/tests/token/test_erc721.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/token/test_erc721.cairo b/src/tests/token/test_erc721.cairo index a1bd04ff5..f1d92cad1 100644 --- a/src/tests/token/test_erc721.cairo +++ b/src/tests/token/test_erc721.cairo @@ -13,10 +13,10 @@ use openzeppelin::tests::utils::constants::{ DATA, ZERO, OWNER, RECIPIENT, SPENDER, OPERATOR, OTHER, NAME, SYMBOL, URI, TOKEN_ID, PUBKEY, }; use openzeppelin::tests::utils; -use openzeppelin::token::erc721::ERC721Component::{Approval, ApprovalForAll, Transfer}; use openzeppelin::token::erc721::ERC721Component::{ ERC721CamelOnlyImpl, ERC721MetadataCamelOnlyImpl }; +use openzeppelin::token::erc721::ERC721Component::{Approval, ApprovalForAll, Transfer}; use openzeppelin::token::erc721::ERC721Component::{ERC721Impl, ERC721MetadataImpl, InternalImpl}; use openzeppelin::token::erc721::ERC721Component; use openzeppelin::token::erc721; From 1c106a6c2f75b9a0e06c60a19942732c6c6200f1 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 1 Nov 2023 17:16:07 -0400 Subject: [PATCH 213/246] add receiver tests --- src/tests/token.cairo | 1 + src/tests/token/test_erc721_receiver.cairo | 30 ++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 src/tests/token/test_erc721_receiver.cairo diff --git a/src/tests/token.cairo b/src/tests/token.cairo index 458159c98..562e49123 100644 --- a/src/tests/token.cairo +++ b/src/tests/token.cairo @@ -3,4 +3,5 @@ mod test_dual721; mod test_dual721_receiver; mod test_erc20; mod test_erc721; +mod test_erc721_receiver; diff --git a/src/tests/token/test_erc721_receiver.cairo b/src/tests/token/test_erc721_receiver.cairo new file mode 100644 index 000000000..9e607ac15 --- /dev/null +++ b/src/tests/token/test_erc721_receiver.cairo @@ -0,0 +1,30 @@ +use openzeppelin::introspection::src5::SRC5::SRC5Impl; +use openzeppelin::introspection::interface::ISRC5_ID; +use openzeppelin::token::erc721::ERC721ReceiverComponent; +use openzeppelin::token::erc721::ERC721ReceiverComponent::{ + ERC721ReceiverImpl, ERC721ReceiverCamelImpl, InternalImpl +}; +use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; +use openzeppelin::tests::mocks::erc721_receiver_mocks::DualCaseERC721ReceiverMock; +use openzeppelin::tests::utils::constants::{DATA, OWNER, OPERATOR, TOKEN_ID}; + +fn STATE() -> DualCaseERC721ReceiverMock::ContractState { + DualCaseERC721ReceiverMock::contract_state_for_testing() +} + +#[test] +#[available_gas(20000000)] +fn test_initializer() { + let mut state = STATE(); + state.erc721_receiver.initializer(); + assert(state.src5.supports_interface(IERC721_RECEIVER_ID), 'Missing interface ID'); + assert(state.src5.supports_interface(ISRC5_ID), 'Missing interface ID'); +} + +#[test] +#[available_gas(20000000)] +fn test_on_erc721_received() { + let mut state = STATE(); + assert(state.erc721_receiver.on_erc721_received(OPERATOR(), OWNER(), TOKEN_ID, DATA(true)) == IERC721_RECEIVER_ID, ''); + assert(state.erc721_receiver.onERC721Received(OPERATOR(), OWNER(), TOKEN_ID, DATA(true)) == IERC721_RECEIVER_ID, ''); +} From 0941a6193c2a334eaaaa128d88457904352d02ae Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 1 Nov 2023 17:25:03 -0400 Subject: [PATCH 214/246] fix formatting and test --- src/tests/token/test_erc721_receiver.cairo | 23 ++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/tests/token/test_erc721_receiver.cairo b/src/tests/token/test_erc721_receiver.cairo index 9e607ac15..8cfd9242c 100644 --- a/src/tests/token/test_erc721_receiver.cairo +++ b/src/tests/token/test_erc721_receiver.cairo @@ -1,12 +1,12 @@ -use openzeppelin::introspection::src5::SRC5::SRC5Impl; use openzeppelin::introspection::interface::ISRC5_ID; -use openzeppelin::token::erc721::ERC721ReceiverComponent; +use openzeppelin::introspection::src5::SRC5::SRC5Impl; +use openzeppelin::tests::mocks::erc721_receiver_mocks::DualCaseERC721ReceiverMock; +use openzeppelin::tests::utils::constants::{OWNER, OPERATOR, TOKEN_ID}; use openzeppelin::token::erc721::ERC721ReceiverComponent::{ ERC721ReceiverImpl, ERC721ReceiverCamelImpl, InternalImpl }; +use openzeppelin::token::erc721::ERC721ReceiverComponent; use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; -use openzeppelin::tests::mocks::erc721_receiver_mocks::DualCaseERC721ReceiverMock; -use openzeppelin::tests::utils::constants::{DATA, OWNER, OPERATOR, TOKEN_ID}; fn STATE() -> DualCaseERC721ReceiverMock::ContractState { DualCaseERC721ReceiverMock::contract_state_for_testing() @@ -25,6 +25,17 @@ fn test_initializer() { #[available_gas(20000000)] fn test_on_erc721_received() { let mut state = STATE(); - assert(state.erc721_receiver.on_erc721_received(OPERATOR(), OWNER(), TOKEN_ID, DATA(true)) == IERC721_RECEIVER_ID, ''); - assert(state.erc721_receiver.onERC721Received(OPERATOR(), OWNER(), TOKEN_ID, DATA(true)) == IERC721_RECEIVER_ID, ''); + let data = array![]; + assert( + state + .erc721_receiver + .on_erc721_received(OPERATOR(), OWNER(), TOKEN_ID, data.span()) == IERC721_RECEIVER_ID, + 'Should return receiver ID' + ); + assert( + state + .erc721_receiver + .onERC721Received(OPERATOR(), OWNER(), TOKEN_ID, data.span()) == IERC721_RECEIVER_ID, + 'Should return receiver ID' + ); } From cf7b7d6991e477444ac5b1ca5b8cf56492deea19 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 1 Nov 2023 17:28:35 -0400 Subject: [PATCH 215/246] remove import --- src/tests/token/test_erc721_receiver.cairo | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tests/token/test_erc721_receiver.cairo b/src/tests/token/test_erc721_receiver.cairo index 8cfd9242c..1581bb431 100644 --- a/src/tests/token/test_erc721_receiver.cairo +++ b/src/tests/token/test_erc721_receiver.cairo @@ -5,7 +5,6 @@ use openzeppelin::tests::utils::constants::{OWNER, OPERATOR, TOKEN_ID}; use openzeppelin::token::erc721::ERC721ReceiverComponent::{ ERC721ReceiverImpl, ERC721ReceiverCamelImpl, InternalImpl }; -use openzeppelin::token::erc721::ERC721ReceiverComponent; use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; fn STATE() -> DualCaseERC721ReceiverMock::ContractState { From d5feed7f2e92197c4526834461a4a9ad94513229 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 1 Nov 2023 17:48:44 -0400 Subject: [PATCH 216/246] add code comments --- src/token/erc721/erc721_receiver.cairo | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/token/erc721/erc721_receiver.cairo b/src/token/erc721/erc721_receiver.cairo index 3ddb9dd59..3c55b0f4f 100644 --- a/src/token/erc721/erc721_receiver.cairo +++ b/src/token/erc721/erc721_receiver.cairo @@ -1,3 +1,11 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.8.0-beta.0 (token/erc721/erc721_receiver.cairo) + +/// # ERC721Receiver Component +/// +/// The ERC721Receiver component provides implementations for the IERC721Receiver +/// interface. Integrating this component allows contracts to support ERC721 +/// safe transfers. #[starknet::component] mod ERC721ReceiverComponent { use openzeppelin::introspection::src5::SRC5::InternalTrait as SRC5InternalTrait; @@ -20,6 +28,9 @@ mod ERC721ReceiverComponent { +SRC5::HasComponent, +Drop > of IERC721Receiver> { + /// Called whenever the implementing contract receives `token_id` through + /// a safe transfer. This function must return `IERC721_RECEIVER_ID` + /// to confirm the token transfer. fn on_erc721_received( self: @ComponentState, operator: ContractAddress, @@ -31,6 +42,7 @@ mod ERC721ReceiverComponent { } } + /// Adds camelCase support for `IERC721Receiver`. #[embeddable_as(ERC721ReceiverCamelImpl)] impl ERC721ReceiverCamel< TContractState, @@ -56,6 +68,8 @@ mod ERC721ReceiverComponent { +SRC5::HasComponent, +Drop > of InternalTrait { + /// Initializes the contract by registering the IERC721Receiver interface ID. + /// This should be used inside the contract's constructor. fn initializer(ref self: ComponentState) { let mut contract = self.get_contract_mut(); let mut src5_component = SRC5::HasComponent::< From 35f144cc53dcc7411d8a1448ec9ee08dddbd762a Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 2 Nov 2023 14:03:50 -0400 Subject: [PATCH 217/246] fix conflicts --- src/tests/mocks.cairo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/mocks.cairo b/src/tests/mocks.cairo index 794713af6..2434120ea 100644 --- a/src/tests/mocks.cairo +++ b/src/tests/mocks.cairo @@ -1,10 +1,10 @@ mod accesscontrol_mocks; -mod erc721_mocks; -mod erc721_receiver_mocks; mod account_mocks; mod dual721_receiver_mocks; mod erc20_mocks; +mod erc721_mocks; mod erc721_receiver; +mod erc721_receiver_mocks; mod initializable_mock; mod non_implementing_mock; mod ownable_mocks; From e8f6a3ae36d2be4978f3fc6ca0bc7e327bac702d Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 2 Nov 2023 17:09:45 -0400 Subject: [PATCH 218/246] simplify with get_dep_component_mut --- src/token/erc721/erc721.cairo | 7 ++----- src/token/erc721/erc721_receiver.cairo | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 0e4a88350..7a9fc4b19 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -305,7 +305,7 @@ mod ERC721Component { impl InternalImpl< TContractState, +HasComponent, - +SRC5Component::HasComponent, + impl SRC5: SRC5Component::HasComponent, +Drop > of InternalTrait { /// Initializes the contract by setting the token name and symbol. @@ -314,10 +314,7 @@ mod ERC721Component { self.ERC721_name.write(name); self.ERC721_symbol.write(symbol); - let mut contract = self.get_contract_mut(); - let mut src5_component = SRC5Component::HasComponent::< - TContractState - >::get_component_mut(ref contract); + let mut src5_component = get_dep_component_mut!(ref self, SRC5); src5_component.register_interface(interface::IERC721_ID); src5_component.register_interface(interface::IERC721_METADATA_ID); } diff --git a/src/token/erc721/erc721_receiver.cairo b/src/token/erc721/erc721_receiver.cairo index 375308ab4..a2b4ce508 100644 --- a/src/token/erc721/erc721_receiver.cairo +++ b/src/token/erc721/erc721_receiver.cairo @@ -65,16 +65,13 @@ mod ERC721ReceiverComponent { impl InternalImpl< TContractState, +HasComponent, - +SRC5Component::HasComponent, + impl SRC5: SRC5Component::HasComponent, +Drop > of InternalTrait { /// Initializes the contract by registering the IERC721Receiver interface ID. /// This should be used inside the contract's constructor. fn initializer(ref self: ComponentState) { - let mut contract = self.get_contract_mut(); - let mut src5_component = SRC5Component::HasComponent::< - TContractState - >::get_component_mut(ref contract); + let mut src5_component = get_dep_component_mut!(ref self, SRC5); src5_component.register_interface(IERC721_RECEIVER_ID); } } From 50e7f2626c5cd5be1efef367b877c160321d76fe Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 4 Nov 2023 12:48:30 -0400 Subject: [PATCH 219/246] add recipient to constructor --- src/tests/mocks/erc721_mocks.cairo | 30 +++++++++++++++++++++--------- src/tests/token/test_dual721.cairo | 2 ++ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/tests/mocks/erc721_mocks.cairo b/src/tests/mocks/erc721_mocks.cairo index bc057f09b..6d77d40c3 100644 --- a/src/tests/mocks/erc721_mocks.cairo +++ b/src/tests/mocks/erc721_mocks.cairo @@ -3,7 +3,6 @@ mod DualCaseERC721Mock { use openzeppelin::introspection::src5::SRC5Component; use openzeppelin::token::erc721::ERC721Component; use starknet::ContractAddress; - use starknet::get_caller_address; component!(path: ERC721Component, storage: erc721, event: ERC721Event); component!(path: SRC5Component, storage: src5, event: SRC5Event); @@ -40,10 +39,15 @@ mod DualCaseERC721Mock { #[constructor] fn constructor( - ref self: ContractState, name: felt252, symbol: felt252, token_id: u256, uri: felt252 + ref self: ContractState, + name: felt252, + symbol: felt252, + recipient: ContractAddress, + token_id: u256, + uri: felt252 ) { self.erc721.initializer(name, symbol); - self.erc721._mint(get_caller_address(), token_id); + self.erc721._mint(recipient, token_id); self.erc721._set_token_uri(token_id, uri); } } @@ -53,7 +57,6 @@ mod SnakeERC721Mock { use openzeppelin::introspection::src5::SRC5Component; use openzeppelin::token::erc721::ERC721Component; use starknet::ContractAddress; - use starknet::get_caller_address; component!(path: ERC721Component, storage: erc721, event: ERC721Event); component!(path: SRC5Component, storage: src5, event: SRC5Event); @@ -85,10 +88,15 @@ mod SnakeERC721Mock { #[constructor] fn constructor( - ref self: ContractState, name: felt252, symbol: felt252, token_id: u256, uri: felt252 + ref self: ContractState, + name: felt252, + symbol: felt252, + recipient: ContractAddress, + token_id: u256, + uri: felt252 ) { self.erc721.initializer(name, symbol); - self.erc721._mint(get_caller_address(), token_id); + self.erc721._mint(recipient, token_id); self.erc721._set_token_uri(token_id, uri); } } @@ -99,7 +107,6 @@ mod CamelERC721Mock { use openzeppelin::token::erc721::ERC721Component::{ERC721Impl, ERC721MetadataImpl}; use openzeppelin::token::erc721::ERC721Component; use starknet::ContractAddress; - use starknet::get_caller_address; component!(path: ERC721Component, storage: erc721, event: ERC721Event); component!(path: SRC5Component, storage: src5, event: SRC5Event); @@ -132,10 +139,15 @@ mod CamelERC721Mock { #[constructor] fn constructor( - ref self: ContractState, name: felt252, symbol: felt252, token_id: u256, uri: felt252 + ref self: ContractState, + name: felt252, + symbol: felt252, + recipient: ContractAddress, + token_id: u256, + uri: felt252 ) { self.erc721.initializer(name, symbol); - self.erc721._mint(get_caller_address(), token_id); + self.erc721._mint(recipient, token_id); self.erc721._set_token_uri(token_id, uri); } diff --git a/src/tests/token/test_dual721.cairo b/src/tests/token/test_dual721.cairo index efd3dc7bc..28e2c196b 100644 --- a/src/tests/token/test_dual721.cairo +++ b/src/tests/token/test_dual721.cairo @@ -25,6 +25,7 @@ fn setup_snake() -> (DualCaseERC721, IERC721Dispatcher) { let mut calldata = array![]; calldata.append_serde(NAME); calldata.append_serde(SYMBOL); + calldata.append_serde(OWNER()); calldata.append_serde(TOKEN_ID); calldata.append_serde(URI); set_contract_address(OWNER()); @@ -36,6 +37,7 @@ fn setup_camel() -> (DualCaseERC721, IERC721CamelOnlyDispatcher) { let mut calldata = array![]; calldata.append_serde(NAME); calldata.append_serde(SYMBOL); + calldata.append_serde(OWNER()); calldata.append_serde(TOKEN_ID); calldata.append_serde(URI); set_contract_address(OWNER()); From cd861471393be8f61049987d289beb7f2bc314ce Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 4 Nov 2023 12:50:37 -0400 Subject: [PATCH 220/246] remove unused mocks --- src/tests/mocks.cairo | 2 - src/tests/mocks/dual721_receiver_mocks.cairo | 129 ------------------- src/tests/mocks/erc721_receiver.cairo | 87 ------------- 3 files changed, 218 deletions(-) delete mode 100644 src/tests/mocks/dual721_receiver_mocks.cairo delete mode 100644 src/tests/mocks/erc721_receiver.cairo diff --git a/src/tests/mocks.cairo b/src/tests/mocks.cairo index 2434120ea..4bc03de22 100644 --- a/src/tests/mocks.cairo +++ b/src/tests/mocks.cairo @@ -1,9 +1,7 @@ mod accesscontrol_mocks; mod account_mocks; -mod dual721_receiver_mocks; mod erc20_mocks; mod erc721_mocks; -mod erc721_receiver; mod erc721_receiver_mocks; mod initializable_mock; mod non_implementing_mock; diff --git a/src/tests/mocks/dual721_receiver_mocks.cairo b/src/tests/mocks/dual721_receiver_mocks.cairo deleted file mode 100644 index 1e3121b72..000000000 --- a/src/tests/mocks/dual721_receiver_mocks.cairo +++ /dev/null @@ -1,129 +0,0 @@ -use openzeppelin::introspection::src5::SRC5Component; -use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver::IERC721_RECEIVER_ID; -use openzeppelin::tests::mocks::erc721_receiver::ERC721Receiver; - -#[starknet::contract] -mod SnakeERC721ReceiverMock { - use openzeppelin::introspection::src5::SRC5Component; - use starknet::ContractAddress; - use super::ERC721Receiver; - use super::IERC721_RECEIVER_ID; - - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - impl InternalImpl = SRC5Component::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - src5: SRC5Component::Storage, - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - SRC5Event: SRC5Component::Event - } - - #[constructor] - fn constructor(ref self: ContractState) { - self.src5.register_interface(IERC721_RECEIVER_ID); - } - - #[external(v0)] - fn on_erc721_received( - self: @ContractState, - operator: ContractAddress, - from: ContractAddress, - token_id: u256, - data: Span - ) -> felt252 { - let unsafe_state = ERC721Receiver::unsafe_new_contract_state(); - ERC721Receiver::on_erc721_received(@unsafe_state, operator, from, token_id, data) - } -} - -#[starknet::contract] -mod CamelERC721ReceiverMock { - use openzeppelin::introspection::src5::SRC5Component; - use starknet::ContractAddress; - use super::ERC721Receiver; - use super::IERC721_RECEIVER_ID; - - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - #[abi(embed_v0)] - impl SRC5CamelImpl = SRC5Component::SRC5CamelImpl; - impl InternalImpl = SRC5Component::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - src5: SRC5Component::Storage, - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - SRC5Event: SRC5Component::Event - } - - #[constructor] - fn constructor(ref self: ContractState) { - self.src5.register_interface(IERC721_RECEIVER_ID); - } - - #[external(v0)] - fn onERC721Received( - self: @ContractState, - operator: ContractAddress, - from: ContractAddress, - tokenId: u256, - data: Span - ) -> felt252 { - let unsafe_state = ERC721Receiver::unsafe_new_contract_state(); - ERC721Receiver::on_erc721_received(@unsafe_state, operator, from, tokenId, data) - } -} - -#[starknet::contract] -mod SnakeERC721ReceiverPanicMock { - use starknet::ContractAddress; - - #[storage] - struct Storage {} - - #[external(v0)] - fn on_erc721_received( - self: @ContractState, - operator: ContractAddress, - from: ContractAddress, - token_id: u256, - data: Span - ) -> felt252 { - panic_with_felt252('Some error'); - 3 - } -} - -#[starknet::contract] -mod CamelERC721ReceiverPanicMock { - use starknet::ContractAddress; - - #[storage] - struct Storage {} - - #[external(v0)] - fn onERC721Received( - self: @ContractState, - operator: ContractAddress, - from: ContractAddress, - tokenId: u256, - data: Span - ) -> felt252 { - panic_with_felt252('Some error'); - 3 - } -} diff --git a/src/tests/mocks/erc721_receiver.cairo b/src/tests/mocks/erc721_receiver.cairo deleted file mode 100644 index 7f91865d7..000000000 --- a/src/tests/mocks/erc721_receiver.cairo +++ /dev/null @@ -1,87 +0,0 @@ -use openzeppelin::tests::utils::constants::{FAILURE, SUCCESS}; - -#[starknet::contract] -mod ERC721Receiver { - use openzeppelin::introspection::interface::ISRC5; - use openzeppelin::introspection::interface::ISRC5Camel; - use openzeppelin::introspection::src5::SRC5Component; - use openzeppelin::token::erc721::interface::IERC721Receiver; - use openzeppelin::token::erc721::interface::IERC721ReceiverCamel; - use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; - use starknet::ContractAddress; - - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - #[abi(embed_v0)] - impl SRC5CamelImpl = SRC5Component::SRC5CamelImpl; - impl InternalImpl = SRC5Component::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - src5: SRC5Component::Storage, - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - SRC5Event: SRC5Component::Event - } - - #[constructor] - fn constructor(ref self: ContractState) { - self.src5.register_interface(IERC721_RECEIVER_ID); - } - - impl ERC721ReceiverImpl of IERC721Receiver { - fn on_erc721_received( - self: @ContractState, - operator: ContractAddress, - from: ContractAddress, - token_id: u256, - data: Span - ) -> felt252 { - if *data.at(0) == super::SUCCESS { - IERC721_RECEIVER_ID - } else { - 0 - } - } - } - - impl ERC721ReceiverCamelImpl of IERC721ReceiverCamel { - fn onERC721Received( - self: @ContractState, - operator: ContractAddress, - from: ContractAddress, - tokenId: u256, - data: Span - ) -> felt252 { - ERC721ReceiverImpl::on_erc721_received(self, operator, from, tokenId, data) - } - } - - #[external(v0)] - fn on_erc721_received( - self: @ContractState, - operator: ContractAddress, - from: ContractAddress, - token_id: u256, - data: Span - ) -> felt252 { - ERC721ReceiverImpl::on_erc721_received(self, operator, from, token_id, data) - } - - #[external(v0)] - fn onERC721Received( - self: @ContractState, - operator: ContractAddress, - from: ContractAddress, - tokenId: u256, - data: Span - ) -> felt252 { - ERC721ReceiverCamelImpl::onERC721Received(self, operator, from, tokenId, data) - } -} From 9ee7511c4308a0c6dc943a901660feba96709693 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 4 Nov 2023 14:37:56 -0400 Subject: [PATCH 221/246] simplify mocks --- src/tests/mocks/erc721_mocks.cairo | 274 ++++++++++++++--------------- 1 file changed, 131 insertions(+), 143 deletions(-) diff --git a/src/tests/mocks/erc721_mocks.cairo b/src/tests/mocks/erc721_mocks.cairo index 6d77d40c3..f6ca88ab3 100644 --- a/src/tests/mocks/erc721_mocks.cairo +++ b/src/tests/mocks/erc721_mocks.cairo @@ -153,19 +153,20 @@ mod CamelERC721Mock { /// The following external methods are included because they are case-agnostic /// and this contract should not embed the snake_case impl. + #[generate_trait] #[external(v0)] - fn approve(ref self: ContractState, to: ContractAddress, tokenId: u256) { - self.erc721.approve(to, tokenId); - } + impl ExternalImpl of ExternalTrait { + fn approve(ref self: ContractState, to: ContractAddress, tokenId: u256) { + self.erc721.approve(to, tokenId); + } - #[external(v0)] - fn name(self: @ContractState) -> felt252 { - self.erc721.name() - } + fn name(self: @ContractState) -> felt252 { + self.erc721.name() + } - #[external(v0)] - fn symbol(self: @ContractState) -> felt252 { - self.erc721.symbol() + fn symbol(self: @ContractState) -> felt252 { + self.erc721.symbol() + } } } @@ -183,82 +184,74 @@ mod SnakeERC721PanicMock { #[storage] struct Storage {} + #[generate_trait] #[external(v0)] - fn name(self: @ContractState) -> felt252 { - panic_with_felt252('Some error'); - 3 - } - - #[external(v0)] - fn symbol(self: @ContractState) -> felt252 { - panic_with_felt252('Some error'); - 3 - } - - #[external(v0)] - fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) { - panic_with_felt252('Some error'); - } - - #[external(v0)] - fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { - panic_with_felt252('Some error'); - false - } - - #[external(v0)] - fn token_uri(self: @ContractState, token_id: u256) -> felt252 { - panic_with_felt252('Some error'); - 3 - } - - #[external(v0)] - fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { - panic_with_felt252('Some error'); - u256 { low: 3, high: 3 } - } - - #[external(v0)] - fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress { - panic_with_felt252('Some error'); - Zeroable::zero() - } - - #[external(v0)] - fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { - panic_with_felt252('Some error'); - Zeroable::zero() - } - - #[external(v0)] - fn is_approved_for_all( - self: @ContractState, owner: ContractAddress, operator: ContractAddress - ) -> bool { - panic_with_felt252('Some error'); - false - } - - #[external(v0)] - fn set_approval_for_all(ref self: ContractState, operator: ContractAddress, approved: bool) { - panic_with_felt252('Some error'); - } - - #[external(v0)] - fn transfer_from( - ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 - ) { - panic_with_felt252('Some error'); - } - - #[external(v0)] - fn safe_transfer_from( - ref self: ContractState, - from: ContractAddress, - to: ContractAddress, - token_id: u256, - data: Span - ) { - panic_with_felt252('Some error'); + impl ExternalImpl of ExternalTrait { + fn name(self: @ContractState) -> felt252 { + panic_with_felt252('Some error'); + 3 + } + + fn symbol(self: @ContractState) -> felt252 { + panic_with_felt252('Some error'); + 3 + } + + fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) { + panic_with_felt252('Some error'); + } + + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { + panic_with_felt252('Some error'); + false + } + + fn token_uri(self: @ContractState, token_id: u256) -> felt252 { + panic_with_felt252('Some error'); + 3 + } + + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { + panic_with_felt252('Some error'); + u256 { low: 3, high: 3 } + } + + fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress { + panic_with_felt252('Some error'); + Zeroable::zero() + } + + fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { + panic_with_felt252('Some error'); + Zeroable::zero() + } + + fn is_approved_for_all( + self: @ContractState, owner: ContractAddress, operator: ContractAddress + ) -> bool { + panic_with_felt252('Some error'); + false + } + + fn set_approval_for_all(ref self: ContractState, operator: ContractAddress, approved: bool) { + panic_with_felt252('Some error'); + } + + fn transfer_from( + ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 + ) { + panic_with_felt252('Some error'); + } + + fn safe_transfer_from( + ref self: ContractState, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span + ) { + panic_with_felt252('Some error'); + } } } @@ -270,64 +263,59 @@ mod CamelERC721PanicMock { #[storage] struct Storage {} + #[generate_trait] #[external(v0)] - fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { - panic_with_felt252('Some error'); - false - } - - #[external(v0)] - fn tokenURI(self: @ContractState, tokenId: u256) -> felt252 { - panic_with_felt252('Some error'); - 3 - } - - #[external(v0)] - fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 { - panic_with_felt252('Some error'); - u256 { low: 3, high: 3 } - } - - #[external(v0)] - fn ownerOf(self: @ContractState, tokenId: u256) -> ContractAddress { - panic_with_felt252('Some error'); - Zeroable::zero() - } - - #[external(v0)] - fn getApproved(self: @ContractState, tokenId: u256) -> ContractAddress { - panic_with_felt252('Some error'); - Zeroable::zero() - } - - #[external(v0)] - fn isApprovedForAll( - self: @ContractState, owner: ContractAddress, operator: ContractAddress - ) -> bool { - panic_with_felt252('Some error'); - false - } - - #[external(v0)] - fn setApprovalForAll(ref self: ContractState, operator: ContractAddress, approved: bool) { - panic_with_felt252('Some error'); - } - - #[external(v0)] - fn transferFrom( - ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256 - ) { - panic_with_felt252('Some error'); - } - - #[external(v0)] - fn safeTransferFrom( - ref self: ContractState, - from: ContractAddress, - to: ContractAddress, - tokenId: u256, - data: Span - ) { - panic_with_felt252('Some error'); + impl ExternalImpl of ExternalTrait { + fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { + panic_with_felt252('Some error'); + false + } + + fn tokenURI(self: @ContractState, tokenId: u256) -> felt252 { + panic_with_felt252('Some error'); + 3 + } + + fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 { + panic_with_felt252('Some error'); + u256 { low: 3, high: 3 } + } + + fn ownerOf(self: @ContractState, tokenId: u256) -> ContractAddress { + panic_with_felt252('Some error'); + Zeroable::zero() + } + + fn getApproved(self: @ContractState, tokenId: u256) -> ContractAddress { + panic_with_felt252('Some error'); + Zeroable::zero() + } + + fn isApprovedForAll( + self: @ContractState, owner: ContractAddress, operator: ContractAddress + ) -> bool { + panic_with_felt252('Some error'); + false + } + + fn setApprovalForAll(ref self: ContractState, operator: ContractAddress, approved: bool) { + panic_with_felt252('Some error'); + } + + fn transferFrom( + ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256 + ) { + panic_with_felt252('Some error'); + } + + fn safeTransferFrom( + ref self: ContractState, + from: ContractAddress, + to: ContractAddress, + tokenId: u256, + data: Span + ) { + panic_with_felt252('Some error'); + } } } From 8d0cd9ce808f2468b2c3f4791f9ba1cc29abff85 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 4 Nov 2023 14:39:24 -0400 Subject: [PATCH 222/246] fix formatting --- src/tests/mocks/erc721_mocks.cairo | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tests/mocks/erc721_mocks.cairo b/src/tests/mocks/erc721_mocks.cairo index f6ca88ab3..06647fbb0 100644 --- a/src/tests/mocks/erc721_mocks.cairo +++ b/src/tests/mocks/erc721_mocks.cairo @@ -233,7 +233,9 @@ mod SnakeERC721PanicMock { false } - fn set_approval_for_all(ref self: ContractState, operator: ContractAddress, approved: bool) { + fn set_approval_for_all( + ref self: ContractState, operator: ContractAddress, approved: bool + ) { panic_with_felt252('Some error'); } From d518fcf4414ad1859cde67981467707f3d954535 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Mon, 6 Nov 2023 18:33:29 -0500 Subject: [PATCH 223/246] Update src/tests/token/test_erc721.cairo Co-authored-by: Eric Nordelo --- src/tests/token/test_erc721.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/token/test_erc721.cairo b/src/tests/token/test_erc721.cairo index ed12b2936..df2df2af2 100644 --- a/src/tests/token/test_erc721.cairo +++ b/src/tests/token/test_erc721.cairo @@ -167,7 +167,7 @@ fn test__exists() { assert(!state.erc721._exists(token_id), 'Token should not exist'); owner = state.erc721.ERC721_owners.read(token_id); - assert(owner == zero, ''); + assert(owner == zero, 'Invalid owner'); } // From 8094a961c52a23d28b13f9ce8a5fd9057a7e7427 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 6 Nov 2023 18:40:50 -0500 Subject: [PATCH 224/246] fix impl order --- src/tests/mocks/erc721_mocks.cairo | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/tests/mocks/erc721_mocks.cairo b/src/tests/mocks/erc721_mocks.cairo index 06647fbb0..e0a6dc132 100644 --- a/src/tests/mocks/erc721_mocks.cairo +++ b/src/tests/mocks/erc721_mocks.cairo @@ -7,11 +7,10 @@ mod DualCaseERC721Mock { component!(path: ERC721Component, storage: erc721, event: ERC721Event); component!(path: SRC5Component, storage: src5, event: SRC5Event); + // ERC721 #[abi(embed_v0)] impl ERC721Impl = ERC721Component::ERC721Impl; #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - #[abi(embed_v0)] impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl; #[abi(embed_v0)] impl ERC721CamelOnly = ERC721Component::ERC721CamelOnlyImpl; @@ -20,6 +19,10 @@ mod DualCaseERC721Mock { ERC721Component::ERC721MetadataCamelOnlyImpl; impl ERC721InternalImpl = ERC721Component::InternalImpl; + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + #[storage] struct Storage { #[substorage(v0)] @@ -61,13 +64,16 @@ mod SnakeERC721Mock { component!(path: ERC721Component, storage: erc721, event: ERC721Event); component!(path: SRC5Component, storage: src5, event: SRC5Event); + // ERC721 #[abi(embed_v0)] impl ERC721Impl = ERC721Component::ERC721Impl; #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - #[abi(embed_v0)] impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl; - impl InternalImpl = ERC721Component::InternalImpl; + impl ERC721InternalImpl = ERC721Component::InternalImpl; + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; #[storage] struct Storage { @@ -111,14 +117,17 @@ mod CamelERC721Mock { component!(path: ERC721Component, storage: erc721, event: ERC721Event); component!(path: SRC5Component, storage: src5, event: SRC5Event); + // ERC721 #[abi(embed_v0)] impl ERC721CamelOnly = ERC721Component::ERC721CamelOnlyImpl; #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - #[abi(embed_v0)] impl ERC721MetadataCamelOnly = ERC721Component::ERC721MetadataCamelOnlyImpl; - impl InternalImpl = ERC721Component::InternalImpl; + impl ERC721InternalImpl = ERC721Component::InternalImpl; + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; #[storage] struct Storage { From 4ce5a7c39fea68af3d47ebf00ad43772910b6c07 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 9 Nov 2023 02:32:15 -0500 Subject: [PATCH 225/246] start interface section edit --- docs/modules/ROOT/pages/erc721.adoc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 438cf0162..0e26d812e 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -2,13 +2,9 @@ :token-types: https://docs.openzeppelin.com/contracts/4.x/tokens#different-kinds-of-tokens[non-fungible tokens] :eip721: https://eips.ethereum.org/EIPS/eip-721[EIP-721] -:erc721-api: xref:/api/erc721.adoc[API Reference] -:introspection: xref:/introspection.adoc[Introspection] The ERC721 token standard is a specification for {token-types}, or more colloquially: NFTs. -The `token::erc721::ERC721` contract implements an approximation of {eip721} in Cairo for Starknet. - -TIP: For detailed information on the usage and implementation check the {erc721-api} section. +`token::erc721::ERC721Component` provides an approximation of {eip721} in Cairo for Starknet. == Interface @@ -17,6 +13,10 @@ TIP: For detailed information on the usage and implementation check the {erc721- :ierc721metadata-interface: xref:/erc721.adoc#ierc721metadata[IERC721Metadata] :isrc5-interface: xref:/erc721.adoc#isrc5[ISRC5] +The following interface represents the full ABI of the Contracts for Cairo ERC721 component. +The interface includes the IERC721 standard interface, the ISRC5 introspection interface, and the optional IERC721Metadata interface. +To support older token deployments, as mentioned in Dual interfaces, the ERC721 component also includes implementations of the interface written in camelCase. + ERC721 contracts must implement both the {ierc721-interface} and {isrc5-interface} interfaces in order to have an ERC721-compliant contract. Additionally, ERC721 contracts often include {ierc721metadata-interface} interface as well. The following preset contract includes all three interfaces and represents the full interface of the `ERC721ABI` module. @@ -107,6 +107,7 @@ trait IERC721 { === ISRC5 +:introspection: xref:/introspection.adoc[Introspection] :snip5: https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md[SNIP-5] :eip165: https://eips.ethereum.org/EIPS/eip-165[EIP-165] From f917443c712fff6897ae047a8ade09b3c988015b Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 9 Nov 2023 03:14:26 -0500 Subject: [PATCH 226/246] fix fn order --- src/token/erc721/erc721.cairo | 134 +++++++++++++++---------------- src/token/erc721/interface.cairo | 74 ++++++++--------- 2 files changed, 102 insertions(+), 106 deletions(-) diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 7a9fc4b19..e8dce9b4a 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -109,21 +109,50 @@ mod ERC721Component { self._owner_of(token_id) } - /// Returns the address approved for `token_id`. + /// Safely transfer ownership of `token_id` from `from` to `to`. /// /// Requirements: /// + /// - Caller is either approved or the `token_id` owner. + /// - `to` is not the zero address. + /// - `from` is not the zero address. /// - `token_id` exists. - fn get_approved(self: @ComponentState, token_id: u256) -> ContractAddress { - assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); - self.ERC721_token_approvals.read(token_id) + /// - `to` is either an account contract or supports the `IERC721Receiver` interface. + /// + /// Emits a `Transfer` event. + fn safe_transfer_from( + ref self: ComponentState, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span + ) { + assert( + self._is_approved_or_owner(get_caller_address(), token_id), Errors::UNAUTHORIZED + ); + self._safe_transfer(from, to, token_id, data); } - /// Query if `operator` is an authorized operator for `owner`. - fn is_approved_for_all( - self: @ComponentState, owner: ContractAddress, operator: ContractAddress - ) -> bool { - self.ERC721_operator_approvals.read((owner, operator)) + /// Transfer ownership of `token_id` from `from` to `to`. + /// + /// Requirements: + /// + /// - Caller is either approved or the `token_id` owner. + /// - `to` is not the zero address. + /// - `from` is not the zero address. + /// - `token_id` exists. + /// + /// Emits a `Transfer` event. + fn transfer_from( + ref self: ComponentState, + from: ContractAddress, + to: ContractAddress, + token_id: u256 + ) { + assert( + self._is_approved_or_owner(get_caller_address(), token_id), Errors::UNAUTHORIZED + ); + self._transfer(from, to, token_id); } /// Change or reaffirm the approved address for an NFT. @@ -159,50 +188,21 @@ mod ERC721Component { self._set_approval_for_all(get_caller_address(), operator, approved) } - /// Transfer ownership of `token_id` from `from` to `to`. + /// Returns the address approved for `token_id`. /// /// Requirements: /// - /// - Caller is either approved or the `token_id` owner. - /// - `to` is not the zero address. - /// - `from` is not the zero address. /// - `token_id` exists. - /// - /// Emits a `Transfer` event. - fn transfer_from( - ref self: ComponentState, - from: ContractAddress, - to: ContractAddress, - token_id: u256 - ) { - assert( - self._is_approved_or_owner(get_caller_address(), token_id), Errors::UNAUTHORIZED - ); - self._transfer(from, to, token_id); + fn get_approved(self: @ComponentState, token_id: u256) -> ContractAddress { + assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); + self.ERC721_token_approvals.read(token_id) } - /// Safely transfer ownership of `token_id` from `from` to `to`. - /// - /// Requirements: - /// - /// - Caller is either approved or the `token_id` owner. - /// - `to` is not the zero address. - /// - `from` is not the zero address. - /// - `token_id` exists. - /// - `to` is either an account contract or supports the `IERC721Receiver` interface. - /// - /// Emits a `Transfer` event. - fn safe_transfer_from( - ref self: ComponentState, - from: ContractAddress, - to: ContractAddress, - token_id: u256, - data: Span - ) { - assert( - self._is_approved_or_owner(get_caller_address(), token_id), Errors::UNAUTHORIZED - ); - self._safe_transfer(from, to, token_id, data); + /// Query if `operator` is an authorized operator for `owner`. + fn is_approved_for_all( + self: @ComponentState, owner: ContractAddress, operator: ContractAddress + ) -> bool { + self.ERC721_operator_approvals.read((owner, operator)) } } @@ -248,20 +248,14 @@ mod ERC721Component { self.owner_of(tokenId) } - fn getApproved(self: @ComponentState, tokenId: u256) -> ContractAddress { - self.get_approved(tokenId) - } - - fn isApprovedForAll( - self: @ComponentState, owner: ContractAddress, operator: ContractAddress - ) -> bool { - self.is_approved_for_all(owner, operator) - } - - fn setApprovalForAll( - ref self: ComponentState, operator: ContractAddress, approved: bool + fn safeTransferFrom( + ref self: ComponentState, + from: ContractAddress, + to: ContractAddress, + tokenId: u256, + data: Span ) { - self.set_approval_for_all(operator, approved) + self.safe_transfer_from(from, to, tokenId, data) } fn transferFrom( @@ -273,14 +267,20 @@ mod ERC721Component { self.transfer_from(from, to, tokenId) } - fn safeTransferFrom( - ref self: ComponentState, - from: ContractAddress, - to: ContractAddress, - tokenId: u256, - data: Span + fn setApprovalForAll( + ref self: ComponentState, operator: ContractAddress, approved: bool ) { - self.safe_transfer_from(from, to, tokenId, data) + self.set_approval_for_all(operator, approved) + } + + fn getApproved(self: @ComponentState, tokenId: u256) -> ContractAddress { + self.get_approved(tokenId) + } + + fn isApprovedForAll( + self: @ComponentState, owner: ContractAddress, operator: ContractAddress + ) -> bool { + self.is_approved_for_all(owner, operator) } } diff --git a/src/token/erc721/interface.cairo b/src/token/erc721/interface.cairo index 32c2bdfcb..55e6ad9ff 100644 --- a/src/token/erc721/interface.cairo +++ b/src/token/erc721/interface.cairo @@ -13,7 +13,6 @@ const IERC721_RECEIVER_ID: felt252 = trait IERC721 { fn balance_of(self: @TState, account: ContractAddress) -> u256; fn owner_of(self: @TState, token_id: u256) -> ContractAddress; - fn transfer_from(ref self: TState, from: ContractAddress, to: ContractAddress, token_id: u256); fn safe_transfer_from( ref self: TState, from: ContractAddress, @@ -21,6 +20,7 @@ trait IERC721 { token_id: u256, data: Span ); + fn transfer_from(ref self: TState, from: ContractAddress, to: ContractAddress, token_id: u256); fn approve(ref self: TState, to: ContractAddress, token_id: u256); fn set_approval_for_all(ref self: TState, operator: ContractAddress, approved: bool); fn get_approved(self: @TState, token_id: u256) -> ContractAddress; @@ -29,11 +29,17 @@ trait IERC721 { ) -> bool; } +#[starknet::interface] +trait IERC721Metadata { + fn name(self: @TState) -> felt252; + fn symbol(self: @TState) -> felt252; + fn token_uri(self: @TState, token_id: u256) -> felt252; +} + #[starknet::interface] trait IERC721CamelOnly { fn balanceOf(self: @TState, account: ContractAddress) -> u256; fn ownerOf(self: @TState, tokenId: u256) -> ContractAddress; - fn transferFrom(ref self: TState, from: ContractAddress, to: ContractAddress, tokenId: u256); fn safeTransferFrom( ref self: TState, from: ContractAddress, @@ -41,53 +47,17 @@ trait IERC721CamelOnly { tokenId: u256, data: Span ); + fn transferFrom(ref self: TState, from: ContractAddress, to: ContractAddress, tokenId: u256); fn setApprovalForAll(ref self: TState, operator: ContractAddress, approved: bool); fn getApproved(self: @TState, tokenId: u256) -> ContractAddress; fn isApprovedForAll(self: @TState, owner: ContractAddress, operator: ContractAddress) -> bool; } -// -// IERC721Metadata -// - -#[starknet::interface] -trait IERC721Metadata { - fn name(self: @TState) -> felt252; - fn symbol(self: @TState) -> felt252; - fn token_uri(self: @TState, token_id: u256) -> felt252; -} - #[starknet::interface] trait IERC721MetadataCamelOnly { fn tokenURI(self: @TState, tokenId: u256) -> felt252; } -// -// ERC721Receiver -// - -#[starknet::interface] -trait IERC721Receiver { - fn on_erc721_received( - self: @TState, - operator: ContractAddress, - from: ContractAddress, - token_id: u256, - data: Span - ) -> felt252; -} - -#[starknet::interface] -trait IERC721ReceiverCamel { - fn onERC721Received( - self: @TState, - operator: ContractAddress, - from: ContractAddress, - tokenId: u256, - data: Span - ) -> felt252; -} - // // ERC721 ABI // @@ -141,3 +111,29 @@ trait ERC721ABI { // IERC721MetadataCamelOnly fn tokenURI(self: @TState, tokenId: u256) -> felt252; } + +// +// ERC721Receiver +// + +#[starknet::interface] +trait IERC721Receiver { + fn on_erc721_received( + self: @TState, + operator: ContractAddress, + from: ContractAddress, + token_id: u256, + data: Span + ) -> felt252; +} + +#[starknet::interface] +trait IERC721ReceiverCamel { + fn onERC721Received( + self: @TState, + operator: ContractAddress, + from: ContractAddress, + tokenId: u256, + data: Span + ) -> felt252; +} From a048b8a7cd08dc484898fa62ec4e9e98775a4517 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 9 Nov 2023 03:19:53 -0500 Subject: [PATCH 227/246] fix title --- docs/modules/ROOT/pages/accounts.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/accounts.adoc b/docs/modules/ROOT/pages/accounts.adoc index da71bce85..dcf472fab 100644 --- a/docs/modules/ROOT/pages/accounts.adoc +++ b/docs/modules/ROOT/pages/accounts.adoc @@ -1,7 +1,7 @@ :test-signers: https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.6.1/tests/signers.py :snip-5: https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md :snip-6: https://github.com/ericnordelo/SNIPs/blob/feat/standard-account/SNIPS/snip-6.md -:counterfactual: xref:/guides/deployment.adoc[Counterfactual Deployments] +:counterfactual: xref:/guides/deployment.adoc[Counterfactual deployments] = Accounts From 73d5693959689d582414f68adce9684fd01fae33 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 9 Nov 2023 18:49:25 -0500 Subject: [PATCH 228/246] fix conflicts --- src/token/erc721/interface.cairo | 54 -------------------------------- 1 file changed, 54 deletions(-) diff --git a/src/token/erc721/interface.cairo b/src/token/erc721/interface.cairo index f6dea8e17..55e6ad9ff 100644 --- a/src/token/erc721/interface.cairo +++ b/src/token/erc721/interface.cairo @@ -137,57 +137,3 @@ trait IERC721ReceiverCamel { data: Span ) -> felt252; } - -// -// ERC721 ABI -// - -#[starknet::interface] -trait ERC721ABI { - // IERC721 - fn balance_of(self: @TState, account: ContractAddress) -> u256; - fn owner_of(self: @TState, token_id: u256) -> ContractAddress; - fn safe_transfer_from( - ref self: TState, - from: ContractAddress, - to: ContractAddress, - token_id: u256, - data: Span - ); - fn transfer_from(ref self: TState, from: ContractAddress, to: ContractAddress, token_id: u256); - fn approve(ref self: TState, to: ContractAddress, token_id: u256); - fn set_approval_for_all(ref self: TState, operator: ContractAddress, approved: bool); - fn get_approved(self: @TState, token_id: u256) -> ContractAddress; - fn is_approved_for_all( - self: @TState, owner: ContractAddress, operator: ContractAddress - ) -> bool; - - // ISRC5 - fn supports_interface(self: @TState, interface_id: felt252) -> bool; - - // IERC721Metadata - fn name(self: @TState) -> felt252; - fn symbol(self: @TState) -> felt252; - fn token_uri(self: @TState, token_id: u256) -> felt252; - - // IERC721CamelOnly - fn balanceOf(self: @TState, account: ContractAddress) -> u256; - fn ownerOf(self: @TState, tokenId: u256) -> ContractAddress; - fn safeTransferFrom( - ref self: TState, - from: ContractAddress, - to: ContractAddress, - tokenId: u256, - data: Span - ); - fn transferFrom(ref self: TState, from: ContractAddress, to: ContractAddress, tokenId: u256); - fn setApprovalForAll(ref self: TState, operator: ContractAddress, approved: bool); - fn getApproved(self: @TState, tokenId: u256) -> ContractAddress; - fn isApprovedForAll(self: @TState, owner: ContractAddress, operator: ContractAddress) -> bool; - - // ISRC5Camel - fn supportsInterface(self: @TState, interfaceId: felt252) -> bool; - - // IERC721MetadataCamelOnly - fn tokenURI(self: @TState, tokenId: u256) -> felt252; -} From eb9ecf8119c03ab234e78058e27b51cef08cedfc Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 9 Nov 2023 18:54:28 -0500 Subject: [PATCH 229/246] update usage section, fix code examples --- docs/modules/ROOT/pages/erc721.adoc | 178 +++++++++++++--------------- 1 file changed, 85 insertions(+), 93 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 0e26d812e..9a6385269 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -170,31 +170,58 @@ The design for `SRC5` is similar to OpenZeppelin's {erc165-storage}. == Usage -:components: https://community.starknet.io/t/cairo-1-contract-syntax-is-evolving/94794#extensibility-and-components-11[Components] :mint-api: xref:api/erc721.adoc#ERC721-_mint[_mint] -WARNING: The following example uses a contract's `unsafe_new_contract_state` to access another contract's state. -Although this is useful to use them as modules, it's considered unsafe because storage members could clash among used contracts if not reviewed carefully. -Extensibility will be revisited after {components} are introduced. - -Using Contracts for Cairo, constructing an ERC721 contract requires setting up the constructor and exposing the ERC721 interface. -Here’s what that looks like: +Using Contracts for Cairo, constructing an ERC721 contract requires integrating the components and setting up the constructor. +Here's an example of a basic contract: [,javascript] ---- #[starknet::contract] mod MyNFT { + use openzeppelin::introspection::src5::SRC5Component; + use openzeppelin::token::erc721::ERC721Component; use starknet::ContractAddress; - use openzeppelin::token::erc721::ERC721; - use openzeppelin::token::erc721::interface; - use openzeppelin::introspection::interface::ISRC5; + + component!(path: ERC721Component, storage: erc721, event: ERC721Event); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // ERC721 + #[abi(embed_v0)] + impl ERC721Impl = ERC721Component::ERC721Impl; + #[abi(embed_v0)] + impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl; + #[abi(embed_v0)] + impl ERC721CamelOnly = ERC721Component::ERC721CamelOnlyImpl; + #[abi(embed_v0)] + impl ERC721MetadataCamelOnly = + ERC721Component::ERC721MetadataCamelOnlyImpl; + impl ERC721InternalImpl = ERC721Component::InternalImpl; + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; #[storage] - struct Storage {} + struct Storage { + #[substorage(v0)] + erc721: ERC721Component::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC721Event: ERC721Component::Event, + #[flat] + SRC5Event: SRC5Component::Event + } #[constructor] fn constructor( - self: @ContractState, + ref self: ContractState, recipient: ContractAddress ) { let name = 'MyNFT'; @@ -202,62 +229,27 @@ mod MyNFT { let token_id = 1; let token_uri = 'NFT_URI'; - let mut unsafe_state = ERC721::unsafe_new_contract_state(); - // Initialize the ERC721 storage - ERC721::InternalImpl::initializer(ref unsafe_state, name, symbol); - // Mint the NFT to recipient and set the token's URI - _mint_with_uri(recipient, token_id, token_uri); - } - - /// Implement the ISRC5 interface. - #[external(v0)] - impl SRC5Impl of ISRC5 { - fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { - let unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721::SRC5Impl::supports_interface(@unsafe_state, interface_id) - } + self.erc721.initializer(name, symbol); + self._mint_with_uri(recipient, token_id, token_uri); } - /// Implement the standard IERC721 interface. - #[external(v0)] - impl MyTokenImpl of interface::IERC721 { - fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { - let unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721::ERC721Impl::balance_of(@unsafe_state, account) - } - - (...) - } - - /// Implement the IERC721Metadata interface. - #[external(v0)] - impl MyTokenMetadataImpl of interface::IERC721Metadata { - fn name(self: @ContractState) -> felt252 { - let unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721::ERC721Impl::name(@unsafe_state) + #[generate_trait] + impl InternalImpl of InternalTrait { + fn _mint_with_uri( + ref self: ContractState, + recipient: ContractAddress, + token_id: u256, + token_uri: felt252 + ) { + // Initialize the ERC721 storage + self.erc721._mint(recipient, token_id); + // Mint the NFT to recipient and set the token's URI + self.erc721._set_token_uri(token_id, token_uri); } - - (...) - } - - #[internal] - fn _mint_with_uri( - recipient: ContractAddress, - token_id: u256, - token_uri: felt252 - ) { - let mut unsafe_state = ERC721::unsafe_new_contract_state(); - ERC721::InternalImpl::_mint(ref unsafe_state, recipient, token_id); - ERC721::InternalImpl::_set_token_uri(ref unsafe_state, token_id, token_uri); } } ---- -In order for the `MyNFT` contract to extend the `ERC721` contract, it utilizes the `unsafe_new_contract_state`. -The unsafe contract state allows access to ``ERC721``'s storage. -With this access, the constructor first calls the initializer to set the NFT name and symbol. -Next, the constructor calls the custom internal function `_mint_with_uri` that mints a one-of-one NFT and sets the URI for the minted token ID. - === Token transfers :transfer_from-api: xref:api/erc721.adoc#IERC721-transfer_from[transfer_from] @@ -308,45 +300,45 @@ TIP: For information on how to calculate interface IDs, see {computing-interface [,javascript] ---- #[starknet::contract] -mod ERC721Receiver { +mod MyTokenReceiver { + use openzeppelin::introspection::src5::SRC5Component; + use openzeppelin::token::erc721::ERC721ReceiverComponent; use starknet::ContractAddress; - use openzeppelin::token::erc721::ERC721; - use openzeppelin::token::erc721::interface; - use openzeppelin::introspection::interface::ISRC5; - use openzeppelin::introspection::src5::SRC5; - #[storage] - struct Storage {} + component!(path: ERC721ReceiverComponent, storage: erc721_receiver, event: ERC721ReceiverEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); - #[constructor] - fn constructor(ref self: ContractState) { - // Register the token receiver interface - let mut unsafe_state = SRC5::unsafe_new_contract_state(); - SRC5::InternalImpl::register_interface(ref unsafe_state, interface::IERC721_RECEIVER_ID); + // ERC721Receiver + #[abi(embed_v0)] + impl ERC721ReceiverImpl = ERC721ReceiverComponent::ERC721ReceiverImpl; + #[abi(embed_v0)] + impl ERC721ReceiverCamelImpl = ERC721ReceiverComponent::ERC721ReceiverCamelImpl; + impl ERC721ReceiverInternalImpl = ERC721ReceiverComponent::InternalImpl; + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc721_receiver: ERC721ReceiverComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage } - /// Implement the ISRC5 interface so the sender contract can query - /// if the recipient supports the token receiver interface ID. - #[external(v0)] - impl ISRC5Impl of ISRC5 { - fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { - let unsafe_state = SRC5::unsafe_new_contract_state(); - SRC5::SRC5Impl::supports_interface(@unsafe_state, interface_id) - } + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC721ReceiverEvent: ERC721ReceiverComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event } - /// Implement the token receiver interface. - #[external(v0)] - impl ERC721ReceiverImpl of interface::IERC721Receiver { - fn on_erc721_received( - self: @ContractState, - operator: ContractAddress, - from: ContractAddress, - token_id: u256, - data: Span - ) -> felt252 { - interface::IERC721_RECEIVER_ID - } + #[constructor] + fn constructor(ref self: ContractState) { + self.erc721_receiver.initializer(); } } ---- From 2d2ca00afe79046264b68b2a32ac0ed6b978c12e Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 10 Nov 2023 10:38:09 -0500 Subject: [PATCH 230/246] update receiver section --- docs/modules/ROOT/pages/erc721.adoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 9a6385269..73a2e9a36 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -297,6 +297,10 @@ TIP: For information on how to calculate interface IDs, see {computing-interface ==== Creating a token receiver contract +The Contracts for Cairo `IERC721ReceiverImpl` already returns the correct interface ID for safe token transfers. +To integrate the `IERC721Receiver` interface into a contract, simply include the ABI embed directive to the implementation and add the `initializer` in the contract's constructor . +Here's an example of a simple token receiver contract: + [,javascript] ---- #[starknet::contract] From 03b5d5a788c8861e096e15311a77223f54d8a7af Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 10 Nov 2023 17:54:29 -0500 Subject: [PATCH 231/246] remove the constructor --- docs/modules/ROOT/pages/api/erc721.adoc | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 4e73a99ab..0841d7839 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -215,11 +215,6 @@ use openzeppelin::token::erc721::ERC721; Implementation of ERC721 which includes the IERC721Metadata extension as specified in https://eips.ethereum.org/EIPS/eip-721[EIP-721]. -[.contract-index] -.Constructor - -* xref:#ERC721-constructor[`++constructor(self, name, symbol)++`] - [.contract-index] .External functions -- @@ -288,19 +283,6 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi * xref:#IERC721-Transfer[`++Transfer(from, to, token_id)++`] -- -==== Constructor - -:src5: xref:introspection.adoc#src5[SRC5] -:ierc721: xref:/api/erc721.adoc#IERC721[IERC721] -:ierc721metadata: xref:/api/erc721.adoc#IERC721Metadata[IERC721Metadata] - -[.contract-item] -[[ERC721-constructor]] -==== `[.contract-item-name]#++constructor++#++(ref self: ContractState, name: felt252, symbol: felt252)++` [.item-kind]#constructor# - -Initializes the state of the ERC721 contract by setting the token name and symbol. -The constructor also registers the {ierc721} and {ierc721metadata} interface ids according to {src5}. - ==== External functions [.contract-item] From 0ddf9fa67fa707462dbae0a148c534ff1fbb2315 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 10 Nov 2023 21:39:41 -0500 Subject: [PATCH 232/246] change to component, remove src5 --- docs/modules/ROOT/pages/api/erc721.adoc | 27 +++++++++---------------- 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 0841d7839..cfd96b893 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -39,9 +39,6 @@ Interface of the IERC721 standard as defined in {eip721}. * xref:#IERC721-set_approval_for_all[`++set_approval_for_all(operator, approved)++`] * xref:#IERC721-get_approved[`++get_approved(token_id)++`] * xref:#IERC721-is_approved_for_all[`++is_approved_for_all(owner, operator)++`] - -.ISRC5 -* xref:/api/introspection.adoc#ISRC5-supports_interface[`++supports_interface(interface_id)++`] -- [.contract-index] @@ -143,8 +140,7 @@ Emitted when `token_id` token is transferred from `from` to `to`. use openzeppelin::token::erc721::interface::IERC721Metadata; ``` -ERC721 extension that allows your smart contract to be interrogated for its name and for details about the assets which the NFTs represent. -See {eip721}. +Interface for the optional metadata functions in {eip721}. [.contract-index] .{inner-src5} @@ -168,9 +164,6 @@ See {eip721}. * xref:#IERC721-set_approval_for_all[`++set_approval_for_all(operator, approved)++`] * xref:#IERC721-get_approved[`++get_approved(token_id)++`] * xref:#IERC721-is_approved_for_all[`++is_approved_for_all(owner, operator)++`] - -.ISRC5 -* xref:/api/introspection.adoc#ISRC5-supports_interface[`++supports_interface(interface_id)++`] -- [.contract-index] @@ -205,15 +198,17 @@ Returns the Uniform Resource Identifier (URI) as a short string for the `token_i If the URI is not set for `token_id`, the return value will be `0`. [.contract] -[[ERC721]] -=== `++ERC721++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/cairo-2/src/token/erc721/erc721.cairo#L7[{github-icon},role=heading-link] +[[ERC721Component]] +=== `++ERC721Component++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/cairo-2/src/token/erc721/erc721.cairo#L7[{github-icon},role=heading-link] [.hljs-theme-dark] ```javascript -use openzeppelin::token::erc721::ERC721; +use openzeppelin::token::erc721::ERC721Component; ``` -Implementation of ERC721 which includes the IERC721Metadata extension as specified in https://eips.ethereum.org/EIPS/eip-721[EIP-721]. +ERC721 component implementing <> and <>. + +NOTE: Implementing xref:api/introspection.adoc#SRC5Component[SRC5Component] is a requirement for this component to be implemented. [.contract-index] .External functions @@ -228,9 +223,6 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi * xref:#IERC721-get_approved[`++get_approved(self, token_id)++`] * xref:#IERC721-is_approved_for_all[`++is_approved_for_all(self, owner, operator)++`] -.ISRC5Impl -* xref:/api/introspection.adoc#ISRC5-supports_interface[`++supports_interface(self, interface_id)++`] - .IERC721MetadataImpl * xref:#IERC721Metadata-name[`++name(self)++`] * xref:#IERC721Metadata-symbol[`++symbol(self)++`] @@ -249,9 +241,6 @@ Implementation of ERC721 which includes the IERC721Metadata extension as specifi * xref:#ERC721-getApproved[`++getApproved(self, tokenId)++`] * xref:#ERC721-isApprovedForAll[`++isApprovedForAll(self, owner, operator)++`] -.SRC5CamelImpl -* xref:/api/introspection.adoc#ISRC5-supports_interface[`++supportsInterface(self, interfaceId)++`] - .ERC721MetadataCamelOnlyImpl * xref:#ERC721-tokenURI[`++tokenURI(self, tokenId)++`] -- @@ -609,6 +598,8 @@ See <>. use openzeppelin::token::erc721::interface::IERC721Receiver; ``` +Interface for any contract that wants to support safe token transfers from ERC721 asset contracts. + [.contract-index] .{inner-src5} -- From 63976661230a1795622e66279bc9da3bd1535c87 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 10 Nov 2023 21:45:40 -0500 Subject: [PATCH 233/246] clean up metadata section --- docs/modules/ROOT/pages/api/erc721.adoc | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index cfd96b893..d06236508 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -154,26 +154,6 @@ Interface for the optional metadata functions in {eip721}. * xref:#IERC721Metadata-name[`++name()++`] * xref:#IERC721Metadata-owner_of[`++symbol()++`] * xref:#IERC721Metadata-token_uri[`++token_uri(token_id)++`] - -.IERC721 -* xref:#IERC721-balance_of[`++balance_of(account)++`] -* xref:#IERC721-owner_of[`++owner_of(token_id)++`] -* xref:#IERC721-safe_transfer_from[`++safe_transfer_from(from, to, token_id, data)++`] -* xref:#IERC721-transfer_from[`++transfer_from(from, to, token_id)++`] -* xref:#IERC721-approve[`++approve(to, token_id)++`] -* xref:#IERC721-set_approval_for_all[`++set_approval_for_all(operator, approved)++`] -* xref:#IERC721-get_approved[`++get_approved(token_id)++`] -* xref:#IERC721-is_approved_for_all[`++is_approved_for_all(owner, operator)++`] --- - -[.contract-index] -.Events --- -.IERC721 - -* xref:#IERC721-Approval[`++Approval(owner, approved, token_id)++`] -* xref:#IERC721-ApprovalForAll[`++ApprovalForAll(owner, operator, approved)++`] -* xref:#IERC721-Transfer[`++Transfer(from, to, token_id)++`] -- ==== Functions @@ -232,7 +212,7 @@ NOTE: Implementing xref:api/introspection.adoc#SRC5Component[SRC5Component] is a [.contract-index] .camelCase support -- -.ER721CamelImpl +.ER721CamelOnlyImpl * xref:#ERC721-balanceOf[`++balanceOf(self, account)++`] * xref:#ERC721-ownerOf[`++ownerOf(self, tokenId)++`] * xref:#ERC721-safeTransferFrom[`++safeTransferFrom(self, from, to, tokenId, data)++`] @@ -537,7 +517,7 @@ Emits an <> event. Requirements: -- `token_id` exists. +- `token_id` does not already exist. - `to` is either an account contract or supports the <> interface. [.contract-item] From 5599636fb10f3814ed217fab8972395a67d22137 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 12 Nov 2023 00:17:29 -0500 Subject: [PATCH 234/246] remove src5 from interface section --- docs/modules/ROOT/pages/erc721.adoc | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 73a2e9a36..52e7ce345 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -43,9 +43,6 @@ trait IERC721ABI { fn get_approved(token_id: u256) -> ContractAddress; fn is_approved_for_all(owner: ContractAddress, operator: ContractAddress) -> bool; - // ISRC5 - fn supports_interface(interface_id: felt252) -> bool; - // IERC721Metadata fn name() -> felt252; fn symbol() -> felt252; @@ -65,9 +62,6 @@ trait IERC721ABI { fn getApproved(tokenId: u256) -> ContractAddress; fn isApprovedForAll(owner: ContractAddress, operator: ContractAddress) -> bool; - // ISRC5Camel - fn supportsInterface(interfaceId: felt252) -> bool; - // IERC721MetadataCamelOnly fn tokenURI(tokenId: u256) -> felt252; } From 42f7b8f494fe5d92348b595f0d0b6a243d2b83e9 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 12 Nov 2023 14:42:30 -0500 Subject: [PATCH 235/246] fix symbol link --- docs/modules/ROOT/pages/api/erc721.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index d06236508..497fcf27a 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -152,7 +152,7 @@ Interface for the optional metadata functions in {eip721}. .Functions -- * xref:#IERC721Metadata-name[`++name()++`] -* xref:#IERC721Metadata-owner_of[`++symbol()++`] +* xref:#IERC721Metadata-symbol[`++symbol()++`] * xref:#IERC721Metadata-token_uri[`++token_uri(token_id)++`] -- From 3fe29ff8bdc033ad29a5fdc4ab0af010b8c5487d Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 12 Nov 2023 21:03:34 -0500 Subject: [PATCH 236/246] fix safe descriptions --- docs/modules/ROOT/pages/api/erc721.adoc | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 497fcf27a..ebe280220 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -67,7 +67,7 @@ Returns the owner address of `token_id`. [[IERC721-safe_transfer_from]] ==== `[.contract-item-name]#++safe_transfer_from++#++(from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#external# -Safely transfer ownership of `token_id` from `from` to `to`, checking first that `to` is aware of the ERC721 protocol to prevent tokens being locked forever. +Transfer ownership of `token_id` from `from` to `to`, checking first that `to` is aware of the ERC721 protocol to prevent tokens being locked forever. For information regarding how contracts communicate their awareness of the ERC721 protocol, see {receiving-tokens}. Emits a <> event. @@ -467,8 +467,6 @@ Requirements: [[ERC721-_mint]] ==== `[.contract-item-name]#++_mint++#++(ref self: ContractState, to: ContractAddress, token_id: u256)++` [.item-kind]#internal# -WARNING: Usage of this method is discouraged, use <> whenever possible. - Internal function that mints `token_id` and transfers it to `to`. Emits an <> event. @@ -510,7 +508,7 @@ Requirements: [[ERC721-_safe_mint]] ==== `[.contract-item-name]#++_safe_mint++#++(ref self: ContractState, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#internal# -Internal function that safely mints `token_id` and transfers it to `to`. +Internal function that mints `token_id` and transfers it to `to`. If `to` is not an account contract, `to` must support <>; otherwise, the transaction will fail. Emits an <> event. @@ -524,7 +522,7 @@ Requirements: [[ERC721-_safe_transfer]] ==== `[.contract-item-name]#++_safe_transfer++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#internal# -Safely transfers `token_id` token from `from` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. +Transfers `token_id` token from `from` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. `data` is additional data, it has no specified format and it is sent in call to `to`. From e7d750018c48ca5b99561821ff25b2f8742de349 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 12 Nov 2023 21:04:30 -0500 Subject: [PATCH 237/246] fix camel fn order --- docs/modules/ROOT/pages/api/erc721.adoc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index ebe280220..d32106d7c 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -367,18 +367,18 @@ See <>. See <>. -[.contract-item] -[[ERC721-transferFrom]] -==== `[.contract-item-name]#++transferFrom++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256)++` [.item-kind]#external# - -See <>. - [.contract-item] [[ERC721-safeTransferFrom]] ==== `[.contract-item-name]#++safeTransferFrom++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256, data: Span)++` [.item-kind]#external# See <>. +[.contract-item] +[[ERC721-transferFrom]] +==== `[.contract-item-name]#++transferFrom++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256)++` [.item-kind]#external# + +See <>. + [.contract-item] [[ERC721-setApprovalForAll]] ==== `[.contract-item-name]#++setApprovalForAll++#++(ref self: ContractState, operator: ContractAddress, approved: bool)++` [.item-kind]#external# From 6bfc30144a5cf26421f35ffab5824a61ac5e3553 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 12 Nov 2023 21:07:41 -0500 Subject: [PATCH 238/246] fix typos --- docs/modules/ROOT/pages/api/erc721.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index d32106d7c..8db75ca46 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -522,7 +522,7 @@ Requirements: [[ERC721-_safe_transfer]] ==== `[.contract-item-name]#++_safe_transfer++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#internal# -Transfers `token_id` token from `from` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. +Internal function that transfers `token_id` token from `from` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. `data` is additional data, it has no specified format and it is sent in call to `to`. @@ -541,7 +541,7 @@ Requirements: [[ERC721-_set_token_uri]] ==== `[.contract-item-name]#++_set_token_uri++#++(ref self: ContractState, token_id: u256, token_uri: felt252)++` [.item-kind]#internal# -Sets the `token_uri` of `token_id`. +Internal function that sets the `token_uri` of `token_id`. Requirements: From d1146c967133f9b2a3af0a347e54e2f71506b2c9 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 13 Nov 2023 11:53:58 -0500 Subject: [PATCH 239/246] simplify interface section --- docs/modules/ROOT/pages/erc721.adoc | 83 ++--------------------------- 1 file changed, 4 insertions(+), 79 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 52e7ce345..af1b1790e 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -11,19 +11,11 @@ The ERC721 token standard is a specification for {token-types}, or more colloqui :compatibility: xref:/erc721.adoc#erc721_compatibility[ERC721 Compatibility] :ierc721-interface: xref:/erc721.adoc#ierc721[IERC721] :ierc721metadata-interface: xref:/erc721.adoc#ierc721metadata[IERC721Metadata] -:isrc5-interface: xref:/erc721.adoc#isrc5[ISRC5] +:dual-interfaces: xref:interfaces.adoc##dual_interfaces[Dual interfaces] -The following interface represents the full ABI of the Contracts for Cairo ERC721 component. -The interface includes the IERC721 standard interface, the ISRC5 introspection interface, and the optional IERC721Metadata interface. -To support older token deployments, as mentioned in Dual interfaces, the ERC721 component also includes implementations of the interface written in camelCase. - -ERC721 contracts must implement both the {ierc721-interface} and {isrc5-interface} interfaces in order to have an ERC721-compliant contract. -Additionally, ERC721 contracts often include {ierc721metadata-interface} interface as well. -The following preset contract includes all three interfaces and represents the full interface of the `ERC721ABI` module. - -WARNING: The {ierc721-interface} and {ierc721metadata-interface} interfaces are approximations of their Ethereum counterparts. -These approximations include a few notable differences. -See {compatibility} for more information. +The following interface represents the full ABI of the Contracts for Cairo `ERC721Component`. +The interface includes the <> standard interface and the optional <> interface. +To support older token deployments, as mentioned in {dual-interfaces}, the component also includes implementations of the interface written in camelCase. [,javascript] ---- @@ -67,73 +59,6 @@ trait IERC721ABI { } ---- -=== IERC721 - -The `IERC721` interface provides basic functionality to track, transfer, and consign NFTs. - -[,javascript] ----- -trait IERC721 { - fn balance_of(account: ContractAddress) -> u256; - fn owner_of(token_id: u256) -> ContractAddress; - fn safe_transfer_from( - from: ContractAddress, - to: ContractAddress, - token_id: u256, - data: Span - ); - fn transfer_from( - from: ContractAddress, - to: ContractAddress, - token_id: u256 - ); - fn approve(to: ContractAddress, token_id: u256); - fn set_approval_for_all( - operator: ContractAddress, - approved: bool - ); - fn get_approved(token_id: u256) -> ContractAddress; - fn is_approved_for_all( - owner: ContractAddress, operator: ContractAddress - ) -> bool; -} ----- - -=== ISRC5 - -:introspection: xref:/introspection.adoc[Introspection] -:snip5: https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md[SNIP-5] -:eip165: https://eips.ethereum.org/EIPS/eip-165[EIP-165] - -`ISRC5`, from the {snip5} standard, allows for other contracts to query if the implementing contract supports a specific interface. -This is especially important with NFT contracts because if an NFT is transferred to a contract that does not support the `IERC721Receiver` interface, the NFT may be lost forever. -To mitigate this risk, the `ISRC5` interface allows for interface introspection which helps facilitate safe token transfers. - -TIP: `ISRC5` is very similar to Ethereum's {eip165}. -It's imperative to understand the introspection mechanism to avoid making drastic errors. -See {introspection}. - -[,javascript] ----- -trait ISRC5 { - fn supports_interface(interface_id: felt252) -> bool; -} ----- - -=== IERC721Metadata - -The `ERC721Metadata` extension allows a smart contract to be interrogated for its name and for details about the assets which the NFTs represent. -The vast majority of NFT contracts include this interface. - -[,javascript] ----- -trait IERC721Metadata { - fn name() -> felt252; - fn symbol() -> felt252; - fn token_uri(token_id: u256) -> felt252; -} ----- - === ERC721 compatibility :erc165-storage: https://docs.openzeppelin.com/contracts/4.x/api/utils#ERC165Storage[ERC165Storage] From 2e45ee0bc13477c4f1435f97c2337687c4a09e6e Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 13 Nov 2023 13:21:40 -0500 Subject: [PATCH 240/246] fix impl order in code blocks --- docs/modules/ROOT/pages/erc721.adoc | 33 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index af1b1790e..3bd1cf29b 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -102,8 +102,12 @@ mod MyNFT { use openzeppelin::token::erc721::ERC721Component; use starknet::ContractAddress; - component!(path: ERC721Component, storage: erc721, event: ERC721Event); component!(path: SRC5Component, storage: src5, event: SRC5Event); + component!(path: ERC721Component, storage: erc721, event: ERC721Event); + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; // ERC721 #[abi(embed_v0)] @@ -117,10 +121,6 @@ mod MyNFT { ERC721Component::ERC721MetadataCamelOnlyImpl; impl ERC721InternalImpl = ERC721Component::InternalImpl; - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - #[storage] struct Storage { #[substorage(v0)] @@ -133,9 +133,9 @@ mod MyNFT { #[derive(Drop, starknet::Event)] enum Event { #[flat] - ERC721Event: ERC721Component::Event, + SRC5Event: SRC5Component::Event, #[flat] - SRC5Event: SRC5Component::Event + ERC721Event: ERC721Component::Event } #[constructor] @@ -182,7 +182,6 @@ WARNING: Usage of `safe_transfer_from` prevents loss, though the caller must und === Receiving tokens -:erc165-discussion: https://github.com/OpenZeppelin/cairo-contracts/discussions/100[this discussion] :src5: xref:introspection.adoc#src5[SRC5] :on_erc721_received-api: xref:api/erc721.adoc#IERC721Receiver-on_erc721_received[on_erc721_received] :computing-interface-id: xref:introspection.adoc#computing_the_interface_id[Computing the interface ID] @@ -228,8 +227,12 @@ mod MyTokenReceiver { use openzeppelin::token::erc721::ERC721ReceiverComponent; use starknet::ContractAddress; - component!(path: ERC721ReceiverComponent, storage: erc721_receiver, event: ERC721ReceiverEvent); component!(path: SRC5Component, storage: src5, event: SRC5Event); + component!(path: ERC721ReceiverComponent, storage: erc721_receiver, event: ERC721ReceiverEvent); + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; // ERC721Receiver #[abi(embed_v0)] @@ -238,25 +241,21 @@ mod MyTokenReceiver { impl ERC721ReceiverCamelImpl = ERC721ReceiverComponent::ERC721ReceiverCamelImpl; impl ERC721ReceiverInternalImpl = ERC721ReceiverComponent::InternalImpl; - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - #[storage] struct Storage { #[substorage(v0)] - erc721_receiver: ERC721ReceiverComponent::Storage, + src5: SRC5Component::Storage, #[substorage(v0)] - src5: SRC5Component::Storage + erc721_receiver: ERC721ReceiverComponent::Storage } #[event] #[derive(Drop, starknet::Event)] enum Event { #[flat] - ERC721ReceiverEvent: ERC721ReceiverComponent::Event, + SRC5Event: SRC5Component::Event, #[flat] - SRC5Event: SRC5Component::Event + ERC721ReceiverEvent: ERC721ReceiverComponent::Event } #[constructor] From 94734d947e43f2858873d19a56bb7782758ae206 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 13 Nov 2023 13:22:20 -0500 Subject: [PATCH 241/246] fix usage sentence --- docs/modules/ROOT/pages/erc721.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 3bd1cf29b..0e1f55ada 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -91,7 +91,7 @@ The design for `SRC5` is similar to OpenZeppelin's {erc165-storage}. :mint-api: xref:api/erc721.adoc#ERC721-_mint[_mint] -Using Contracts for Cairo, constructing an ERC721 contract requires integrating the components and setting up the constructor. +Using Contracts for Cairo, constructing an ERC721 contract requires integrating both the ERC721 and SRC5 components and setting up the constructor. Here's an example of a basic contract: [,javascript] From 033df1862233880f128aecd33833d79390d5cd07 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 13 Nov 2023 13:26:04 -0500 Subject: [PATCH 242/246] add erc20 camel impl to examples --- docs/modules/ROOT/pages/erc20.adoc | 4 ++++ docs/modules/ROOT/pages/guides/erc20-supply.adoc | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/docs/modules/ROOT/pages/erc20.adoc b/docs/modules/ROOT/pages/erc20.adoc index d5847ffbb..7f324c18b 100644 --- a/docs/modules/ROOT/pages/erc20.adoc +++ b/docs/modules/ROOT/pages/erc20.adoc @@ -90,6 +90,8 @@ mod MyToken { impl ERC20Impl = ERC20Component::ERC20Impl; #[abi(embed_v0)] impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl; + #[abi(embed_v0)] + impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl; impl ERC20InternalImpl = ERC20Component::InternalImpl; #[storage] @@ -179,6 +181,8 @@ mod MyToken { #[abi(embed_v0)] impl ERC20Impl = ERC20Component::ERC20Impl; + #[abi(embed_v0)] + impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl; impl ERC20InternalImpl = ERC20Component::InternalImpl; #[storage] diff --git a/docs/modules/ROOT/pages/guides/erc20-supply.adoc b/docs/modules/ROOT/pages/guides/erc20-supply.adoc index a51a2a0d2..46882c8c1 100644 --- a/docs/modules/ROOT/pages/guides/erc20-supply.adoc +++ b/docs/modules/ROOT/pages/guides/erc20-supply.adoc @@ -24,6 +24,8 @@ mod MyToken { impl ERC20Impl = ERC20Component::ERC20Impl; #[abi(embed_v0)] impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl; + #[abi(embed_v0)] + impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl; impl InternalImpl = ERC20Component::InternalImpl; #[storage] @@ -79,6 +81,8 @@ mod MyToken { impl ERC20Impl = ERC20Component::ERC20Impl; #[abi(embed_v0)] impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl; + #[abi(embed_v0)] + impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl; impl InternalImpl = ERC20Component::InternalImpl; #[storage] From 51f6d77dc004573ebeb4220c826669ec1400018f Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 13 Nov 2023 14:04:23 -0500 Subject: [PATCH 243/246] fix usage section --- docs/modules/ROOT/pages/erc721.adoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 0e1f55ada..725b089e7 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -91,7 +91,8 @@ The design for `SRC5` is similar to OpenZeppelin's {erc165-storage}. :mint-api: xref:api/erc721.adoc#ERC721-_mint[_mint] -Using Contracts for Cairo, constructing an ERC721 contract requires integrating both the ERC721 and SRC5 components and setting up the constructor. +Using Contracts for Cairo, constructing an ERC721 contract requires integrating both `ERC721Component` and `SRC5Component`. +The contract should also set up the constructor to initialize the token's name, symbol, and interface support. Here's an example of a basic contract: [,javascript] From 563e177734ef557bd6a26ef9ef454139612ce52a Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Wed, 22 Nov 2023 10:20:16 -0500 Subject: [PATCH 244/246] Apply suggestions from code review Co-authored-by: Eric Nordelo --- docs/modules/ROOT/pages/api/erc721.adoc | 2 +- docs/modules/ROOT/pages/erc721.adoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 8db75ca46..37da7d47d 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -252,7 +252,7 @@ NOTE: Implementing xref:api/introspection.adoc#SRC5Component[SRC5Component] is a * xref:#IERC721-Transfer[`++Transfer(from, to, token_id)++`] -- -==== External functions +==== Embeddable functions [.contract-item] [[ERC721-balance_of]] diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 725b089e7..2089cf74e 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -11,7 +11,7 @@ The ERC721 token standard is a specification for {token-types}, or more colloqui :compatibility: xref:/erc721.adoc#erc721_compatibility[ERC721 Compatibility] :ierc721-interface: xref:/erc721.adoc#ierc721[IERC721] :ierc721metadata-interface: xref:/erc721.adoc#ierc721metadata[IERC721Metadata] -:dual-interfaces: xref:interfaces.adoc##dual_interfaces[Dual interfaces] +:dual-interfaces: xref:interfaces.adoc#dual_interfaces[Dual interfaces] The following interface represents the full ABI of the Contracts for Cairo `ERC721Component`. The interface includes the <> standard interface and the optional <> interface. From b6e512cbfd5ca427403ad4baeb87270c3a72514c Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 22 Nov 2023 15:04:37 -0500 Subject: [PATCH 245/246] reorder components in code examples --- docs/modules/ROOT/pages/erc721.adoc | 32 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 2089cf74e..c864b2f77 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -103,12 +103,8 @@ mod MyNFT { use openzeppelin::token::erc721::ERC721Component; use starknet::ContractAddress; - component!(path: SRC5Component, storage: src5, event: SRC5Event); component!(path: ERC721Component, storage: erc721, event: ERC721Event); - - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; + component!(path: SRC5Component, storage: src5, event: SRC5Event); // ERC721 #[abi(embed_v0)] @@ -122,6 +118,10 @@ mod MyNFT { ERC721Component::ERC721MetadataCamelOnlyImpl; impl ERC721InternalImpl = ERC721Component::InternalImpl; + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + #[storage] struct Storage { #[substorage(v0)] @@ -134,9 +134,9 @@ mod MyNFT { #[derive(Drop, starknet::Event)] enum Event { #[flat] - SRC5Event: SRC5Component::Event, + ERC721Event: ERC721Component::Event, #[flat] - ERC721Event: ERC721Component::Event + SRC5Event: SRC5Component::Event } #[constructor] @@ -228,12 +228,8 @@ mod MyTokenReceiver { use openzeppelin::token::erc721::ERC721ReceiverComponent; use starknet::ContractAddress; - component!(path: SRC5Component, storage: src5, event: SRC5Event); component!(path: ERC721ReceiverComponent, storage: erc721_receiver, event: ERC721ReceiverEvent); - - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; + component!(path: SRC5Component, storage: src5, event: SRC5Event); // ERC721Receiver #[abi(embed_v0)] @@ -242,21 +238,25 @@ mod MyTokenReceiver { impl ERC721ReceiverCamelImpl = ERC721ReceiverComponent::ERC721ReceiverCamelImpl; impl ERC721ReceiverInternalImpl = ERC721ReceiverComponent::InternalImpl; + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + #[storage] struct Storage { #[substorage(v0)] - src5: SRC5Component::Storage, + erc721_receiver: ERC721ReceiverComponent::Storage, #[substorage(v0)] - erc721_receiver: ERC721ReceiverComponent::Storage + src5: SRC5Component::Storage } #[event] #[derive(Drop, starknet::Event)] enum Event { #[flat] - SRC5Event: SRC5Component::Event, + ERC721ReceiverEvent: ERC721ReceiverComponent::Event, #[flat] - ERC721ReceiverEvent: ERC721ReceiverComponent::Event + SRC5Event: SRC5Component::Event } #[constructor] From 054caeed6b2a06070794aa4584203079a93c87ee Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Fri, 24 Nov 2023 17:33:57 -0500 Subject: [PATCH 246/246] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Martín Triay --- docs/modules/ROOT/pages/api/erc721.adoc | 2 +- docs/modules/ROOT/pages/erc721.adoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 37da7d47d..29192dad8 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -576,7 +576,7 @@ See <>. use openzeppelin::token::erc721::interface::IERC721Receiver; ``` -Interface for any contract that wants to support safe token transfers from ERC721 asset contracts. +Interface for contracts that support receiving `safe_transfer_from` transfers. [.contract-index] .{inner-src5} diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index c864b2f77..484feeea9 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -1,6 +1,6 @@ = ERC721 -:token-types: https://docs.openzeppelin.com/contracts/4.x/tokens#different-kinds-of-tokens[non-fungible tokens] +:token-types: https://docs.openzeppelin.com/contracts/5.x/tokens#different-kinds-of-tokens[non-fungible tokens] :eip721: https://eips.ethereum.org/EIPS/eip-721[EIP-721] The ERC721 token standard is a specification for {token-types}, or more colloquially: NFTs.