├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature-request.md ├── pull_request_template.md └── workflows │ └── verify_cairo_programs.yml ├── .gitignore ├── .npmrc ├── .tool-versions ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── Scarb.lock ├── Scarb.toml ├── _typos.toml ├── components ├── ThemeImage.tsx ├── useSidebarToggle.tsx ├── useTheme.tsx └── useTracking.tsx ├── footer.tsx ├── layout.tsx ├── listings ├── advanced-concepts │ ├── commit_reveal │ │ ├── .gitignore │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── commit_reveal.cairo │ │ │ └── lib.cairo │ ├── ecdsa_verification │ │ ├── .gitignore │ │ ├── Scarb.lock │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── ecdsa_verification.cairo │ │ │ └── lib.cairo │ ├── hash_solidity_compatible │ │ ├── .gitignore │ │ ├── Scarb.lock │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── contract.cairo │ │ │ ├── lib.cairo │ │ │ └── tests.cairo │ ├── hash_trait │ │ ├── .gitignore │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── hash_trait.cairo │ │ │ └── lib.cairo │ ├── library_calls │ │ ├── .gitignore │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── lib.cairo │ │ │ ├── library_call.cairo │ │ │ └── tests.cairo │ ├── sierra_ir │ │ ├── .gitignore │ │ ├── Scarb.toml │ │ ├── simple_program.sierra │ │ └── src │ │ │ ├── lib.cairo │ │ │ └── simple_program.cairo │ ├── simple_account │ │ ├── .gitignore │ │ ├── Scarb.lock │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── lib.cairo │ │ │ ├── simple_account.cairo │ │ │ └── tests.cairo │ ├── store_using_packing │ │ ├── .gitignore │ │ ├── Scarb.lock │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── contract.cairo │ │ │ ├── lib.cairo │ │ │ └── tests.cairo │ ├── struct_as_mapping_key │ │ ├── .gitignore │ │ ├── Scarb.lock │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── contract.cairo │ │ │ ├── lib.cairo │ │ │ └── test.cairo │ ├── verify_proofs │ │ ├── .gitignore │ │ ├── Scarb.toml │ │ ├── package.json │ │ └── src │ │ │ ├── circuit │ │ │ ├── circuit.circom │ │ │ └── input.json │ │ │ ├── contract.cairo │ │ │ ├── lib.cairo │ │ │ └── verifier │ │ │ ├── groth16_verifier.cairo │ │ │ └── groth16_verifier_constants.cairo │ └── write_to_any_slot │ │ ├── .gitignore │ │ ├── Scarb.lock │ │ ├── Scarb.toml │ │ └── src │ │ ├── contract.cairo │ │ ├── lib.cairo │ │ └── tests.cairo ├── applications │ ├── advanced_factory │ │ ├── .gitignore │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── contract.cairo │ │ │ ├── lib.cairo │ │ │ ├── mock_upgrade.cairo │ │ │ └── tests.cairo │ ├── coin_flip │ │ ├── .gitignore │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── contract.cairo │ │ │ ├── lib.cairo │ │ │ ├── mock_randomness.cairo │ │ │ └── tests.cairo │ ├── components │ │ ├── .gitignore │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── countable.cairo │ │ │ ├── lib.cairo │ │ │ ├── others.cairo │ │ │ ├── others │ │ │ └── switch_collision.cairo │ │ │ ├── ownable.cairo │ │ │ └── switchable.cairo │ ├── components_dependencies │ │ ├── .gitignore │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── contract_countable.cairo │ │ │ ├── contract_countable_switchable.cairo │ │ │ ├── contract_countable_switchable_internal.cairo │ │ │ ├── countable_dep_switch.cairo │ │ │ ├── countable_internal_dep_switch.cairo │ │ │ └── lib.cairo │ ├── constant_product_amm │ │ ├── .gitignore │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── contracts.cairo │ │ │ ├── lib.cairo │ │ │ └── tests.cairo │ ├── crowdfunding │ │ ├── .gitignore │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── campaign.cairo │ │ │ ├── campaign │ │ │ └── pledgeable.cairo │ │ │ ├── lib.cairo │ │ │ ├── mock_upgrade.cairo │ │ │ └── tests.cairo │ ├── erc20 │ │ ├── .gitignore │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── lib.cairo │ │ │ └── token.cairo │ ├── erc721 │ │ ├── .gitignore │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── erc721.cairo │ │ │ ├── interfaces.cairo │ │ │ ├── lib.cairo │ │ │ ├── mocks.cairo │ │ │ ├── mocks │ │ │ ├── account.cairo │ │ │ ├── non_receiver.cairo │ │ │ └── receiver.cairo │ │ │ └── tests.cairo │ ├── l1_l2_token_bridge │ │ ├── .gitignore │ │ ├── Scarb.toml │ │ ├── solidity │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── foundry.toml │ │ │ ├── lib │ │ │ │ ├── forge-std │ │ │ │ │ ├── .gitattributes │ │ │ │ │ ├── .github │ │ │ │ │ │ └── workflows │ │ │ │ │ │ │ ├── ci.yml │ │ │ │ │ │ │ └── sync.yml │ │ │ │ │ ├── .gitignore │ │ │ │ │ ├── CONTRIBUTING.md │ │ │ │ │ ├── LICENSE-APACHE │ │ │ │ │ ├── LICENSE-MIT │ │ │ │ │ ├── README.md │ │ │ │ │ ├── foundry.toml │ │ │ │ │ ├── package.json │ │ │ │ │ ├── scripts │ │ │ │ │ │ └── vm.py │ │ │ │ │ ├── src │ │ │ │ │ │ ├── Base.sol │ │ │ │ │ │ ├── Script.sol │ │ │ │ │ │ ├── StdAssertions.sol │ │ │ │ │ │ ├── StdChains.sol │ │ │ │ │ │ ├── StdCheats.sol │ │ │ │ │ │ ├── StdError.sol │ │ │ │ │ │ ├── StdInvariant.sol │ │ │ │ │ │ ├── StdJson.sol │ │ │ │ │ │ ├── StdMath.sol │ │ │ │ │ │ ├── StdStorage.sol │ │ │ │ │ │ ├── StdStyle.sol │ │ │ │ │ │ ├── StdToml.sol │ │ │ │ │ │ ├── StdUtils.sol │ │ │ │ │ │ ├── Test.sol │ │ │ │ │ │ ├── Vm.sol │ │ │ │ │ │ ├── console.sol │ │ │ │ │ │ ├── console2.sol │ │ │ │ │ │ ├── interfaces │ │ │ │ │ │ │ ├── IERC1155.sol │ │ │ │ │ │ │ ├── IERC165.sol │ │ │ │ │ │ │ ├── IERC20.sol │ │ │ │ │ │ │ ├── IERC4626.sol │ │ │ │ │ │ │ ├── IERC721.sol │ │ │ │ │ │ │ └── IMulticall3.sol │ │ │ │ │ │ ├── mocks │ │ │ │ │ │ │ ├── MockERC20.sol │ │ │ │ │ │ │ └── MockERC721.sol │ │ │ │ │ │ └── safeconsole.sol │ │ │ │ │ └── test │ │ │ │ │ │ ├── StdAssertions.t.sol │ │ │ │ │ │ ├── StdChains.t.sol │ │ │ │ │ │ ├── StdCheats.t.sol │ │ │ │ │ │ ├── StdError.t.sol │ │ │ │ │ │ ├── StdJson.t.sol │ │ │ │ │ │ ├── StdMath.t.sol │ │ │ │ │ │ ├── StdStorage.t.sol │ │ │ │ │ │ ├── StdStyle.t.sol │ │ │ │ │ │ ├── StdToml.t.sol │ │ │ │ │ │ ├── StdUtils.t.sol │ │ │ │ │ │ ├── Vm.t.sol │ │ │ │ │ │ ├── compilation │ │ │ │ │ │ ├── CompilationScript.sol │ │ │ │ │ │ ├── CompilationScriptBase.sol │ │ │ │ │ │ ├── CompilationTest.sol │ │ │ │ │ │ └── CompilationTestBase.sol │ │ │ │ │ │ ├── fixtures │ │ │ │ │ │ ├── broadcast.log.json │ │ │ │ │ │ ├── test.json │ │ │ │ │ │ └── test.toml │ │ │ │ │ │ └── mocks │ │ │ │ │ │ ├── MockERC20.t.sol │ │ │ │ │ │ └── MockERC721.t.sol │ │ │ │ └── starknet │ │ │ │ │ ├── IStarknetMessaging.sol │ │ │ │ │ ├── IStarknetMessagingEvents.sol │ │ │ │ │ ├── NamedStorage.sol │ │ │ │ │ └── StarknetMessaging.sol │ │ │ ├── src │ │ │ │ ├── IMintableToken.sol │ │ │ │ ├── IMintableTokenEvents.sol │ │ │ │ ├── MintableTokenMock.sol │ │ │ │ ├── StarknetMessagingLocal.sol │ │ │ │ └── TokenBridge.sol │ │ │ └── test │ │ │ │ └── TokenBridge.t.sol │ │ └── src │ │ │ ├── contract.cairo │ │ │ ├── lib.cairo │ │ │ ├── mintable_token_mock.cairo │ │ │ └── tests.cairo │ ├── merkle_tree │ │ ├── .gitignore │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── contract.cairo │ │ │ ├── lib.cairo │ │ │ └── tests.cairo │ ├── nft_dutch_auction │ │ ├── .gitignore │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── erc721.cairo │ │ │ ├── lib.cairo │ │ │ └── nft_dutch_auction.cairo │ ├── simple_storage_starknetjs │ │ ├── .env.example │ │ ├── .gitignore │ │ ├── Scarb.lock │ │ ├── Scarb.toml │ │ ├── abi.json │ │ ├── index.js │ │ └── src │ │ │ ├── lib.cairo │ │ │ └── storage.cairo │ ├── simple_vault │ │ ├── .gitignore │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── lib.cairo │ │ │ └── simple_vault.cairo │ ├── staking │ │ ├── .gitignore │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── contract.cairo │ │ │ ├── lib.cairo │ │ │ └── tests │ │ │ ├── staking_tests.cairo │ │ │ └── tokens.cairo │ ├── timelock │ │ ├── .gitignore │ │ ├── Scarb.toml │ │ └── src │ │ │ ├── erc721.cairo │ │ │ ├── lib.cairo │ │ │ ├── tests │ │ │ ├── timelock.cairo │ │ │ └── utils.cairo │ │ │ └── timelock.cairo │ └── upgradeable_contract │ │ ├── .gitignore │ │ ├── Scarb.lock │ │ ├── Scarb.toml │ │ └── src │ │ ├── lib.cairo │ │ ├── tests.cairo │ │ ├── upgradeable_contract_v0.cairo │ │ └── upgradeable_contract_v1.cairo ├── cairo_cheatsheet │ ├── Scarb.lock │ ├── Scarb.toml │ └── src │ │ ├── array_example.cairo │ │ ├── dict_example.cairo │ │ ├── enum_example.cairo │ │ ├── felt_example.cairo │ │ ├── if_let_example.cairo │ │ ├── lib.cairo │ │ ├── loop_example.cairo │ │ ├── mapping_example.cairo │ │ ├── match_example.cairo │ │ ├── struct_example.cairo │ │ ├── tuple_example.cairo │ │ ├── type_casting_example.cairo │ │ ├── while_example.cairo │ │ └── while_let_example.cairo └── getting-started │ ├── calling_other_contracts │ ├── .gitignore │ ├── Scarb.lock │ ├── Scarb.toml │ └── src │ │ ├── caller.cairo │ │ └── lib.cairo │ ├── constructor │ ├── counter │ ├── .gitignore │ ├── Scarb.lock │ ├── Scarb.toml │ └── src │ │ ├── counter.cairo │ │ └── lib.cairo │ ├── custom_type_entrypoints │ ├── .gitignore │ ├── Scarb.lock │ ├── Scarb.toml │ └── src │ │ ├── contract.cairo │ │ └── lib.cairo │ ├── errors │ ├── .gitignore │ ├── Scarb.lock │ ├── Scarb.toml │ └── src │ │ ├── custom_errors.cairo │ │ ├── lib.cairo │ │ ├── simple_errors.cairo │ │ └── vault_errors.cairo │ ├── events │ ├── .gitignore │ ├── Scarb.lock │ ├── Scarb.toml │ └── src │ │ ├── counter.cairo │ │ └── lib.cairo │ ├── factory │ ├── .gitignore │ ├── Scarb.lock │ ├── Scarb.toml │ └── src │ │ ├── lib.cairo │ │ └── simple_factory.cairo │ ├── mappings │ ├── .gitignore │ ├── Scarb.lock │ ├── Scarb.toml │ └── src │ │ ├── lib.cairo │ │ └── mappings.cairo │ ├── storage │ ├── .gitignore │ ├── Scarb.lock │ ├── Scarb.toml │ └── src │ │ ├── contract.cairo │ │ ├── lib.cairo │ │ └── minimal_contract.cairo │ ├── storing_custom_types │ ├── .gitignore │ ├── Scarb.lock │ ├── Scarb.toml │ └── src │ │ ├── contract.cairo │ │ └── lib.cairo │ ├── testing_how_to │ ├── .gitignore │ ├── Scarb.lock │ ├── Scarb.toml │ └── src │ │ ├── contract.cairo │ │ ├── lib.cairo │ │ └── test_contract.cairo │ ├── variables │ ├── .gitignore │ ├── Scarb.lock │ ├── Scarb.toml │ ├── src │ │ ├── global_variables.cairo │ │ ├── lib.cairo │ │ ├── local_variables.cairo │ │ └── storage_variables.cairo │ └── storage_variables.sierra │ └── visibility │ ├── .gitignore │ ├── Scarb.lock │ ├── Scarb.toml │ └── src │ ├── lib.cairo │ └── visibility.cairo ├── package.json ├── pages ├── advanced-concepts │ ├── account_abstraction │ │ ├── account_contract.md │ │ ├── account_spending_limits.md │ │ └── index.md │ ├── commit-reveal.md │ ├── hash-solidity-compatible.md │ ├── hashing.md │ ├── library_calls.md │ ├── optimisations │ │ └── store_using_packing.md │ ├── plugins.md │ ├── sierra_ir.md │ ├── sierra_ir_storage_contract.md │ ├── signature_verification.md │ ├── struct-mapping-key.md │ ├── verify_proofs.md │ └── write_to_any_slot.md ├── applications │ ├── advanced_factory.md │ ├── constant-product-amm.md │ ├── crowdfunding.md │ ├── erc20.md │ ├── erc721.md │ ├── l1_l2_token_bridge.md │ ├── merkle_tree.md │ ├── nft_dutch_auction.md │ ├── random_number_generator.md │ ├── signature_verification.md │ ├── simple_storage_starknetjs.md │ ├── simple_vault.md │ ├── staking.md │ ├── timelock.md │ └── upgradeable_contract.md ├── cairo_cheatsheet │ ├── arrays.md │ ├── dict.md │ ├── enums.md │ ├── felt.md │ ├── if_let.md │ ├── loop.md │ ├── mapping.md │ ├── match.md │ ├── struct.md │ ├── tuples.md │ ├── type_casting.md │ ├── while.md │ └── while_let.md ├── components │ ├── collisions.md │ ├── dependencies.md │ ├── how_to.md │ └── ownable.md ├── getting-started │ ├── basics │ │ ├── constructor.md │ │ ├── counter.md │ │ ├── custom-types-in-entrypoints.md │ │ ├── documentation.md │ │ ├── errors.md │ │ ├── events.md │ │ ├── mappings.md │ │ ├── storage.md │ │ ├── storing_custom_types.md │ │ ├── variables.md │ │ └── visibility-mutability.md │ ├── env_setup.md │ ├── interacting │ │ ├── calling_other_contracts.md │ │ ├── factory.md │ │ └── how_to_deploy.md │ ├── syscalls.md │ └── testing │ │ ├── index.mdx │ │ ├── testing-cairo-test.md │ │ └── testing-snforge.md └── index.mdx ├── patches └── vocs.patch ├── pnpm-lock.yaml ├── postcss.config.js ├── public ├── collaborators │ ├── Juno.svg │ ├── Nethermind.svg │ ├── Onlydust.svg │ ├── PoweredByNethermind.svg │ ├── Starknet.svg │ ├── Starknet_RPC.svg │ ├── Starknet_Remix_Plugin.svg │ └── Voyager.svg ├── merkle_root.png └── svg │ ├── Horizontal_Dark.svg │ ├── Horizontal_Light.svg │ ├── Icon_Dark.svg │ ├── Icon_Light.svg │ ├── Vertical_Dark.svg │ └── Vertical_Light.svg ├── routes.ts ├── scripts ├── cairo_programs_verifier.sh ├── test_resolver.sh └── test_runner.sh ├── styles.css ├── tailwind.config.js ├── tsconfig.json └── vocs.config.ts /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Screenshots** 20 | If applicable, add screenshots to help explain your problem. 21 | 22 | **Checklist:** 23 | - Using the correct tooling versions, from https://github.com/NethermindEth/StarknetByExample/blob/dev/.tool-versions 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea of example or concept to showcase 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the concept/idea you would like to see** 11 | 12 | **Is it a new example or a modification/extension of a current one** 13 | If you consider editing some content, explain exactly why and what. 14 | 15 | **Additional context** 16 | Add any other context or screenshots about the request here. 17 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | **Issue**: Close #issue-number 2 | 3 | ### Description 4 | 5 | 6 | 7 | 16 | -------------------------------------------------------------------------------- /.github/workflows/verify_cairo_programs.yml: -------------------------------------------------------------------------------- 1 | name: Verify Cairo programs compilation 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - dev 7 | - main 8 | workflow_dispatch: 9 | 10 | jobs: 11 | typos: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: crate-ci/typos@master 16 | 17 | compile_and_verify: 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@v4 23 | with: 24 | fetch-depth: 0 25 | 26 | - name: Configure upstream repository 27 | run: | 28 | git remote add upstream https://github.com/NethermindEth/StarknetByExample 29 | git fetch upstream 30 | 31 | - name: Install scarb 32 | uses: software-mansion/setup-scarb@v1 33 | 34 | - name: Install snforge 35 | uses: foundry-rs/setup-snfoundry@v3 36 | 37 | - name: Verify changes 38 | run: | 39 | chmod +x scripts/cairo_programs_verifier.sh 40 | ./scripts/cairo_programs_verifier.sh 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | dist/* 4 | 5 | # Editors tmp files. 6 | *~ 7 | .idea/ 8 | 9 | # Others 10 | .snfoundry_cache 11 | .vscode/settings.json 12 | **/starkli-wallet 13 | 14 | # From previous mdbook build 15 | book/* 16 | target/* 17 | src/**/*.md 18 | 19 | node_modules 20 | .DS_Store 21 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | node-linker=hoisted -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | scarb 2.10.1 2 | starknet-foundry 0.38.0 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Nethermind 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Starknet by Example 2 | 3 | Dev preview at: https://starknet-by-example-dev.voyager.online/ 4 | 5 | ## Description 6 | 7 | Starknet by Example is a collection of examples of how to use the [Cairo](https://github.com/starkware-libs/cairo) programming language to create smart contracts on Starknet. 8 | 9 | ## Contribute 10 | 11 | Please refer to the Contribution Guidelines page: 12 | - [Contributing](CONTRIBUTING.md#contributing) 13 | - [Table of Contents](CONTRIBUTING.md#table-of-contents) 14 | - [Setup](CONTRIBUTING.md#setup) 15 | - [Working with Markdown Files](CONTRIBUTING.md#working-with-markdown-files) 16 | - [Adding a new chapter](CONTRIBUTING.md#adding-a-new-chapter) 17 | - [Adding a new Cairo program](CONTRIBUTING.md#adding-a-new-cairo-program) 18 | - [Tests](CONTRIBUTING.md#tests) 19 | - [Use of region](CONTRIBUTING.md#use-of-region) 20 | - [Code of Conduct](CONTRIBUTING.md#code-of-conduct) 21 | - [Our Pledge](CONTRIBUTING.md#our-pledge) 22 | - [Our Standards](CONTRIBUTING.md#our-standards) 23 | - [Our Responsibilities](CONTRIBUTING.md#our-responsibilities) 24 | - [Scope](CONTRIBUTING.md#scope) 25 | - [Enforcement](CONTRIBUTING.md#enforcement) 26 | - [Attribution](CONTRIBUTING.md#attribution) 27 | -------------------------------------------------------------------------------- /Scarb.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "listings/getting-started/*", 4 | "listings/applications/*", 5 | "listings/advanced-concepts/*", 6 | "listings/cairo_cheatsheet", 7 | ] 8 | 9 | [workspace.scripts] 10 | test = "$(git rev-parse --show-toplevel)/scripts/test_resolver.sh" 11 | 12 | [workspace.tool.snforge] 13 | 14 | [workspace.dependencies] 15 | starknet = "2.10.1" 16 | cairo_test = "2.10.1" 17 | assert_macros = "2.10.1" 18 | snforge_std = "0.38.0" 19 | openzeppelin_account = "1.0.0" 20 | openzeppelin_introspection = "1.0.0" 21 | openzeppelin_presets = "1.0.0" 22 | openzeppelin_token = "1.0.0" 23 | openzeppelin_utils = "1.0.0" 24 | components = { path = "listings/applications/components" } 25 | pragma_lib = { git = "https://github.com/astraly-labs/pragma-lib", tag = "2.9.1" } 26 | garaga = { git = "https://github.com/keep-starknet-strange/garaga.git", tag = "v0.15.5" } 27 | 28 | [workspace.package] 29 | description = "Collection of examples of how to use the Cairo programming language to create smart contracts on Starknet." 30 | repository = "https://github.com/NethermindEth/StarknetByExample" 31 | homepage = "https://www.nethermind.io/" 32 | license = "MIT" 33 | authors = ["julio4", "msaug"] 34 | version = "0.1.0" 35 | edition = "2024_07" 36 | 37 | [tool] 38 | snforge.workspace = true 39 | -------------------------------------------------------------------------------- /_typos.toml: -------------------------------------------------------------------------------- 1 | [default] 2 | extend-ignore-identifiers-re = ["requestor", "REQUESTOR", "Requestor", "groth", "Groth"] 3 | 4 | [type.po] 5 | extend-glob = ["*.po", "*.css", "*.js"] 6 | check-file = false 7 | 8 | [files] 9 | extend-exclude = ["po/*.po", "listings/**/*.json"] 10 | -------------------------------------------------------------------------------- /components/ThemeImage.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { useTheme } from "./useTheme"; 3 | 4 | export const ThemeImage = ({ 5 | light, 6 | dark, 7 | alt, 8 | ...props 9 | }: { 10 | light: string; 11 | dark: string; 12 | alt: string; 13 | [key: string]: any; 14 | }) => { 15 | const [mounted, setMounted] = useState(false); 16 | const theme = useTheme(); 17 | 18 | useEffect(() => { 19 | setMounted(true); 20 | }, []); 21 | if (!mounted) { 22 | return {alt}; 23 | } 24 | 25 | // Client-side rendering after hydrating 26 | return {alt}; 27 | }; 28 | -------------------------------------------------------------------------------- /components/useSidebarToggle.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useCallback } from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | 4 | export const useSidebarToggle = () => { 5 | const createToggleButton = useCallback(() => { 6 | const SidebarToggle = () => ( 7 |
8 | 30 |
31 | ); 32 | 33 | return SidebarToggle; 34 | }, []); 35 | 36 | useEffect(() => { 37 | const targetNode = document.querySelector(".vocs_DocsLayout_gutterLeft"); 38 | if (!targetNode) { 39 | console.warn("Target node for sidebar toggle not found"); 40 | return; 41 | } 42 | 43 | const toggleContainer = document.createElement("div"); 44 | toggleContainer.className = "sidebar_toggle flex justify-center"; 45 | targetNode.appendChild(toggleContainer); 46 | 47 | const Toggle = createToggleButton(); 48 | const root = createRoot(toggleContainer); 49 | root.render(); 50 | 51 | // Cleanup 52 | return () => { 53 | root.unmount(); 54 | toggleContainer.remove(); 55 | }; 56 | }, [createToggleButton]); 57 | }; 58 | -------------------------------------------------------------------------------- /components/useTheme.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | 3 | export function useTheme() { 4 | const [theme, setTheme] = useState<"dark" | "light">(() => { 5 | if (typeof window === "undefined") return "light"; 6 | return document.documentElement.classList.contains("dark") 7 | ? "dark" 8 | : "light"; 9 | }); 10 | 11 | useEffect(() => { 12 | const handleThemeChange = () => { 13 | const newTheme = document.documentElement.classList.contains("dark") 14 | ? "dark" 15 | : "light"; 16 | setTheme(newTheme); 17 | }; 18 | 19 | // Watch for class changes on documentElement 20 | const observer = new MutationObserver(handleThemeChange); 21 | observer.observe(document.documentElement, { 22 | attributes: true, 23 | attributeFilter: ["class"], 24 | }); 25 | 26 | return () => { 27 | observer.disconnect(); 28 | }; 29 | }, []); 30 | 31 | return theme; 32 | } 33 | -------------------------------------------------------------------------------- /components/useTracking.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | 3 | const CLARITY_ID = "q6bz69wquw"; 4 | 5 | export const useTracking = () => { 6 | useEffect(() => { 7 | const script = document.createElement("script"); 8 | script.type = "text/javascript"; 9 | script.text = ` 10 | (function(c,l,a,r,i,t,y){ 11 | c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)}; 12 | t=l.createElement(r);t.async=1; 13 | t.src="https://www.clarity.ms/tag/"+i; 14 | y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y); 15 | })(window, document, "clarity", "script", "${CLARITY_ID}"); 16 | `; 17 | document.head.appendChild(script); 18 | return () => { 19 | document.head.removeChild(script); 20 | }; 21 | }, []); 22 | }; 23 | -------------------------------------------------------------------------------- /footer.tsx: -------------------------------------------------------------------------------- 1 | export default function Footer() { 2 | return ( 3 |
4 | Powered By Nethermind 9 |
10 | Released under the MIT License. 11 | © 2025 Nethermind. All Rights Reserved 12 |
13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /layout.tsx: -------------------------------------------------------------------------------- 1 | import { useSidebarToggle } from "./components/useSidebarToggle"; 2 | import { useTracking } from "./components/useTracking"; 3 | 4 | export default function Root({ children }: { children: React.ReactNode }) { 5 | useTracking(); 6 | useSidebarToggle(); 7 | return
{children}
; 8 | } 9 | -------------------------------------------------------------------------------- /listings/advanced-concepts/commit_reveal/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/advanced-concepts/commit_reveal/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "commit_reveal" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/advanced-concepts/commit_reveal/src/commit_reveal.cairo: -------------------------------------------------------------------------------- 1 | #[starknet::interface] 2 | pub trait ICommitmentRevealTrait { 3 | fn commit(ref self: T, commitment: felt252); 4 | fn reveal(self: @T, secret: felt252) -> bool; 5 | } 6 | 7 | // [!region contract] 8 | #[starknet::contract] 9 | mod CommitmentRevealTraits { 10 | use starknet::storage::{StoragePointerWriteAccess, StoragePointerReadAccess}; 11 | use core::hash::HashStateTrait; 12 | use core::pedersen::PedersenTrait; 13 | 14 | #[storage] 15 | struct Storage { 16 | commitment: felt252, 17 | } 18 | 19 | #[abi(embed_v0)] 20 | impl CommitmentRevealTrait of super::ICommitmentRevealTrait { 21 | fn commit(ref self: ContractState, commitment: felt252) { 22 | self.commitment.write(commitment); 23 | } 24 | 25 | fn reveal(self: @ContractState, secret: felt252) -> bool { 26 | let hash = PedersenTrait::new(secret).finalize(); 27 | self.commitment.read() == hash 28 | } 29 | } 30 | } 31 | // [!endregion contract] 32 | 33 | #[cfg(test)] 34 | mod tests { 35 | use super::{ICommitmentRevealTraitDispatcher, ICommitmentRevealTraitDispatcherTrait}; 36 | use core::hash::HashStateTrait; 37 | use core::pedersen::PedersenTrait; 38 | use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; 39 | 40 | fn deploy() -> ICommitmentRevealTraitDispatcher { 41 | let contract = declare("CommitmentRevealTraits").unwrap().contract_class(); 42 | let (contract_address, _) = contract.deploy(@array![]).unwrap(); 43 | ICommitmentRevealTraitDispatcher { contract_address } 44 | } 45 | 46 | #[test] 47 | fn commit_and_reveal() { 48 | let contract = deploy(); 49 | 50 | // [!region offchain] 51 | // Off-chain, compute the commitment hash for secret 52 | let secret = 'My secret'; 53 | let offchain_commitment = PedersenTrait::new(secret).finalize(); 54 | 55 | // Commit on-chain 56 | contract.commit(offchain_commitment); 57 | 58 | // Reveal on-chain and assert the result 59 | let reveal_result = contract.reveal(secret); 60 | // [!endregion offchain] 61 | assert_eq!(reveal_result, true); 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /listings/advanced-concepts/commit_reveal/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod commit_reveal; 2 | -------------------------------------------------------------------------------- /listings/advanced-concepts/ecdsa_verification/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/advanced-concepts/ecdsa_verification/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "ecdsa_verification" 6 | version = "0.1.0" 7 | -------------------------------------------------------------------------------- /listings/advanced-concepts/ecdsa_verification/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ecdsa_verification" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | cairo_test.workspace = true 11 | 12 | [scripts] 13 | test.workspace = true 14 | 15 | [[target.starknet-contract]] 16 | -------------------------------------------------------------------------------- /listings/advanced-concepts/ecdsa_verification/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod ecdsa_verification; 2 | -------------------------------------------------------------------------------- /listings/advanced-concepts/hash_solidity_compatible/.gitignore: -------------------------------------------------------------------------------- 1 | target -------------------------------------------------------------------------------- /listings/advanced-concepts/hash_solidity_compatible/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "hash_solidity_compatible" 6 | version.workspace = true 7 | -------------------------------------------------------------------------------- /listings/advanced-concepts/hash_solidity_compatible/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hash_solidity_compatible" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/advanced-concepts/hash_solidity_compatible/src/contract.cairo: -------------------------------------------------------------------------------- 1 | #[starknet::interface] 2 | pub trait ISolidityHashExample { 3 | fn hash_data(ref self: TContractState, input_data: Span) -> u256; 4 | } 5 | 6 | #[starknet::contract] 7 | mod SolidityHashExample { 8 | use core::keccak::keccak_u256s_be_inputs; 9 | use core::integer; 10 | 11 | #[storage] 12 | struct Storage {} 13 | 14 | #[abi(embed_v0)] 15 | impl SolidityHashExample of super::ISolidityHashExample { 16 | fn hash_data(ref self: ContractState, input_data: Span) -> u256 { 17 | let hashed = keccak_u256s_be_inputs(input_data); 18 | 19 | // Split the hashed value into two 128-bit segments 20 | let low: u128 = hashed.low; 21 | let high: u128 = hashed.high; 22 | 23 | // Reverse each 128-bit segment 24 | let reversed_low = integer::u128_byte_reverse(low); 25 | let reversed_high = integer::u128_byte_reverse(high); 26 | 27 | // Reverse merge the reversed segments back into a u256 value 28 | let compatible_hash = u256 { low: reversed_high, high: reversed_low }; 29 | 30 | compatible_hash 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /listings/advanced-concepts/hash_solidity_compatible/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod contract; 2 | 3 | #[cfg(test)] 4 | mod tests; 5 | -------------------------------------------------------------------------------- /listings/advanced-concepts/hash_solidity_compatible/src/tests.cairo: -------------------------------------------------------------------------------- 1 | use hash_solidity_compatible::contract::{ 2 | ISolidityHashExampleDispatcher, ISolidityHashExampleDispatcherTrait, 3 | }; 4 | use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; 5 | 6 | fn setup() -> ISolidityHashExampleDispatcher { 7 | let contract = declare("SolidityHashExample").unwrap().contract_class(); 8 | let (contract_address, _) = contract.deploy(@array![]).unwrap(); 9 | ISolidityHashExampleDispatcher { contract_address } 10 | } 11 | 12 | #[test] 13 | fn get_same_hash_solidity() { 14 | let contract = setup(); 15 | let hash_expected: u256 = 0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6; 16 | assert_eq!(contract.hash_data(array![1].span()), hash_expected); 17 | } 18 | -------------------------------------------------------------------------------- /listings/advanced-concepts/hash_trait/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/advanced-concepts/hash_trait/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hash_trait" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/advanced-concepts/hash_trait/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod hash_trait; 2 | -------------------------------------------------------------------------------- /listings/advanced-concepts/library_calls/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/advanced-concepts/library_calls/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "library_calls" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/advanced-concepts/library_calls/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod library_call; 2 | 3 | #[cfg(test)] 4 | mod tests; 5 | -------------------------------------------------------------------------------- /listings/advanced-concepts/library_calls/src/library_call.cairo: -------------------------------------------------------------------------------- 1 | // [!region library_dispatcher] 2 | #[starknet::interface] 3 | pub trait IMathUtils { 4 | fn add(ref self: T, x: u32, y: u32) -> u32; 5 | fn set_class_hash(ref self: T, class_hash: starknet::ClassHash); 6 | } 7 | 8 | // contract A 9 | #[starknet::contract] 10 | pub mod MathUtils { 11 | #[storage] 12 | struct Storage {} 13 | 14 | #[abi(embed_v0)] 15 | impl ImathUtilsImpl of super::IMathUtils { 16 | fn add(ref self: ContractState, x: u32, y: u32) -> u32 { 17 | x + y 18 | } 19 | 20 | fn set_class_hash(ref self: ContractState, class_hash: starknet::ClassHash) {} 21 | } 22 | } 23 | 24 | 25 | // contract B to make library call to the class of contract A 26 | #[starknet::contract] 27 | pub mod MathUtilsLibraryCall { 28 | use super::{IMathUtilsDispatcherTrait, IMathUtilsLibraryDispatcher}; 29 | use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; 30 | 31 | #[storage] 32 | struct Storage { 33 | value: u32, 34 | lib_class_hash: starknet::ClassHash, 35 | } 36 | 37 | #[abi(embed_v0)] 38 | impl MathUtils of super::IMathUtils { 39 | fn add(ref self: ContractState, x: u32, y: u32) -> u32 { 40 | IMathUtilsLibraryDispatcher { class_hash: self.lib_class_hash.read() }.add(x, y) 41 | } 42 | 43 | #[abi(embed_v0)] 44 | fn set_class_hash(ref self: ContractState, class_hash: starknet::ClassHash) { 45 | self.lib_class_hash.write(class_hash); 46 | } 47 | } 48 | } 49 | // [!endregion library_dispatcher] 50 | 51 | 52 | -------------------------------------------------------------------------------- /listings/advanced-concepts/library_calls/src/tests.cairo: -------------------------------------------------------------------------------- 1 | use library_calls::library_call::{IMathUtilsDispatcher, IMathUtilsDispatcherTrait}; 2 | use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; 3 | 4 | #[test] 5 | fn test_library_dispatcher() { 6 | let math_utils = declare("MathUtils").unwrap().contract_class(); 7 | 8 | let contract = declare("MathUtilsLibraryCall").unwrap().contract_class(); 9 | let (contract_address, _) = contract.deploy(@array![]).unwrap(); 10 | let contract = IMathUtilsDispatcher { contract_address }; 11 | 12 | contract.set_class_hash(*math_utils.class_hash); 13 | 14 | let mut result = contract.add(30, 5); 15 | assert_eq!(result, 35, "Wrong result"); 16 | } 17 | -------------------------------------------------------------------------------- /listings/advanced-concepts/sierra_ir/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/advanced-concepts/sierra_ir/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sierra_ir" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [lib] 7 | sierra-text = true 8 | 9 | [dependencies] 10 | starknet.workspace = true 11 | 12 | [dev-dependencies] 13 | cairo_test.workspace = true 14 | 15 | [scripts] 16 | test.workspace = true 17 | -------------------------------------------------------------------------------- /listings/advanced-concepts/sierra_ir/simple_program.sierra: -------------------------------------------------------------------------------- 1 | type felt252 = felt252 [storable: true, drop: true, dup: true, zero_sized: false]; 2 | 3 | libfunc felt252_add = felt252_add; 4 | libfunc store_temp = store_temp; 5 | 6 | felt252_add([0], [1]) -> ([2]); // 0 7 | store_temp([2]) -> ([2]); // 1 8 | return([2]); // 2 9 | 10 | sierra_ir::add_numbers@0([0]: felt252, [1]: felt252) -> (felt252); 11 | -------------------------------------------------------------------------------- /listings/advanced-concepts/sierra_ir/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod simple_program; 2 | -------------------------------------------------------------------------------- /listings/advanced-concepts/sierra_ir/src/simple_program.cairo: -------------------------------------------------------------------------------- 1 | fn add_numbers(a: felt252, b: felt252) -> felt252 { 2 | a + b 3 | } 4 | -------------------------------------------------------------------------------- /listings/advanced-concepts/simple_account/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/advanced-concepts/simple_account/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "ecdsa_verification" 6 | version = "0.1.0" 7 | -------------------------------------------------------------------------------- /listings/advanced-concepts/simple_account/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simple_account" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | openzeppelin_account.workspace = true 9 | openzeppelin_introspection.workspace = true 10 | 11 | [scripts] 12 | test.workspace = true 13 | 14 | [[target.starknet-contract]] 15 | -------------------------------------------------------------------------------- /listings/advanced-concepts/simple_account/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod simple_account; 2 | 3 | #[cfg(test)] 4 | mod tests; 5 | -------------------------------------------------------------------------------- /listings/advanced-concepts/simple_account/src/tests.cairo: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { // TODO 3 | } 4 | -------------------------------------------------------------------------------- /listings/advanced-concepts/store_using_packing/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/advanced-concepts/store_using_packing/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "store_using_packing" 6 | version.workspace = true 7 | -------------------------------------------------------------------------------- /listings/advanced-concepts/store_using_packing/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "store_using_packing" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/advanced-concepts/store_using_packing/src/contract.cairo: -------------------------------------------------------------------------------- 1 | #[derive(Copy, Serde, Drop)] 2 | pub struct Time { 3 | pub hour: u8, 4 | pub minute: u8, 5 | } 6 | 7 | #[starknet::interface] 8 | pub trait ITime { 9 | fn set(ref self: TContractState, value: Time); 10 | fn get(self: @TContractState) -> Time; 11 | } 12 | 13 | #[starknet::contract] 14 | mod TimeContract { 15 | use starknet::storage::{StoragePointerWriteAccess, StoragePointerReadAccess}; 16 | use super::Time; 17 | use starknet::storage_access::StorePacking; 18 | 19 | #[storage] 20 | struct Storage { 21 | time: Time, 22 | } 23 | 24 | impl TimePackable of StorePacking { 25 | fn pack(value: Time) -> felt252 { 26 | let msb: felt252 = 256 * value.hour.into(); 27 | let lsb: felt252 = value.minute.into(); 28 | msb + lsb 29 | } 30 | fn unpack(value: felt252) -> Time { 31 | let value: u16 = value.try_into().unwrap(); 32 | let (q, r) = DivRem::div_rem(value, 256_u16.try_into().unwrap()); 33 | let hour: u8 = Into::::into(q).try_into().unwrap(); 34 | let minute: u8 = Into::::into(r).try_into().unwrap(); 35 | Time { hour, minute } 36 | } 37 | } 38 | 39 | #[abi(embed_v0)] 40 | impl TimeContract of super::ITime { 41 | fn set(ref self: ContractState, value: Time) { 42 | // This will call the pack method of the TimePackable trait 43 | // and store the resulting felt252 44 | self.time.write(value); 45 | } 46 | fn get(self: @ContractState) -> Time { 47 | // This will read the felt252 value from storage 48 | // and return the result of the unpack method of the TimePackable trait 49 | return self.time.read(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /listings/advanced-concepts/store_using_packing/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod contract; 2 | 3 | #[cfg(test)] 4 | mod tests; 5 | -------------------------------------------------------------------------------- /listings/advanced-concepts/store_using_packing/src/tests.cairo: -------------------------------------------------------------------------------- 1 | use store_using_packing::contract::{Time, ITimeDispatcher, ITimeDispatcherTrait}; 2 | use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; 3 | 4 | #[test] 5 | fn test_packing() { 6 | // Set up. 7 | let contract = declare("TimeContract").unwrap().contract_class(); 8 | let (contract_address, _) = contract.deploy(@array![]).unwrap(); 9 | let contract = ITimeDispatcher { contract_address }; 10 | 11 | // Store a Time struct. 12 | let time = Time { hour: 1, minute: 2 }; 13 | contract.set(time); 14 | 15 | // Read the stored struct. 16 | let read_time: Time = contract.get(); 17 | assert_eq!(read_time.hour, time.hour); 18 | assert_eq!(read_time.minute, time.minute); 19 | } 20 | -------------------------------------------------------------------------------- /listings/advanced-concepts/struct_as_mapping_key/.gitignore: -------------------------------------------------------------------------------- 1 | target -------------------------------------------------------------------------------- /listings/advanced-concepts/struct_as_mapping_key/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "struct_as_mapping_key" 6 | version.workspace = true 7 | -------------------------------------------------------------------------------- /listings/advanced-concepts/struct_as_mapping_key/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "struct_as_mapping_key" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/advanced-concepts/struct_as_mapping_key/src/contract.cairo: -------------------------------------------------------------------------------- 1 | #[derive(Copy, Drop, Serde, Hash)] 2 | pub struct Pet { 3 | pub name: felt252, 4 | pub age: u8, 5 | pub owner: felt252, 6 | } 7 | 8 | #[starknet::interface] 9 | pub trait IPetRegistry { 10 | fn register_pet(ref self: TContractState, key: Pet, timestamp: u64); 11 | fn get_registration_date(self: @TContractState, key: Pet) -> u64; 12 | } 13 | 14 | #[starknet::contract] 15 | pub mod PetRegistry { 16 | use super::Pet; 17 | use starknet::storage::{Map, StorageMapReadAccess, StorageMapWriteAccess}; 18 | 19 | #[storage] 20 | struct Storage { 21 | registration_time: Map::, 22 | } 23 | 24 | #[abi(embed_v0)] 25 | impl PetRegistry of super::IPetRegistry { 26 | fn register_pet(ref self: ContractState, key: Pet, timestamp: u64) { 27 | self.registration_time.write(key, timestamp); 28 | } 29 | 30 | fn get_registration_date(self: @ContractState, key: Pet) -> u64 { 31 | self.registration_time.read(key) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /listings/advanced-concepts/struct_as_mapping_key/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod contract; 2 | 3 | #[cfg(test)] 4 | mod test; 5 | -------------------------------------------------------------------------------- /listings/advanced-concepts/struct_as_mapping_key/src/test.cairo: -------------------------------------------------------------------------------- 1 | use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; 2 | use struct_as_mapping_key::contract::{Pet, IPetRegistryDispatcher, IPetRegistryDispatcherTrait}; 3 | 4 | #[test] 5 | fn test_e2e() { 6 | // Set up. 7 | let contract = declare("PetRegistry").unwrap().contract_class(); 8 | let (contract_address, _) = contract.deploy(@array![]).unwrap(); 9 | let contract = IPetRegistryDispatcher { contract_address }; 10 | 11 | let pet = Pet { name: 'Cute Labrador', age: 5, owner: 'Louis' }; 12 | 13 | // Store a pet. 14 | contract.register_pet(pet, 1234); 15 | 16 | // Read the array. 17 | let registration_date = contract.get_registration_date(pet); 18 | assert_eq!(registration_date, 1234); 19 | } 20 | -------------------------------------------------------------------------------- /listings/advanced-concepts/verify_proofs/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | node_modules 3 | -------------------------------------------------------------------------------- /listings/advanced-concepts/verify_proofs/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "snarkjs" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | snforge_std.workspace = true 9 | openzeppelin_token.workspace = true 10 | garaga.workspace = true 11 | 12 | [dev-dependencies] 13 | cairo_test.workspace = true 14 | 15 | [scripts] 16 | test.workspace = true 17 | 18 | [[target.starknet-contract]] 19 | sierra = true -------------------------------------------------------------------------------- /listings/advanced-concepts/verify_proofs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "snarkjs", 3 | "version": "1.0.0", 4 | "description": "", 5 | "keywords": [], 6 | "author": "", 7 | "license": "ISC", 8 | "dependencies": { 9 | "circomlib": "^2.0.5" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /listings/advanced-concepts/verify_proofs/src/circuit/circuit.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../node_modules/circomlib/circuits/poseidon.circom"; 4 | 5 | template PasswordCheck() { 6 | // Public inputs 7 | signal input userAddress; 8 | signal input pwdHash; 9 | // Private input 10 | signal input pwd; 11 | 12 | // (Public) output 13 | signal output uniqueToUser; 14 | 15 | // Make sure password is the correct one by comparing its hash to the expected known hash 16 | component hasher = Poseidon(1); 17 | hasher.inputs[0] <== pwd; 18 | 19 | hasher.out === pwdHash; 20 | 21 | // Compute a number unique to user so that other users can't simply copy and use same proof 22 | // but instead have to execute this circuit to generate a proof unique to them 23 | component uniqueHasher = Poseidon(2); 24 | uniqueHasher.inputs[0] <== pwdHash; 25 | uniqueHasher.inputs[1] <== userAddress; 26 | 27 | uniqueToUser <== uniqueHasher.out; 28 | } 29 | 30 | component main {public [userAddress, pwdHash]} = PasswordCheck(); 31 | -------------------------------------------------------------------------------- /listings/advanced-concepts/verify_proofs/src/circuit/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "pwdHash": "16260938803047823847354854419633652218467975114284208787981985448019235110758", 3 | "userAddress": "0xabcd", 4 | "pwd": "2468" 5 | } -------------------------------------------------------------------------------- /listings/advanced-concepts/verify_proofs/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod contract; 2 | -------------------------------------------------------------------------------- /listings/advanced-concepts/write_to_any_slot/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/advanced-concepts/write_to_any_slot/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "write_to_any_slot" 6 | version.workspace = true 7 | -------------------------------------------------------------------------------- /listings/advanced-concepts/write_to_any_slot/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "write_to_any_slot" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/advanced-concepts/write_to_any_slot/src/contract.cairo: -------------------------------------------------------------------------------- 1 | #[starknet::interface] 2 | pub trait IWriteToAnySlots { 3 | fn write_slot(ref self: TContractState, value: u32); 4 | fn read_slot(self: @TContractState) -> u32; 5 | } 6 | 7 | #[starknet::contract] 8 | pub mod WriteToAnySlot { 9 | use starknet::syscalls::{storage_read_syscall, storage_write_syscall}; 10 | use core::poseidon::poseidon_hash_span; 11 | use starknet::StorageAddress; 12 | 13 | #[storage] 14 | struct Storage {} 15 | 16 | const SLOT_NAME: felt252 = 'test_slot'; 17 | 18 | #[abi(embed_v0)] 19 | impl WriteToAnySlot of super::IWriteToAnySlots { 20 | fn write_slot(ref self: ContractState, value: u32) { 21 | storage_write_syscall(0, get_address_from_name(SLOT_NAME), value.into()).unwrap(); 22 | } 23 | 24 | fn read_slot(self: @ContractState) -> u32 { 25 | storage_read_syscall(0, get_address_from_name(SLOT_NAME)).unwrap().try_into().unwrap() 26 | } 27 | } 28 | pub fn get_address_from_name(variable_name: felt252) -> StorageAddress { 29 | let mut data: Array = array![]; 30 | data.append(variable_name); 31 | let hashed_name: felt252 = poseidon_hash_span(data.span()); 32 | let MASK_250: u256 = 0x03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; 33 | // By taking the 250 least significant bits of the hash output, we get a valid 250bits 34 | // storage address. 35 | let result: felt252 = (hashed_name.into() & MASK_250).try_into().unwrap(); 36 | let result: StorageAddress = result.try_into().unwrap(); 37 | result 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /listings/advanced-concepts/write_to_any_slot/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod contract; 2 | 3 | #[cfg(test)] 4 | mod tests; 5 | -------------------------------------------------------------------------------- /listings/advanced-concepts/write_to_any_slot/src/tests.cairo: -------------------------------------------------------------------------------- 1 | use write_to_any_slot::contract::{IWriteToAnySlotsDispatcher, IWriteToAnySlotsDispatcherTrait}; 2 | use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; 3 | 4 | #[test] 5 | fn test_read_write() { 6 | // Set up. 7 | let contract = declare("WriteToAnySlot").unwrap().contract_class(); 8 | let (contract_address, _) = contract.deploy(@array![]).unwrap(); 9 | let mut contract = IWriteToAnySlotsDispatcher { contract_address }; 10 | 11 | // Write to slot. 12 | let value = 42; 13 | contract.write_slot(value); 14 | 15 | // Read from slot. 16 | let read_value = contract.read_slot(); 17 | assert_eq!(read_value, value); 18 | } 19 | -------------------------------------------------------------------------------- /listings/applications/advanced_factory/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .snfoundry_cache/ 3 | -------------------------------------------------------------------------------- /listings/applications/advanced_factory/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "advanced_factory" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | components.workspace = true 9 | crowdfunding = { path = "../crowdfunding" } 10 | 11 | [dev-dependencies] 12 | assert_macros.workspace = true 13 | snforge_std.workspace = true 14 | 15 | [scripts] 16 | test.workspace = true 17 | 18 | [[target.starknet-contract]] 19 | build-external-contracts = ["crowdfunding::campaign::Campaign"] 20 | -------------------------------------------------------------------------------- /listings/applications/advanced_factory/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod contract; 2 | mod mock_upgrade; 3 | 4 | #[cfg(test)] 5 | mod tests; 6 | -------------------------------------------------------------------------------- /listings/applications/advanced_factory/src/mock_upgrade.cairo: -------------------------------------------------------------------------------- 1 | #[starknet::contract] 2 | pub mod MockContract { 3 | #[storage] 4 | struct Storage {} 5 | #[event] 6 | #[derive(Drop, starknet::Event)] 7 | enum Event {} 8 | } 9 | -------------------------------------------------------------------------------- /listings/applications/coin_flip/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .snfoundry_cache/ 3 | -------------------------------------------------------------------------------- /listings/applications/coin_flip/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "coin_flip" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | openzeppelin_token.workspace = true 9 | pragma_lib.workspace = true 10 | 11 | [dev-dependencies] 12 | openzeppelin_presets.workspace = true 13 | assert_macros.workspace = true 14 | snforge_std.workspace = true 15 | 16 | [scripts] 17 | test.workspace = true 18 | 19 | [[target.starknet-contract]] 20 | build-external-contracts = ["openzeppelin_presets::erc20::ERC20Upgradeable"] 21 | -------------------------------------------------------------------------------- /listings/applications/coin_flip/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod contract; 2 | mod mock_randomness; 3 | 4 | #[cfg(test)] 5 | mod tests; 6 | -------------------------------------------------------------------------------- /listings/applications/components/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/applications/components/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "components" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [lib] 7 | 8 | [dependencies] 9 | starknet.workspace = true 10 | 11 | [dev-dependencies] 12 | assert_macros.workspace = true 13 | snforge_std.workspace = true 14 | 15 | [scripts] 16 | test.workspace = true 17 | 18 | [[target.starknet-contract]] 19 | -------------------------------------------------------------------------------- /listings/applications/components/src/lib.cairo: -------------------------------------------------------------------------------- 1 | // Components 2 | pub mod switchable; 3 | pub mod countable; 4 | pub mod ownable; 5 | 6 | // Not components but using them for specific examples 7 | mod others; 8 | -------------------------------------------------------------------------------- /listings/applications/components/src/others.cairo: -------------------------------------------------------------------------------- 1 | mod switch_collision; 2 | -------------------------------------------------------------------------------- /listings/applications/components_dependencies/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/applications/components_dependencies/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "components_dependencies" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | components = { path = "../components" } 9 | 10 | [dev-dependencies] 11 | cairo_test.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/applications/components_dependencies/src/lib.cairo: -------------------------------------------------------------------------------- 1 | // components 2 | mod countable_dep_switch; 3 | mod countable_internal_dep_switch; 4 | 5 | // contracts 6 | mod contract_countable; 7 | mod contract_countable_switchable; 8 | mod contract_countable_switchable_internal; 9 | -------------------------------------------------------------------------------- /listings/applications/constant_product_amm/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/applications/constant_product_amm/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "constant_product_amm" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | openzeppelin_token.workspace = true 9 | 10 | [dev-dependencies] 11 | cairo_test.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/applications/constant_product_amm/src/lib.cairo: -------------------------------------------------------------------------------- 1 | pub mod contracts; 2 | 3 | #[cfg(test)] 4 | mod tests; 5 | -------------------------------------------------------------------------------- /listings/applications/crowdfunding/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .snfoundry_cache/ 3 | -------------------------------------------------------------------------------- /listings/applications/crowdfunding/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crowdfunding" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [lib] 7 | 8 | [dependencies] 9 | starknet.workspace = true 10 | openzeppelin_token.workspace = true 11 | openzeppelin_presets.workspace = true 12 | components.workspace = true 13 | 14 | [dev-dependencies] 15 | assert_macros.workspace = true 16 | snforge_std.workspace = true 17 | 18 | [scripts] 19 | test.workspace = true 20 | 21 | [[target.starknet-contract]] 22 | build-external-contracts = ["openzeppelin_presets::erc20::ERC20Upgradeable"] 23 | -------------------------------------------------------------------------------- /listings/applications/crowdfunding/src/lib.cairo: -------------------------------------------------------------------------------- 1 | pub mod campaign; 2 | mod mock_upgrade; 3 | 4 | #[cfg(test)] 5 | mod tests; 6 | -------------------------------------------------------------------------------- /listings/applications/erc20/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/applications/erc20/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "erc20" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [lib] 7 | 8 | [dependencies] 9 | starknet.workspace = true 10 | 11 | [dev-dependencies] 12 | assert_macros.workspace = true 13 | snforge_std.workspace = true 14 | 15 | [scripts] 16 | test.workspace = true 17 | 18 | [[target.starknet-contract]] 19 | -------------------------------------------------------------------------------- /listings/applications/erc20/src/lib.cairo: -------------------------------------------------------------------------------- 1 | pub mod token; 2 | -------------------------------------------------------------------------------- /listings/applications/erc721/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/applications/erc721/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "erc721" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | openzeppelin_account.workspace = true 9 | openzeppelin_introspection.workspace = true 10 | 11 | [dev-dependencies] 12 | assert_macros.workspace = true 13 | snforge_std.workspace = true 14 | 15 | [scripts] 16 | test.workspace = true 17 | 18 | [[target.starknet-contract]] 19 | -------------------------------------------------------------------------------- /listings/applications/erc721/src/lib.cairo: -------------------------------------------------------------------------------- 1 | pub mod erc721; 2 | pub mod interfaces; 3 | mod mocks; 4 | 5 | #[cfg(test)] 6 | mod tests; 7 | -------------------------------------------------------------------------------- /listings/applications/erc721/src/mocks.cairo: -------------------------------------------------------------------------------- 1 | pub mod account; 2 | pub mod receiver; 3 | pub mod non_receiver; 4 | 5 | pub use account::AccountMock; 6 | pub use non_receiver::NonReceiverMock; 7 | pub use receiver::ERC721ReceiverMock; 8 | -------------------------------------------------------------------------------- /listings/applications/erc721/src/mocks/account.cairo: -------------------------------------------------------------------------------- 1 | //! Copied with modifications from OpenZeppelin's repo 2 | //! https://github.com/OpenZeppelin/cairo-contracts/blob/6e60ba9310fa7953f045d0c30b343b0ffc168c14/packages/test_common/src/mocks/account.cairo 3 | 4 | #[starknet::contract(account)] 5 | pub mod AccountMock { 6 | use openzeppelin_account::AccountComponent; 7 | use openzeppelin_introspection::src5::SRC5Component; 8 | 9 | component!(path: AccountComponent, storage: account, event: AccountEvent); 10 | component!(path: SRC5Component, storage: src5, event: SRC5Event); 11 | 12 | // Account 13 | #[abi(embed_v0)] 14 | impl SRC6Impl = AccountComponent::SRC6Impl; 15 | #[abi(embed_v0)] 16 | impl DeclarerImpl = AccountComponent::DeclarerImpl; 17 | #[abi(embed_v0)] 18 | impl DeployableImpl = AccountComponent::DeployableImpl; 19 | impl AccountInternalImpl = AccountComponent::InternalImpl; 20 | 21 | // SCR5 22 | #[abi(embed_v0)] 23 | impl SRC5Impl = SRC5Component::SRC5Impl; 24 | 25 | #[storage] 26 | pub struct Storage { 27 | #[substorage(v0)] 28 | pub account: AccountComponent::Storage, 29 | #[substorage(v0)] 30 | pub src5: SRC5Component::Storage, 31 | } 32 | 33 | #[event] 34 | #[derive(Drop, starknet::Event)] 35 | enum Event { 36 | #[flat] 37 | AccountEvent: AccountComponent::Event, 38 | #[flat] 39 | SRC5Event: SRC5Component::Event, 40 | } 41 | 42 | #[constructor] 43 | fn constructor(ref self: ContractState, public_key: felt252) { 44 | self.account.initializer(public_key); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /listings/applications/erc721/src/mocks/non_receiver.cairo: -------------------------------------------------------------------------------- 1 | #[starknet::contract] 2 | pub mod NonReceiverMock { 3 | #[storage] 4 | pub struct Storage {} 5 | 6 | #[external(v0)] 7 | fn nope(self: @ContractState) -> bool { 8 | false 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /listings/applications/erc721/src/mocks/receiver.cairo: -------------------------------------------------------------------------------- 1 | const SUCCESS: felt252 = 'SUCCESS'; 2 | 3 | #[starknet::contract] 4 | pub mod ERC721ReceiverMock { 5 | use openzeppelin_introspection::src5::SRC5Component; 6 | use erc721::interfaces::{IERC721Receiver, IERC721_RECEIVER_ID}; 7 | use starknet::ContractAddress; 8 | 9 | component!(path: SRC5Component, storage: src5, event: SRC5Event); 10 | 11 | // SRC5 12 | #[abi(embed_v0)] 13 | impl SRC5Impl = SRC5Component::SRC5Impl; 14 | impl SRC5InternalImpl = SRC5Component::InternalImpl; 15 | 16 | #[storage] 17 | pub struct Storage { 18 | #[substorage(v0)] 19 | pub src5: SRC5Component::Storage, 20 | } 21 | 22 | #[event] 23 | #[derive(Drop, starknet::Event)] 24 | enum Event { 25 | #[flat] 26 | SRC5Event: SRC5Component::Event, 27 | } 28 | 29 | #[constructor] 30 | fn constructor(ref self: ContractState) { 31 | self.src5.register_interface(IERC721_RECEIVER_ID); 32 | } 33 | 34 | #[abi(embed_v0)] 35 | impl ExternalImpl of IERC721Receiver { 36 | fn on_erc721_received( 37 | self: @ContractState, 38 | operator: ContractAddress, 39 | from: ContractAddress, 40 | token_id: u256, 41 | data: Span, 42 | ) -> felt252 { 43 | if *data.at(0) == super::SUCCESS { 44 | IERC721_RECEIVER_ID 45 | } else { 46 | 0 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .snfoundry_cache/ 3 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "l1_l2_token_bridge" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/.gitignore: -------------------------------------------------------------------------------- 1 | cache 2 | out 3 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/README.md: -------------------------------------------------------------------------------- 1 | ## Foundry 2 | 3 | **Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** 4 | 5 | Foundry consists of: 6 | 7 | - **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). 8 | - **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. 9 | - **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. 10 | - **Chisel**: Fast, utilitarian, and verbose solidity REPL. 11 | 12 | ## Documentation 13 | 14 | https://book.getfoundry.sh/ 15 | 16 | ## Usage 17 | 18 | ### Build 19 | 20 | ```shell 21 | $ forge build 22 | ``` 23 | 24 | ### Test 25 | 26 | ```shell 27 | $ forge test 28 | ``` 29 | 30 | ### Format 31 | 32 | ```shell 33 | $ forge fmt 34 | ``` 35 | 36 | ### Gas Snapshots 37 | 38 | ```shell 39 | $ forge snapshot 40 | ``` 41 | 42 | ### Anvil 43 | 44 | ```shell 45 | $ anvil 46 | ``` 47 | 48 | ### Deploy 49 | 50 | ```shell 51 | $ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key 52 | ``` 53 | 54 | ### Cast 55 | 56 | ```shell 57 | $ cast 58 | ``` 59 | 60 | ### Help 61 | 62 | ```shell 63 | $ forge --help 64 | $ anvil --help 65 | $ cast --help 66 | ``` 67 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = "src" 3 | out = "out" 4 | libs = ["lib"] 5 | 6 | # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options 7 | remappings = ["starknet/=lib/starknet/"] 8 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/.gitattributes: -------------------------------------------------------------------------------- 1 | src/Vm.sol linguist-generated 2 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: Sync Release Branch 2 | 3 | on: 4 | release: 5 | types: 6 | - created 7 | 8 | jobs: 9 | sync-release-branch: 10 | runs-on: ubuntu-latest 11 | if: startsWith(github.event.release.tag_name, 'v1') 12 | steps: 13 | - name: Check out the repo 14 | uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 0 17 | ref: v1 18 | 19 | # The email is derived from the bots user id, 20 | # found here: https://api.github.com/users/github-actions%5Bbot%5D 21 | - name: Configure Git 22 | run: | 23 | git config user.name github-actions[bot] 24 | git config user.email 41898282+github-actions[bot]@users.noreply.github.com 25 | 26 | - name: Sync Release Branch 27 | run: | 28 | git fetch --tags 29 | git checkout v1 30 | git reset --hard ${GITHUB_REF} 31 | git push --force 32 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/.gitignore: -------------------------------------------------------------------------------- 1 | cache/ 2 | out/ 3 | .vscode 4 | .idea 5 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright Contributors to Forge Standard Library 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE O THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE.R 26 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | fs_permissions = [{ access = "read-write", path = "./"}] 3 | 4 | [rpc_endpoints] 5 | # The RPC URLs are modified versions of the default for testing initialization. 6 | mainnet = "https://eth-mainnet.alchemyapi.io/v2/WV407BEiBmjNJfKo9Uo_55u0z0ITyCOX" # Different API key. 7 | optimism_sepolia = "https://sepolia.optimism.io/" # Adds a trailing slash. 8 | arbitrum_one_sepolia = "https://sepolia-rollup.arbitrum.io/rpc/" # Adds a trailing slash. 9 | needs_undefined_env_var = "${UNDEFINED_RPC_URL_PLACEHOLDER}" 10 | 11 | [fmt] 12 | # These are all the `forge fmt` defaults. 13 | line_length = 120 14 | tab_width = 4 15 | bracket_spacing = false 16 | int_types = 'long' 17 | multiline_func_header = 'attributes_first' 18 | quote_style = 'double' 19 | number_underscore = 'preserve' 20 | single_line_statement_blocks = 'preserve' 21 | ignore = ["src/console.sol", "src/console2.sol"] -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "forge-std", 3 | "version": "1.9.3", 4 | "description": "Forge Standard Library is a collection of helpful contracts and libraries for use with Forge and Foundry.", 5 | "homepage": "https://book.getfoundry.sh/forge/forge-std", 6 | "bugs": "https://github.com/foundry-rs/forge-std/issues", 7 | "license": "(Apache-2.0 OR MIT)", 8 | "author": "Contributors to Forge Standard Library", 9 | "files": [ 10 | "src/**/*" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/foundry-rs/forge-std.git" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/src/Base.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | import {StdStorage} from "./StdStorage.sol"; 5 | import {Vm, VmSafe} from "./Vm.sol"; 6 | 7 | abstract contract CommonBase { 8 | // Cheat code address, 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D. 9 | address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); 10 | // console.sol and console2.sol work by executing a staticcall to this address. 11 | address internal constant CONSOLE = 0x000000000000000000636F6e736F6c652e6c6f67; 12 | // Used when deploying with create2, https://github.com/Arachnid/deterministic-deployment-proxy. 13 | address internal constant CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C; 14 | // Default address for tx.origin and msg.sender, 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38. 15 | address internal constant DEFAULT_SENDER = address(uint160(uint256(keccak256("foundry default caller")))); 16 | // Address of the test contract, deployed by the DEFAULT_SENDER. 17 | address internal constant DEFAULT_TEST_CONTRACT = 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f; 18 | // Deterministic deployment address of the Multicall3 contract. 19 | address internal constant MULTICALL3_ADDRESS = 0xcA11bde05977b3631167028862bE2a173976CA11; 20 | // The order of the secp256k1 curve. 21 | uint256 internal constant SECP256K1_ORDER = 22 | 115792089237316195423570985008687907852837564279074904382605163141518161494337; 23 | 24 | uint256 internal constant UINT256_MAX = 25 | 115792089237316195423570985008687907853269984665640564039457584007913129639935; 26 | 27 | Vm internal constant vm = Vm(VM_ADDRESS); 28 | StdStorage internal stdstore; 29 | } 30 | 31 | abstract contract TestBase is CommonBase {} 32 | 33 | abstract contract ScriptBase is CommonBase { 34 | VmSafe internal constant vmSafe = VmSafe(VM_ADDRESS); 35 | } 36 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/src/Script.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | // 💬 ABOUT 5 | // Forge Std's default Script. 6 | 7 | // 🧩 MODULES 8 | import {console} from "./console.sol"; 9 | import {console2} from "./console2.sol"; 10 | import {safeconsole} from "./safeconsole.sol"; 11 | import {StdChains} from "./StdChains.sol"; 12 | import {StdCheatsSafe} from "./StdCheats.sol"; 13 | import {stdJson} from "./StdJson.sol"; 14 | import {stdMath} from "./StdMath.sol"; 15 | import {StdStorage, stdStorageSafe} from "./StdStorage.sol"; 16 | import {StdStyle} from "./StdStyle.sol"; 17 | import {StdUtils} from "./StdUtils.sol"; 18 | import {VmSafe} from "./Vm.sol"; 19 | 20 | // 📦 BOILERPLATE 21 | import {ScriptBase} from "./Base.sol"; 22 | 23 | // ⭐️ SCRIPT 24 | abstract contract Script is ScriptBase, StdChains, StdCheatsSafe, StdUtils { 25 | // Note: IS_SCRIPT() must return true. 26 | bool public IS_SCRIPT = true; 27 | } 28 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/src/StdError.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Panics work for versions >=0.8.0, but we lowered the pragma to make this compatible with Test 3 | pragma solidity >=0.6.2 <0.9.0; 4 | 5 | library stdError { 6 | bytes public constant assertionError = abi.encodeWithSignature("Panic(uint256)", 0x01); 7 | bytes public constant arithmeticError = abi.encodeWithSignature("Panic(uint256)", 0x11); 8 | bytes public constant divisionError = abi.encodeWithSignature("Panic(uint256)", 0x12); 9 | bytes public constant enumConversionError = abi.encodeWithSignature("Panic(uint256)", 0x21); 10 | bytes public constant encodeStorageError = abi.encodeWithSignature("Panic(uint256)", 0x22); 11 | bytes public constant popError = abi.encodeWithSignature("Panic(uint256)", 0x31); 12 | bytes public constant indexOOBError = abi.encodeWithSignature("Panic(uint256)", 0x32); 13 | bytes public constant memOverflowError = abi.encodeWithSignature("Panic(uint256)", 0x41); 14 | bytes public constant zeroVarError = abi.encodeWithSignature("Panic(uint256)", 0x51); 15 | } 16 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/src/StdMath.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | library stdMath { 5 | int256 private constant INT256_MIN = -57896044618658097711785492504343953926634992332820282019728792003956564819968; 6 | 7 | function abs(int256 a) internal pure returns (uint256) { 8 | // Required or it will fail when `a = type(int256).min` 9 | if (a == INT256_MIN) { 10 | return 57896044618658097711785492504343953926634992332820282019728792003956564819968; 11 | } 12 | 13 | return uint256(a > 0 ? a : -a); 14 | } 15 | 16 | function delta(uint256 a, uint256 b) internal pure returns (uint256) { 17 | return a > b ? a - b : b - a; 18 | } 19 | 20 | function delta(int256 a, int256 b) internal pure returns (uint256) { 21 | // a and b are of the same sign 22 | // this works thanks to two's complement, the left-most bit is the sign bit 23 | if ((a ^ b) > -1) { 24 | return delta(abs(a), abs(b)); 25 | } 26 | 27 | // a and b are of opposite signs 28 | return abs(a) + abs(b); 29 | } 30 | 31 | function percentDelta(uint256 a, uint256 b) internal pure returns (uint256) { 32 | uint256 absDelta = delta(a, b); 33 | 34 | return absDelta * 1e18 / b; 35 | } 36 | 37 | function percentDelta(int256 a, int256 b) internal pure returns (uint256) { 38 | uint256 absDelta = delta(a, b); 39 | uint256 absB = abs(b); 40 | 41 | return absDelta * 1e18 / absB; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/src/Test.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | // 💬 ABOUT 7 | // Forge Std's default Test. 8 | 9 | // 🧩 MODULES 10 | import {console} from "./console.sol"; 11 | import {console2} from "./console2.sol"; 12 | import {safeconsole} from "./safeconsole.sol"; 13 | import {StdAssertions} from "./StdAssertions.sol"; 14 | import {StdChains} from "./StdChains.sol"; 15 | import {StdCheats} from "./StdCheats.sol"; 16 | import {stdError} from "./StdError.sol"; 17 | import {StdInvariant} from "./StdInvariant.sol"; 18 | import {stdJson} from "./StdJson.sol"; 19 | import {stdMath} from "./StdMath.sol"; 20 | import {StdStorage, stdStorage} from "./StdStorage.sol"; 21 | import {StdStyle} from "./StdStyle.sol"; 22 | import {stdToml} from "./StdToml.sol"; 23 | import {StdUtils} from "./StdUtils.sol"; 24 | import {Vm} from "./Vm.sol"; 25 | 26 | // 📦 BOILERPLATE 27 | import {TestBase} from "./Base.sol"; 28 | 29 | // ⭐️ TEST 30 | abstract contract Test is TestBase, StdAssertions, StdChains, StdCheats, StdInvariant, StdUtils { 31 | // Note: IS_TEST() must return true. 32 | bool public IS_TEST = true; 33 | } 34 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/src/console2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.22 <0.9.0; 3 | 4 | import {console as console2} from "./console.sol"; 5 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/src/interfaces/IERC165.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | interface IERC165 { 5 | /// @notice Query if a contract implements an interface 6 | /// @param interfaceID The interface identifier, as specified in ERC-165 7 | /// @dev Interface identification is specified in ERC-165. This function 8 | /// uses less than 30,000 gas. 9 | /// @return `true` if the contract implements `interfaceID` and 10 | /// `interfaceID` is not 0xffffffff, `false` otherwise 11 | function supportsInterface(bytes4 interfaceID) external view returns (bool); 12 | } 13 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/src/interfaces/IERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | /// @dev Interface of the ERC20 standard as defined in the EIP. 5 | /// @dev This includes the optional name, symbol, and decimals metadata. 6 | interface IERC20 { 7 | /// @dev Emitted when `value` tokens are moved from one account (`from`) to another (`to`). 8 | event Transfer(address indexed from, address indexed to, uint256 value); 9 | 10 | /// @dev Emitted when the allowance of a `spender` for an `owner` is set, where `value` 11 | /// is the new allowance. 12 | event Approval(address indexed owner, address indexed spender, uint256 value); 13 | 14 | /// @notice Returns the amount of tokens in existence. 15 | function totalSupply() external view returns (uint256); 16 | 17 | /// @notice Returns the amount of tokens owned by `account`. 18 | function balanceOf(address account) external view returns (uint256); 19 | 20 | /// @notice Moves `amount` tokens from the caller's account to `to`. 21 | function transfer(address to, uint256 amount) external returns (bool); 22 | 23 | /// @notice Returns the remaining number of tokens that `spender` is allowed 24 | /// to spend on behalf of `owner` 25 | function allowance(address owner, address spender) external view returns (uint256); 26 | 27 | /// @notice Sets `amount` as the allowance of `spender` over the caller's tokens. 28 | /// @dev Be aware of front-running risks: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 29 | function approve(address spender, uint256 amount) external returns (bool); 30 | 31 | /// @notice Moves `amount` tokens from `from` to `to` using the allowance mechanism. 32 | /// `amount` is then deducted from the caller's allowance. 33 | function transferFrom(address from, address to, uint256 amount) external returns (bool); 34 | 35 | /// @notice Returns the name of the token. 36 | function name() external view returns (string memory); 37 | 38 | /// @notice Returns the symbol of the token. 39 | function symbol() external view returns (string memory); 40 | 41 | /// @notice Returns the decimals places of the token. 42 | function decimals() external view returns (uint8); 43 | } 44 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/test/StdJson.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.7.0 <0.9.0; 3 | 4 | import {Test, stdJson} from "../src/Test.sol"; 5 | 6 | contract StdJsonTest is Test { 7 | using stdJson for string; 8 | 9 | string root; 10 | string path; 11 | 12 | function setUp() public { 13 | root = vm.projectRoot(); 14 | path = string.concat(root, "/test/fixtures/test.json"); 15 | } 16 | 17 | struct SimpleJson { 18 | uint256 a; 19 | string b; 20 | } 21 | 22 | struct NestedJson { 23 | uint256 a; 24 | string b; 25 | SimpleJson c; 26 | } 27 | 28 | function test_readJson() public view { 29 | string memory json = vm.readFile(path); 30 | assertEq(json.readUint(".a"), 123); 31 | } 32 | 33 | function test_writeJson() public { 34 | string memory json = "json"; 35 | json.serialize("a", uint256(123)); 36 | string memory semiFinal = json.serialize("b", string("test")); 37 | string memory finalJson = json.serialize("c", semiFinal); 38 | finalJson.write(path); 39 | 40 | string memory json_ = vm.readFile(path); 41 | bytes memory data = json_.parseRaw("$"); 42 | NestedJson memory decodedData = abi.decode(data, (NestedJson)); 43 | 44 | assertEq(decodedData.a, 123); 45 | assertEq(decodedData.b, "test"); 46 | assertEq(decodedData.c.a, 123); 47 | assertEq(decodedData.c.b, "test"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/test/StdToml.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.7.0 <0.9.0; 3 | 4 | import {Test, stdToml} from "../src/Test.sol"; 5 | 6 | contract StdTomlTest is Test { 7 | using stdToml for string; 8 | 9 | string root; 10 | string path; 11 | 12 | function setUp() public { 13 | root = vm.projectRoot(); 14 | path = string.concat(root, "/test/fixtures/test.toml"); 15 | } 16 | 17 | struct SimpleToml { 18 | uint256 a; 19 | string b; 20 | } 21 | 22 | struct NestedToml { 23 | uint256 a; 24 | string b; 25 | SimpleToml c; 26 | } 27 | 28 | function test_readToml() public view { 29 | string memory json = vm.readFile(path); 30 | assertEq(json.readUint(".a"), 123); 31 | } 32 | 33 | function test_writeToml() public { 34 | string memory json = "json"; 35 | json.serialize("a", uint256(123)); 36 | string memory semiFinal = json.serialize("b", string("test")); 37 | string memory finalJson = json.serialize("c", semiFinal); 38 | finalJson.write(path); 39 | 40 | string memory toml = vm.readFile(path); 41 | bytes memory data = toml.parseRaw("$"); 42 | NestedToml memory decodedData = abi.decode(data, (NestedToml)); 43 | 44 | assertEq(decodedData.a, 123); 45 | assertEq(decodedData.b, "test"); 46 | assertEq(decodedData.c.a, 123); 47 | assertEq(decodedData.c.b, "test"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/test/Vm.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.9.0; 3 | 4 | import {Test} from "../src/Test.sol"; 5 | import {Vm, VmSafe} from "../src/Vm.sol"; 6 | 7 | // These tests ensure that functions are never accidentally removed from a Vm interface, or 8 | // inadvertently moved between Vm and VmSafe. These tests must be updated each time a function is 9 | // added to or removed from Vm or VmSafe. 10 | contract VmTest is Test { 11 | function test_VmInterfaceId() public pure { 12 | assertEq(type(Vm).interfaceId, bytes4(0x329891d9), "Vm"); 13 | } 14 | 15 | function test_VmSafeInterfaceId() public pure { 16 | assertEq(type(VmSafe).interfaceId, bytes4(0xb09496a4), "VmSafe"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/test/compilation/CompilationScript.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../../src/Script.sol"; 7 | 8 | // The purpose of this contract is to benchmark compilation time to avoid accidentally introducing 9 | // a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 10 | contract CompilationScript is Script {} 11 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/test/compilation/CompilationScriptBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../../src/Script.sol"; 7 | 8 | // The purpose of this contract is to benchmark compilation time to avoid accidentally introducing 9 | // a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 10 | contract CompilationScriptBase is ScriptBase {} 11 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/test/compilation/CompilationTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../../src/Test.sol"; 7 | 8 | // The purpose of this contract is to benchmark compilation time to avoid accidentally introducing 9 | // a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 10 | contract CompilationTest is Test {} 11 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/test/compilation/CompilationTestBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../../src/Test.sol"; 7 | 8 | // The purpose of this contract is to benchmark compilation time to avoid accidentally introducing 9 | // a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 10 | contract CompilationTestBase is TestBase {} 11 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/test/fixtures/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "a": 123, 3 | "b": "test", 4 | "c": { 5 | "a": 123, 6 | "b": "test" 7 | } 8 | } -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/lib/forge-std/test/fixtures/test.toml: -------------------------------------------------------------------------------- 1 | a = 123 2 | b = "test" 3 | 4 | [c] 5 | a = 123 6 | b = "test" 7 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/src/IMintableToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT. 2 | pragma solidity ^0.8.0; 3 | 4 | import "./IMintableTokenEvents.sol"; 5 | 6 | interface IMintableToken is IMintableTokenEvents { 7 | function mint(address account, uint256 amount) external; 8 | 9 | function burn(address account, uint256 amount) external; 10 | } 11 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/src/IMintableTokenEvents.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT. 2 | pragma solidity ^0.8.0; 3 | 4 | interface IMintableTokenEvents { 5 | event Minted(address indexed account, uint256 amount); 6 | event Burned(address indexed account, uint256 amount); 7 | } 8 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/src/MintableTokenMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT. 2 | pragma solidity ^0.8.0; 3 | 4 | import "./IMintableToken.sol"; 5 | 6 | contract MintableTokenMock is IMintableToken { 7 | address public bridge; 8 | 9 | /** 10 | * @dev The address is invalid (e.g. `address(0)`). 11 | */ 12 | error InvalidAddress(); 13 | 14 | /** 15 | * @dev The sender is not the bridge. 16 | */ 17 | error Unauthorized(); 18 | 19 | /** 20 | @dev Constructor. 21 | 22 | @param _bridge The address of the L1 bridge. 23 | */ 24 | constructor(address _bridge) { 25 | if (_bridge == address(0)) { 26 | revert InvalidAddress(); 27 | } 28 | bridge = _bridge; 29 | } 30 | 31 | /** 32 | * @dev Throws if the sender is not the bridge. 33 | */ 34 | modifier onlyBridge() { 35 | if (bridge != msg.sender) { 36 | revert Unauthorized(); 37 | } 38 | _; 39 | } 40 | 41 | function mint(address account, uint256 amount) external onlyBridge { 42 | emit Minted(account, amount); 43 | } 44 | 45 | function burn(address account, uint256 amount) external onlyBridge { 46 | emit Burned(account, amount); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/solidity/src/StarknetMessagingLocal.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0. 2 | // This contract is taken from the following original Solidity code: 3 | // https://github.com/glihm/starknet-messaging-dev/blob/75ce67d6e634436d0a4094ee557c8d8fce715877/solidity/src/StarknetMessagingLocal.sol 4 | 5 | pragma solidity ^0.8.0; 6 | 7 | import "starknet/StarknetMessaging.sol"; 8 | 9 | /** 10 | @notice Interface related to local messaging for Starknet. 11 | */ 12 | interface IStarknetMessagingLocal { 13 | function addMessageHashesFromL2( 14 | uint256[] calldata msgHashes 15 | ) external payable; 16 | } 17 | 18 | /** 19 | @title A superset of StarknetMessaging to support 20 | local development by adding a way to directly register 21 | a message hash ready to be consumed, without waiting the block 22 | to be verified. 23 | 24 | @dev The idea is that, to not wait on the block to be proven, 25 | this messaging contract can receive directly a hash of a message 26 | to be considered as `received`. This message can then be consumed normally. 27 | 28 | DISCLAIMER: 29 | The purpose of this contract is for local development only. 30 | */ 31 | contract StarknetMessagingLocal is StarknetMessaging, IStarknetMessagingLocal { 32 | /** 33 | @notice Hashes were added. 34 | */ 35 | event MessageHashesAddedFromL2(uint256[] hashes); 36 | 37 | /** 38 | @notice Adds the hashes of messages from L2. 39 | 40 | @param msgHashes Hashes to register as consumable. 41 | */ 42 | function addMessageHashesFromL2( 43 | uint256[] calldata msgHashes 44 | ) external payable { 45 | for (uint256 i = 0; i < msgHashes.length; i++) { 46 | bytes32 hash = bytes32(msgHashes[i]); 47 | l2ToL1Messages()[hash] += 1; 48 | } 49 | 50 | emit MessageHashesAddedFromL2(msgHashes); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod contract; 2 | mod mintable_token_mock; 3 | 4 | #[cfg(test)] 5 | mod tests; 6 | -------------------------------------------------------------------------------- /listings/applications/l1_l2_token_bridge/src/mintable_token_mock.cairo: -------------------------------------------------------------------------------- 1 | #[starknet::contract] 2 | pub mod MintableTokenMock { 3 | use core::num::traits::Zero; 4 | use starknet::event::EventEmitter; 5 | use starknet::{ContractAddress, get_caller_address}; 6 | use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; 7 | 8 | use l1_l2_token_bridge::contract::IMintableToken; 9 | 10 | #[storage] 11 | struct Storage { 12 | // The address of the L2 bridge contract. Only the bridge can 13 | // invoke burn and mint methods 14 | bridge: ContractAddress, 15 | } 16 | 17 | #[event] 18 | #[derive(Drop, starknet::Event)] 19 | pub enum Event { 20 | Minted: Minted, 21 | Burned: Burned, 22 | } 23 | 24 | #[derive(Drop, starknet::Event)] 25 | pub struct Minted { 26 | pub account: ContractAddress, 27 | pub amount: u256, 28 | } 29 | 30 | #[derive(Drop, starknet::Event)] 31 | pub struct Burned { 32 | pub account: ContractAddress, 33 | pub amount: u256, 34 | } 35 | 36 | pub mod Errors { 37 | pub const INVALID_ADDRESS: felt252 = 'Invalid address'; 38 | pub const UNAUTHORIZED: felt252 = 'Unauthorized'; 39 | } 40 | 41 | #[constructor] 42 | fn constructor(ref self: ContractState, bridge: ContractAddress) { 43 | assert(bridge.is_non_zero(), Errors::INVALID_ADDRESS); 44 | self.bridge.write(bridge); 45 | } 46 | 47 | #[abi(embed_v0)] 48 | impl MintableTokenMock of IMintableToken { 49 | fn mint(ref self: ContractState, account: ContractAddress, amount: u256) { 50 | self._assert_only_bridge(); 51 | self.emit(Minted { account, amount }); 52 | } 53 | 54 | fn burn(ref self: ContractState, account: ContractAddress, amount: u256) { 55 | self._assert_only_bridge(); 56 | self.emit(Burned { account, amount }); 57 | } 58 | } 59 | 60 | #[generate_trait] 61 | impl Internal of InternalTrait { 62 | fn _assert_only_bridge(self: @ContractState) { 63 | assert(get_caller_address() == self.bridge.read(), Errors::UNAUTHORIZED); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /listings/applications/merkle_tree/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/applications/merkle_tree/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "merkle_tree" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/applications/merkle_tree/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod contract; 2 | 3 | #[cfg(test)] 4 | mod tests; 5 | -------------------------------------------------------------------------------- /listings/applications/nft_dutch_auction/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/applications/nft_dutch_auction/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nft_dutch_auction" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | erc20 = { path = "../erc20" } 8 | starknet.workspace = true 9 | 10 | [dev-dependencies] 11 | assert_macros.workspace = true 12 | snforge_std.workspace = true 13 | 14 | [scripts] 15 | test.workspace = true 16 | 17 | [[target.starknet-contract]] 18 | build-external-contracts = ["erc20::token::erc20"] 19 | -------------------------------------------------------------------------------- /listings/applications/nft_dutch_auction/src/lib.cairo: -------------------------------------------------------------------------------- 1 | pub mod nft_dutch_auction; 2 | pub mod erc721; 3 | -------------------------------------------------------------------------------- /listings/applications/simple_storage_starknetjs/.env.example: -------------------------------------------------------------------------------- 1 | PRIVATE_KEY = "PASTE_PRIVATE_KEY_HERE" -------------------------------------------------------------------------------- /listings/applications/simple_storage_starknetjs/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /listings/applications/simple_storage_starknetjs/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "simple_storage" 6 | version = "0.1.0" 7 | -------------------------------------------------------------------------------- /listings/applications/simple_storage_starknetjs/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simple_storage" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | cairo_test.workspace = true 11 | 12 | [scripts] 13 | test.workspace = true 14 | 15 | [[target.starknet-contract]] -------------------------------------------------------------------------------- /listings/applications/simple_storage_starknetjs/index.js: -------------------------------------------------------------------------------- 1 | // [!region imports] 2 | import { Account, RpcProvider, json, Contract } from "starknet"; 3 | import fs from "fs"; 4 | import * as dotenv from "dotenv"; 5 | dotenv.config(); 6 | // [!endregion imports] 7 | 8 | // [!region provider] 9 | const provider = new RpcProvider({ 10 | nodeUrl: "https://free-rpc.nethermind.io/sepolia-juno", 11 | }); 12 | // [!endregion provider] 13 | 14 | const accountAddress = 15 | "0x067981c7F9f55BCbdD4e0d0a9C5BBCeA77dAcB42cccbf13554A847d6353F728e"; 16 | // [!region account] 17 | const privateKey = process.env.PRIVATE_KEY; 18 | // "1" is added to show that our account is deployed using Cairo 1.0. 19 | const account = new Account(provider, accountAddress, privateKey, "1"); 20 | // [!endregion account] 21 | 22 | const contractAddress = 23 | "0x01bb7d67375782ab08178b444dbda2b0c1c9ff4469c421124f54e1d8257f2e97"; 24 | // [!region contract] 25 | const compiledContractAbi = json.parse( 26 | fs.readFileSync("./abi.json").toString("ascii") 27 | ); 28 | const storageContract = new Contract( 29 | compiledContractAbi.abi, 30 | contractAddress, 31 | provider 32 | ); 33 | // [!endregion contract] 34 | 35 | // [!region get] 36 | let getData = await storageContract.get(); 37 | console.log("Stored_data:", getData.toString()); 38 | // [!endregion get] 39 | 40 | // [!region set] 41 | storageContract.connect(account); 42 | const myCall = storageContract.populate("set", [59]); 43 | const res = await storageContract.set(myCall.calldata); 44 | await provider.waitForTransaction(res.transaction_hash); 45 | 46 | // Get the stored data after setting it 47 | getData = await storageContract.get(); 48 | console.log("Stored_data after set():", getData.toString()); 49 | // [!endregion set] 50 | -------------------------------------------------------------------------------- /listings/applications/simple_storage_starknetjs/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod storage; 2 | -------------------------------------------------------------------------------- /listings/applications/simple_storage_starknetjs/src/storage.cairo: -------------------------------------------------------------------------------- 1 | // [!region contract] 2 | #[starknet::interface] 3 | trait ISimpleStorage { 4 | fn set(ref self: T, x: u128); 5 | fn get(self: @T) -> u128; 6 | } 7 | 8 | #[starknet::contract] 9 | mod SimpleStorage { 10 | use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; 11 | 12 | #[storage] 13 | struct Storage { 14 | stored_data: u128, 15 | } 16 | 17 | #[abi(embed_v0)] 18 | impl SimpleStorage of super::ISimpleStorage { 19 | fn set(ref self: ContractState, x: u128) { 20 | self.stored_data.write(x); 21 | } 22 | 23 | fn get(self: @ContractState) -> u128 { 24 | self.stored_data.read() 25 | } 26 | } 27 | } 28 | // [!endregion contract] 29 | 30 | 31 | -------------------------------------------------------------------------------- /listings/applications/simple_vault/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/applications/simple_vault/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simple_vault" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | erc20 = { path = "../erc20" } 9 | 10 | [dev-dependencies] 11 | assert_macros.workspace = true 12 | snforge_std.workspace = true 13 | 14 | [scripts] 15 | test.workspace = true 16 | 17 | [[target.starknet-contract]] 18 | build-external-contracts = ["erc20::token::erc20"] 19 | -------------------------------------------------------------------------------- /listings/applications/simple_vault/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod simple_vault; 2 | -------------------------------------------------------------------------------- /listings/applications/staking/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/applications/staking/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "staking" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | openzeppelin_token.workspace = true 9 | 10 | [dev-dependencies] 11 | cairo_test.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/applications/staking/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod contract; 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | mod tokens; 6 | mod staking_tests; 7 | } 8 | -------------------------------------------------------------------------------- /listings/applications/staking/src/tests/tokens.cairo: -------------------------------------------------------------------------------- 1 | #[starknet::contract] 2 | pub mod RewardToken { 3 | use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; 4 | use starknet::ContractAddress; 5 | 6 | component!(path: ERC20Component, storage: erc20, event: ERC20Event); 7 | 8 | #[abi(embed_v0)] 9 | impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; 10 | impl ERC20InternalImpl = ERC20Component::InternalImpl; 11 | 12 | #[storage] 13 | struct Storage { 14 | #[substorage(v0)] 15 | pub erc20: ERC20Component::Storage, 16 | } 17 | 18 | #[event] 19 | #[derive(Drop, starknet::Event)] 20 | enum Event { 21 | #[flat] 22 | ERC20Event: ERC20Component::Event, 23 | } 24 | 25 | #[constructor] 26 | fn constructor( 27 | ref self: ContractState, 28 | name: ByteArray, 29 | symbol: ByteArray, 30 | initial_supply: u256, 31 | recipient: ContractAddress, 32 | ) { 33 | self.erc20.initializer(name, symbol); 34 | self.erc20.mint(recipient, initial_supply); 35 | } 36 | } 37 | 38 | #[starknet::contract] 39 | pub mod StakingToken { 40 | use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; 41 | use starknet::ContractAddress; 42 | 43 | component!(path: ERC20Component, storage: erc20, event: ERC20Event); 44 | 45 | #[abi(embed_v0)] 46 | impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; 47 | impl ERC20InternalImpl = ERC20Component::InternalImpl; 48 | 49 | #[storage] 50 | struct Storage { 51 | #[substorage(v0)] 52 | pub erc20: ERC20Component::Storage, 53 | } 54 | 55 | #[event] 56 | #[derive(Drop, starknet::Event)] 57 | enum Event { 58 | #[flat] 59 | ERC20Event: ERC20Component::Event, 60 | } 61 | 62 | #[constructor] 63 | fn constructor( 64 | ref self: ContractState, 65 | name: ByteArray, 66 | symbol: ByteArray, 67 | initial_supply: u256, 68 | recipient: ContractAddress, 69 | ) { 70 | self.erc20.initializer(name, symbol); 71 | self.erc20.mint(recipient, initial_supply); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /listings/applications/timelock/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/applications/timelock/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "timelock" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | openzeppelin_utils.workspace = true 9 | openzeppelin_token.workspace = true 10 | openzeppelin_introspection.workspace = true 11 | components.workspace = true 12 | 13 | [dev-dependencies] 14 | assert_macros.workspace = true 15 | snforge_std.workspace = true 16 | 17 | [scripts] 18 | test.workspace = true 19 | 20 | [[target.starknet-contract]] 21 | -------------------------------------------------------------------------------- /listings/applications/timelock/src/erc721.cairo: -------------------------------------------------------------------------------- 1 | #[starknet::contract] 2 | pub mod ERC721 { 3 | use starknet::ContractAddress; 4 | use openzeppelin_introspection::src5::SRC5Component; 5 | use openzeppelin_token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; 6 | 7 | component!(path: ERC721Component, storage: erc721, event: ERC721Event); 8 | component!(path: SRC5Component, storage: src5, event: SRC5Event); 9 | 10 | // ERC20Mixin 11 | #[abi(embed_v0)] 12 | impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl; 13 | impl ERC721InternalImpl = ERC721Component::InternalImpl; 14 | 15 | #[storage] 16 | struct Storage { 17 | #[substorage(v0)] 18 | src5: SRC5Component::Storage, 19 | #[substorage(v0)] 20 | erc721: ERC721Component::Storage, 21 | } 22 | 23 | #[event] 24 | #[derive(Drop, starknet::Event)] 25 | enum Event { 26 | #[flat] 27 | SRC5Event: SRC5Component::Event, 28 | #[flat] 29 | ERC721Event: ERC721Component::Event, 30 | } 31 | 32 | #[constructor] 33 | fn constructor( 34 | ref self: ContractState, 35 | name: ByteArray, 36 | symbol: ByteArray, 37 | base_uri: ByteArray, 38 | recipient: ContractAddress, 39 | token_id: u256, 40 | ) { 41 | self.erc721.initializer(name, symbol, base_uri); 42 | self.erc721.mint(recipient, token_id); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /listings/applications/timelock/src/lib.cairo: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | #[feature("safe_dispatcher")] 4 | mod timelock; 5 | mod utils; 6 | } 7 | mod erc721; 8 | mod timelock; 9 | -------------------------------------------------------------------------------- /listings/applications/upgradeable_contract/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/applications/upgradeable_contract/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "upgradeable_contract" 6 | version.workspace = true 7 | -------------------------------------------------------------------------------- /listings/applications/upgradeable_contract/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "upgradeable_contract" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/applications/upgradeable_contract/src/lib.cairo: -------------------------------------------------------------------------------- 1 | pub mod upgradeable_contract_v0; 2 | pub mod upgradeable_contract_v1; 3 | 4 | #[cfg(test)] 5 | mod tests; 6 | -------------------------------------------------------------------------------- /listings/applications/upgradeable_contract/src/upgradeable_contract_v0.cairo: -------------------------------------------------------------------------------- 1 | // [!region contract] 2 | use starknet::class_hash::ClassHash; 3 | 4 | #[starknet::interface] 5 | pub trait IUpgradeableContract { 6 | fn upgrade(ref self: TContractState, impl_hash: ClassHash); 7 | fn version(self: @TContractState) -> u8; 8 | } 9 | 10 | #[starknet::contract] 11 | pub mod UpgradeableContract_V0 { 12 | use starknet::class_hash::ClassHash; 13 | use core::num::traits::Zero; 14 | 15 | #[storage] 16 | struct Storage {} 17 | 18 | #[event] 19 | #[derive(Copy, Drop, Debug, PartialEq, starknet::Event)] 20 | pub enum Event { 21 | Upgraded: Upgraded, 22 | } 23 | 24 | #[derive(Copy, Drop, Debug, PartialEq, starknet::Event)] 25 | pub struct Upgraded { 26 | pub implementation: ClassHash, 27 | } 28 | 29 | #[abi(embed_v0)] 30 | impl UpgradeableContract of super::IUpgradeableContract { 31 | // [!region upgrade] 32 | fn upgrade(ref self: ContractState, impl_hash: ClassHash) { 33 | assert(impl_hash.is_non_zero(), 'Class hash cannot be zero'); 34 | starknet::syscalls::replace_class_syscall(impl_hash).unwrap(); 35 | self.emit(Event::Upgraded(Upgraded { implementation: impl_hash })) 36 | } 37 | // [!endregion upgrade] 38 | 39 | fn version(self: @ContractState) -> u8 { 40 | 0 41 | } 42 | } 43 | } 44 | // [!endregion contract] 45 | 46 | 47 | -------------------------------------------------------------------------------- /listings/applications/upgradeable_contract/src/upgradeable_contract_v1.cairo: -------------------------------------------------------------------------------- 1 | use starknet::class_hash::ClassHash; 2 | 3 | #[starknet::interface] 4 | pub trait IUpgradeableContract { 5 | fn upgrade(ref self: TContractState, impl_hash: ClassHash); 6 | fn version(self: @TContractState) -> u8; 7 | } 8 | 9 | #[starknet::contract] 10 | pub mod UpgradeableContract_V1 { 11 | use starknet::class_hash::ClassHash; 12 | use core::num::traits::Zero; 13 | 14 | #[storage] 15 | struct Storage {} 16 | 17 | #[event] 18 | #[derive(Drop, starknet::Event)] 19 | enum Event { 20 | Upgraded: Upgraded, 21 | } 22 | 23 | #[derive(Drop, starknet::Event)] 24 | struct Upgraded { 25 | implementation: ClassHash, 26 | } 27 | 28 | #[abi(embed_v0)] 29 | impl UpgradeableContract of super::IUpgradeableContract { 30 | fn upgrade(ref self: ContractState, impl_hash: ClassHash) { 31 | assert(impl_hash.is_non_zero(), 'Class hash cannot be zero'); 32 | starknet::syscalls::replace_class_syscall(impl_hash).unwrap(); 33 | self.emit(Event::Upgraded(Upgraded { implementation: impl_hash })) 34 | } 35 | 36 | fn version(self: @ContractState) -> u8 { 37 | 1 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /listings/cairo_cheatsheet/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "cairo_cheatsheet" 6 | version.workspace = true 7 | -------------------------------------------------------------------------------- /listings/cairo_cheatsheet/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cairo_cheatsheet" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | cairo_test.workspace = true 11 | 12 | [scripts] 13 | test.workspace = true 14 | 15 | [[target.starknet-contract]] 16 | -------------------------------------------------------------------------------- /listings/cairo_cheatsheet/src/array_example.cairo: -------------------------------------------------------------------------------- 1 | fn array() -> bool { 2 | let mut arr = array![]; 3 | arr.append(10); 4 | arr.append(20); 5 | arr.append(30); 6 | 7 | assert(arr.len() == 3, 'array length should be 3'); 8 | 9 | let first_value = arr.pop_front().unwrap(); 10 | assert(first_value == 10, 'first value should match'); 11 | 12 | let second_value = *arr.at(0); 13 | assert(second_value == 20, 'second value should match'); 14 | 15 | // Returns true if an array is empty, and false if it isn't. 16 | arr.is_empty() 17 | } 18 | -------------------------------------------------------------------------------- /listings/cairo_cheatsheet/src/dict_example.cairo: -------------------------------------------------------------------------------- 1 | // [!region sheet] 2 | use core::dict::Felt252Dict; 3 | 4 | fn dict() { 5 | let mut Auctions: Felt252Dict = Default::default(); 6 | 7 | Auctions.insert('Bola', 30); 8 | Auctions.insert('Maria', 40); 9 | 10 | let bola_balance = Auctions.get('Bola'); 11 | assert!(bola_balance == 30, "Bola balance should be 30"); 12 | 13 | let maria_balance = Auctions.get('Maria'); 14 | assert!(maria_balance == 40, "Maria balance should be 40"); 15 | } 16 | // [!endregion sheet] 17 | 18 | 19 | -------------------------------------------------------------------------------- /listings/cairo_cheatsheet/src/felt_example.cairo: -------------------------------------------------------------------------------- 1 | fn felt() { 2 | // [!region sheet] 3 | let felt: felt252 = 100; 4 | let felt_as_str = 'Hello Starknet!'; 5 | 6 | let _felt = felt + felt_as_str; 7 | // [!endregion sheet] 8 | } 9 | -------------------------------------------------------------------------------- /listings/cairo_cheatsheet/src/if_let_example.cairo: -------------------------------------------------------------------------------- 1 | // [!region sheet] 2 | #[derive(Drop)] 3 | enum Foo { 4 | Bar, 5 | Baz, 6 | Qux: usize, 7 | } 8 | 9 | fn if_let() { 10 | let number = Option::Some(0_usize); 11 | let letter: Option = Option::None; 12 | 13 | // "if `let` destructures `number` into `Some(i)`: 14 | // evaluate the block (`{}`). 15 | if let Option::Some(i) = number { 16 | format!("Matched {}", i); 17 | } 18 | 19 | // If you need to specify a failure, use an else: 20 | if let Option::Some(i) = letter { 21 | format!("Matched {}", i); 22 | } else { 23 | // Destructure failed. Change to the failure case. 24 | format!("Didn't match a number."); 25 | } 26 | 27 | // Using `if let` with enum 28 | let a = Foo::Bar; 29 | let b = Foo::Baz; 30 | let c = Foo::Qux(100); 31 | 32 | // Variable a matches Foo::Bar 33 | if let Foo::Bar = a { 34 | format!("a is foobar"); 35 | } 36 | 37 | // Variable b does not match Foo::Bar 38 | // So this will print nothing 39 | if let Foo::Bar = b { 40 | format!("b is foobar"); 41 | } 42 | 43 | // Variable c matches Foo::Qux which has a value 44 | // Similar to Some() in the previous example 45 | if let Foo::Qux(value) = c { 46 | format!("c is {}", value); 47 | } 48 | } 49 | // [!endregion sheet] 50 | 51 | 52 | -------------------------------------------------------------------------------- /listings/cairo_cheatsheet/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod array_example; 2 | mod mapping_example; 3 | mod felt_example; 4 | mod loop_example; 5 | mod while_example; 6 | mod enum_example; 7 | mod match_example; 8 | mod struct_example; 9 | mod type_casting_example; 10 | mod while_let_example; 11 | mod if_let_example; 12 | mod dict_example; 13 | -------------------------------------------------------------------------------- /listings/cairo_cheatsheet/src/loop_example.cairo: -------------------------------------------------------------------------------- 1 | fn do_loop() { 2 | // [!region sheet] 3 | let mut arr = array![]; 4 | 5 | // Same as ~ while (i < 10) arr.append(i++); 6 | let mut i: u32 = 0; 7 | let limit = 10; 8 | loop { 9 | if i == limit { 10 | break; 11 | }; 12 | 13 | arr.append(i); 14 | 15 | i += 1; 16 | }; 17 | // [!endregion sheet] 18 | } 19 | -------------------------------------------------------------------------------- /listings/cairo_cheatsheet/src/mapping_example.cairo: -------------------------------------------------------------------------------- 1 | use starknet::ContractAddress; 2 | 3 | #[starknet::interface] 4 | trait IMappingExample { 5 | fn register_user(ref self: TContractState, student_add: ContractAddress, studentName: felt252); 6 | fn record_student_score( 7 | ref self: TContractState, student_add: ContractAddress, subject: felt252, score: u16, 8 | ); 9 | fn view_student_name(self: @TContractState, student_add: ContractAddress) -> felt252; 10 | fn view_student_score( 11 | self: @TContractState, student_add: ContractAddress, subject: felt252, 12 | ) -> u16; 13 | } 14 | 15 | #[starknet::contract] 16 | mod MappingContract { 17 | use starknet::ContractAddress; 18 | use starknet::storage::{Map, StorageMapReadAccess, StorageMapWriteAccess}; 19 | 20 | #[storage] 21 | struct Storage { 22 | students_name: Map::, 23 | students_result_record: Map::<(ContractAddress, felt252), u16>, 24 | } 25 | 26 | #[abi(embed_v0)] 27 | impl External of super::IMappingExample { 28 | fn register_user( 29 | ref self: ContractState, student_add: ContractAddress, studentName: felt252, 30 | ) { 31 | self.students_name.write(student_add, studentName); 32 | } 33 | 34 | fn record_student_score( 35 | ref self: ContractState, student_add: ContractAddress, subject: felt252, score: u16, 36 | ) { 37 | self.students_result_record.write((student_add, subject), score); 38 | } 39 | 40 | fn view_student_name(self: @ContractState, student_add: ContractAddress) -> felt252 { 41 | self.students_name.read(student_add) 42 | } 43 | 44 | fn view_student_score( 45 | self: @ContractState, student_add: ContractAddress, subject: felt252, 46 | ) -> u16 { 47 | // for a 2D mapping its important to take note of the amount of brackets being used. 48 | self.students_result_record.read((student_add, subject)) 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /listings/cairo_cheatsheet/src/match_example.cairo: -------------------------------------------------------------------------------- 1 | #[derive(Drop, Serde)] 2 | enum Colour { 3 | Red, 4 | Blue, 5 | Green, 6 | Orange, 7 | Black, 8 | } 9 | 10 | #[derive(Drop, Serde)] 11 | enum Coin { 12 | Penny, 13 | Nickel, 14 | Dime, 15 | Quarter, 16 | } 17 | 18 | fn value_in_cents(coin: Coin) -> felt252 { 19 | match coin { 20 | Coin::Penny => 1, 21 | Coin::Nickel => 5, 22 | Coin::Dime => 10, 23 | Coin::Quarter => 25, 24 | } 25 | } 26 | 27 | fn specified_colour(colour: Colour) -> felt252 { 28 | let mut response: felt252 = ''; 29 | 30 | match colour { 31 | Colour::Red => { response = 'You passed in Red'; }, 32 | Colour::Blue => { response = 'You passed in Blue'; }, 33 | Colour::Green => { response = 'You passed in Green'; }, 34 | Colour::Orange => { response = 'You passed in Orange'; }, 35 | Colour::Black => { response = 'You passed in Black'; }, 36 | }; 37 | 38 | response 39 | } 40 | 41 | fn quiz(num: felt252) -> felt252 { 42 | let mut response: felt252 = ''; 43 | 44 | match num { 45 | 0 => { response = 'You failed' }, 46 | _ => { response = 'You Passed' }, 47 | }; 48 | 49 | response 50 | } 51 | -------------------------------------------------------------------------------- /listings/cairo_cheatsheet/src/struct_example.cairo: -------------------------------------------------------------------------------- 1 | // With Store, you can store Data's structs in the storage part of contracts. 2 | #[derive(Drop, starknet::Store)] 3 | struct Data { 4 | address: starknet::ContractAddress, 5 | age: u8, 6 | } 7 | -------------------------------------------------------------------------------- /listings/cairo_cheatsheet/src/tuple_example.cairo: -------------------------------------------------------------------------------- 1 | fn tuple() { 2 | // [!region sheet] 3 | let address = "0x000"; 4 | let age = 20; 5 | let active = true; 6 | 7 | // Create tuple 8 | let user_tuple = (address, age, active); 9 | 10 | // Access tuple 11 | let (address, age, active) = stored_tuple; 12 | // [!endregion sheet] 13 | } 14 | -------------------------------------------------------------------------------- /listings/cairo_cheatsheet/src/type_casting_example.cairo: -------------------------------------------------------------------------------- 1 | fn type_casting() { 2 | // [!region sheet] 3 | let a_number: u32 = 15; 4 | let my_felt252 = 15; 5 | 6 | // Since a u32 might not fit in a u8 and a u16, we need to use try_into, 7 | // then unwrap the Option type thats returned. 8 | let _new_u8: u8 = a_number.try_into().unwrap(); 9 | let new_u16: u16 = a_number.try_into().unwrap(); 10 | 11 | // since new_u32 is the of the same type (u32) as rand_number, we can directly assign them, 12 | // or use the .into() method. 13 | let _new_u32: u32 = a_number; 14 | 15 | // When typecasting from a smaller size to an equal or larger size we use the .into() method. 16 | // Note: u64 and u128 are larger than u32, so a u32 type will always fit into them. 17 | let _new_u64: u64 = a_number.into(); 18 | let _new_u128: u128 = a_number.into(); 19 | 20 | // Since a felt252 is smaller than a u256, we can use the into() method 21 | let _new_u256: u256 = my_felt252.into(); 22 | let _new_felt252: felt252 = new_u16.into(); 23 | 24 | // Note: usize is smaller than felt252, so we use try_into 25 | let _new_usize: usize = my_felt252.try_into().unwrap(); 26 | // [!endregion sheet] 27 | } 28 | -------------------------------------------------------------------------------- /listings/cairo_cheatsheet/src/while_example.cairo: -------------------------------------------------------------------------------- 1 | fn do_loop() { 2 | // [!region sheet] 3 | let mut arr = array![]; 4 | 5 | let mut i: u32 = 0; 6 | while (i < 10) { 7 | arr.append(i); 8 | i += 1; 9 | }; 10 | // [!endregion sheet] 11 | } 12 | -------------------------------------------------------------------------------- /listings/cairo_cheatsheet/src/while_let_example.cairo: -------------------------------------------------------------------------------- 1 | fn while_let() { 2 | // [!region sheet] 3 | let mut option = Option::Some(0_usize); 4 | 5 | // "while `let` destructures `option` into `Some(i)`, 6 | // evaluate the block (`{}`), else `break` 7 | while let Option::Some(i) = option { 8 | if i > 0 { 9 | option = Option::None; 10 | } else { 11 | option = Option::Some(i + 1); 12 | } 13 | } 14 | // [!endregion sheet] 15 | } 16 | -------------------------------------------------------------------------------- /listings/getting-started/calling_other_contracts/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/getting-started/calling_other_contracts/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "calling_other_contracts" 6 | version.workspace = true 7 | -------------------------------------------------------------------------------- /listings/getting-started/calling_other_contracts/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "calling_other_contracts" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/getting-started/calling_other_contracts/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod caller; 2 | -------------------------------------------------------------------------------- /listings/getting-started/constructor/.gitignore: -------------------------------------------------------------------------------- 1 | target -------------------------------------------------------------------------------- /listings/getting-started/constructor/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "constructor" 6 | version.workspace = true 7 | -------------------------------------------------------------------------------- /listings/getting-started/constructor/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "constructor" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/getting-started/constructor/src/constructor.cairo: -------------------------------------------------------------------------------- 1 | // [!region contract] 2 | #[starknet::contract] 3 | mod ConstructorContract { 4 | // This trait is necessary to be able to write to a specific storage variable 5 | use starknet::storage::StoragePointerWriteAccess; 6 | 7 | #[storage] 8 | struct Storage { 9 | a: u128, 10 | b: u8, 11 | c: u256, 12 | } 13 | 14 | // The constructor is decorated with a `#[constructor]` attribute. 15 | // It is not inside an `impl` block. 16 | #[constructor] 17 | fn constructor(ref self: ContractState, a: u128, b: u8, c: u256) { 18 | self.a.write(a); 19 | self.b.write(b); 20 | self.c.write(c); 21 | } 22 | } 23 | // [!endregion contract] 24 | 25 | // [!region tests] 26 | #[cfg(test)] 27 | mod tests { 28 | use snforge_std::{ContractClassTrait, DeclareResultTrait, declare, load}; 29 | 30 | #[test] 31 | fn should_deploy_with_constructor_init_value() { 32 | let contract = declare("ConstructorContract").unwrap().contract_class(); 33 | let mut constructor_calldata: Array = array![]; 34 | 1_u128.serialize(ref constructor_calldata); // a 35 | 2_u8.serialize(ref constructor_calldata); // b 36 | 3_u256.serialize(ref constructor_calldata); // c 37 | let (contract_address, _) = contract.deploy(@constructor_calldata).unwrap(); 38 | 39 | let mut loaded = load(contract_address, selector!("a"), 1).span(); 40 | assert_eq!(Serde::deserialize(ref loaded).unwrap(), 1); 41 | 42 | let mut loaded = load(contract_address, selector!("b"), 1).span(); 43 | assert_eq!(Serde::deserialize(ref loaded).unwrap(), 2); 44 | 45 | let mut loaded = load(contract_address, selector!("c"), 2).span(); 46 | assert_eq!(Serde::deserialize(ref loaded).unwrap(), 3); 47 | } 48 | } 49 | // [!endregion tests] 50 | 51 | 52 | -------------------------------------------------------------------------------- /listings/getting-started/constructor/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod constructor; 2 | -------------------------------------------------------------------------------- /listings/getting-started/counter/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/getting-started/counter/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "counter" 6 | version.workspace = true 7 | -------------------------------------------------------------------------------- /listings/getting-started/counter/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "counter" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/getting-started/counter/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod counter; 2 | -------------------------------------------------------------------------------- /listings/getting-started/custom_type_entrypoints/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/getting-started/custom_type_entrypoints/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "custom_type_serde" 6 | version.workspace = true 7 | -------------------------------------------------------------------------------- /listings/getting-started/custom_type_entrypoints/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "custom_type_entrypoints" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/getting-started/custom_type_entrypoints/src/contract.cairo: -------------------------------------------------------------------------------- 1 | // [!region contract] 2 | #[starknet::interface] 3 | trait ISerdeCustomType { 4 | fn person_input(ref self: TContractState, person: Person); 5 | fn person_output(self: @TContractState) -> Person; 6 | } 7 | 8 | // Deriving the `Serde` trait allows us to use 9 | // the `Person` type as an entrypoint parameter and as a return value 10 | #[derive(Drop, Serde)] 11 | struct Person { 12 | age: u8, 13 | name: felt252, 14 | } 15 | 16 | #[starknet::contract] 17 | mod SerdeCustomType { 18 | use super::Person; 19 | use super::ISerdeCustomType; 20 | 21 | #[storage] 22 | struct Storage {} 23 | 24 | #[abi(embed_v0)] 25 | impl SerdeCustomType of ISerdeCustomType { 26 | fn person_input(ref self: ContractState, person: Person) {} 27 | 28 | fn person_output(self: @ContractState) -> Person { 29 | Person { age: 10, name: 'Joe' } 30 | } 31 | } 32 | } 33 | // [!endregion contract] 34 | 35 | #[cfg(test)] 36 | mod tests { 37 | use super::{ISerdeCustomTypeDispatcher, ISerdeCustomTypeDispatcherTrait, Person}; 38 | use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; 39 | 40 | fn deploy() -> ISerdeCustomTypeDispatcher { 41 | let contract = declare("SerdeCustomType").unwrap().contract_class(); 42 | let (contract_address, _) = contract.deploy(@array![]).unwrap(); 43 | ISerdeCustomTypeDispatcher { contract_address } 44 | } 45 | 46 | #[test] 47 | fn should_get_person_output() { 48 | let contract = deploy(); 49 | let expected_person = Person { age: 10, name: 'Joe' }; 50 | 51 | let received_person = contract.person_output(); 52 | let age_received = received_person.age; 53 | let name_received = received_person.name; 54 | 55 | assert_eq!(age_received, expected_person.age); 56 | assert_eq!(name_received, expected_person.name); 57 | } 58 | 59 | #[test] 60 | fn should_call_person_input() { 61 | let mut contract = deploy(); 62 | let expected_person = Person { age: 10, name: 'Joe' }; 63 | contract.person_input(expected_person); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /listings/getting-started/custom_type_entrypoints/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod contract; 2 | -------------------------------------------------------------------------------- /listings/getting-started/errors/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/getting-started/errors/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "errors" 6 | version.workspace = true 7 | -------------------------------------------------------------------------------- /listings/getting-started/errors/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "errors" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/getting-started/errors/src/custom_errors.cairo: -------------------------------------------------------------------------------- 1 | #[starknet::interface] 2 | trait ICustomErrors { 3 | fn test_assert(self: @TContractState, i: u256); 4 | fn test_panic(self: @TContractState, i: u256); 5 | } 6 | 7 | // [!region contract] 8 | mod Errors { 9 | pub const NOT_POSITIVE: felt252 = 'must be greater than 0'; 10 | pub const NOT_NULL: felt252 = 'must not be null'; 11 | } 12 | 13 | #[starknet::contract] 14 | mod CustomErrorsContract { 15 | use super::{Errors, ICustomErrors}; 16 | 17 | #[storage] 18 | struct Storage {} 19 | 20 | #[abi(embed_v0)] 21 | impl CustomErrorsContract of ICustomErrors { 22 | fn test_assert(self: @ContractState, i: u256) { 23 | assert(i > 0, Errors::NOT_POSITIVE); 24 | } 25 | 26 | fn test_panic(self: @ContractState, i: u256) { 27 | if (i == 0) { 28 | core::panic_with_felt252(Errors::NOT_NULL); 29 | } 30 | } 31 | } 32 | } 33 | // [!endregion contract] 34 | 35 | #[cfg(test)] 36 | mod test { 37 | use super::{ICustomErrorsDispatcher, ICustomErrorsDispatcherTrait}; 38 | use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; 39 | 40 | fn deploy() -> ICustomErrorsDispatcher { 41 | let contract = declare("CustomErrorsContract").unwrap().contract_class(); 42 | let (contract_address, _) = contract.deploy(@array![]).unwrap(); 43 | ICustomErrorsDispatcher { contract_address } 44 | } 45 | 46 | #[test] 47 | #[should_panic(expected: 'must not be null')] 48 | fn should_panic() { 49 | let contract = deploy(); 50 | contract.test_panic(0); 51 | } 52 | 53 | #[test] 54 | #[should_panic(expected: 'must be greater than 0')] 55 | fn should_assert() { 56 | let contract = deploy(); 57 | contract.test_assert(0); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /listings/getting-started/errors/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod simple_errors; 2 | mod custom_errors; 3 | mod vault_errors; 4 | -------------------------------------------------------------------------------- /listings/getting-started/errors/src/simple_errors.cairo: -------------------------------------------------------------------------------- 1 | #[starknet::interface] 2 | trait IErrors { 3 | fn test_assert(self: @TContractState, i: u256); 4 | fn test_panic(self: @TContractState, i: u256); 5 | } 6 | 7 | // [!region contract] 8 | #[starknet::contract] 9 | mod ErrorsContract { 10 | use super::IErrors; 11 | 12 | #[storage] 13 | struct Storage {} 14 | 15 | #[abi(embed_v0)] 16 | impl ErrorsContract of IErrors { 17 | // Assert used to validate a condition 18 | // and abort execution if the condition is not met 19 | fn test_assert(self: @ContractState, i: u256) { 20 | assert(i > 0, 'i must be greater than 0'); 21 | let x = 10; 22 | assert!(i > x, "i must be greater than {}", x); 23 | } 24 | 25 | // Panic used to abort execution directly 26 | fn test_panic(self: @ContractState, i: u256) { 27 | if (i == 0) { 28 | core::panic_with_felt252('i must not be 0'); 29 | } 30 | if (i < 10) { 31 | panic!("i: {} must be greater than 10", i); 32 | } 33 | } 34 | } 35 | } 36 | // [!endregion contract] 37 | 38 | #[cfg(test)] 39 | mod test { 40 | use super::{IErrorsDispatcher, IErrorsDispatcherTrait}; 41 | use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; 42 | 43 | fn deploy() -> IErrorsDispatcher { 44 | let contract = declare("ErrorsContract").unwrap().contract_class(); 45 | let (contract_address, _) = contract.deploy(@array![]).unwrap(); 46 | IErrorsDispatcher { contract_address } 47 | } 48 | 49 | #[test] 50 | #[should_panic(expected: 'i must be greater than 0')] 51 | fn should_assert_with_felt() { 52 | let contract = deploy(); 53 | contract.test_assert(0); 54 | } 55 | 56 | #[test] 57 | #[should_panic(expected: "i must be greater than 10")] 58 | fn should_assert_with_byte_array() { 59 | let contract = deploy(); 60 | contract.test_assert(5); 61 | } 62 | 63 | #[test] 64 | #[should_panic(expected: 'i must not be 0')] 65 | fn should_panic_with_felt() { 66 | let contract = deploy(); 67 | contract.test_panic(0); 68 | } 69 | 70 | #[test] 71 | #[should_panic(expected: "i: 5 must be greater than 10")] 72 | fn should_panic_with_byte_array() { 73 | let contract = deploy(); 74 | contract.test_panic(5); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /listings/getting-started/errors/src/vault_errors.cairo: -------------------------------------------------------------------------------- 1 | #[starknet::interface] 2 | trait IVaultErrors { 3 | fn deposit(ref self: TContractState, amount: u256); 4 | fn withdraw(ref self: TContractState, amount: u256); 5 | } 6 | 7 | // [!region contract] 8 | mod VaultErrors { 9 | pub const INSUFFICIENT_BALANCE: felt252 = 'insufficient_balance'; 10 | // you can define more errors here 11 | } 12 | 13 | #[starknet::contract] 14 | mod VaultErrorsContract { 15 | use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; 16 | use super::{VaultErrors, IVaultErrors}; 17 | 18 | #[storage] 19 | struct Storage { 20 | balance: u256, 21 | } 22 | 23 | #[abi(embed_v0)] 24 | impl VaultErrorsContract of IVaultErrors { 25 | fn deposit(ref self: ContractState, amount: u256) { 26 | let mut balance = self.balance.read(); 27 | balance = balance + amount; 28 | self.balance.write(balance); 29 | } 30 | 31 | fn withdraw(ref self: ContractState, amount: u256) { 32 | let mut balance = self.balance.read(); 33 | 34 | assert(balance >= amount, VaultErrors::INSUFFICIENT_BALANCE); 35 | 36 | // Or using panic: 37 | if (balance < amount) { 38 | core::panic_with_felt252(VaultErrors::INSUFFICIENT_BALANCE); 39 | } 40 | 41 | let balance = balance - amount; 42 | 43 | self.balance.write(balance); 44 | } 45 | } 46 | } 47 | // [!endregion contract] 48 | 49 | #[cfg(test)] 50 | mod test { 51 | use super::{IVaultErrorsDispatcher, IVaultErrorsDispatcherTrait}; 52 | use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; 53 | 54 | fn deploy() -> IVaultErrorsDispatcher { 55 | let contract = declare("VaultErrorsContract").unwrap().contract_class(); 56 | let (contract_address, _) = contract.deploy(@array![]).unwrap(); 57 | IVaultErrorsDispatcher { contract_address } 58 | } 59 | 60 | #[test] 61 | fn should_deposit_and_withdraw() { 62 | let mut contract = deploy(); 63 | contract.deposit(10); 64 | contract.withdraw(5); 65 | } 66 | 67 | #[test] 68 | #[should_panic(expected: 'insufficient_balance')] 69 | fn should_panic_on_insufficient_balance() { 70 | let mut contract = deploy(); 71 | contract.deposit(10); 72 | contract.withdraw(15); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /listings/getting-started/events/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/getting-started/events/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "events" 6 | version.workspace = true 7 | -------------------------------------------------------------------------------- /listings/getting-started/events/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "events" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/getting-started/events/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod counter; 2 | -------------------------------------------------------------------------------- /listings/getting-started/factory/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/getting-started/factory/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "factory" 6 | version.workspace = true 7 | -------------------------------------------------------------------------------- /listings/getting-started/factory/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "factory" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | cairo_test.workspace = true 11 | 12 | [scripts] 13 | test.workspace = true 14 | 15 | [[target.starknet-contract]] 16 | -------------------------------------------------------------------------------- /listings/getting-started/factory/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod simple_factory; 2 | -------------------------------------------------------------------------------- /listings/getting-started/mappings/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/getting-started/mappings/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "maps" 6 | version.workspace = true 7 | -------------------------------------------------------------------------------- /listings/getting-started/mappings/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mappings" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/getting-started/mappings/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod mappings; 2 | -------------------------------------------------------------------------------- /listings/getting-started/mappings/src/mappings.cairo: -------------------------------------------------------------------------------- 1 | use starknet::ContractAddress; 2 | 3 | #[starknet::interface] 4 | trait IMapContract { 5 | fn set(ref self: TContractState, key: ContractAddress, value: felt252); 6 | fn get(self: @TContractState, key: ContractAddress) -> felt252; 7 | } 8 | 9 | // [!region contract] 10 | #[starknet::contract] 11 | mod MapContract { 12 | use super::IMapContract; 13 | use starknet::ContractAddress; 14 | use starknet::storage::{Map, StorageMapReadAccess, StorageMapWriteAccess}; 15 | 16 | #[storage] 17 | struct Storage { 18 | map: Map, 19 | } 20 | 21 | #[abi(embed_v0)] 22 | impl MapContractImpl of IMapContract { 23 | fn set(ref self: ContractState, key: ContractAddress, value: felt252) { 24 | self.map.write(key, value); 25 | } 26 | 27 | fn get(self: @ContractState, key: ContractAddress) -> felt252 { 28 | self.map.read(key) 29 | } 30 | } 31 | } 32 | // [!endregion contract] 33 | 34 | #[cfg(test)] 35 | mod test { 36 | use super::{IMapContractDispatcher, IMapContractDispatcherTrait}; 37 | use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; 38 | use starknet::contract_address_const; 39 | 40 | fn deploy() -> IMapContractDispatcher { 41 | let contract = declare("MapContract").unwrap().contract_class(); 42 | let (contract_address, _) = contract.deploy(@array![]).unwrap(); 43 | IMapContractDispatcher { contract_address } 44 | } 45 | 46 | #[test] 47 | fn test_deploy_and_set_get() { 48 | let contract = deploy(); 49 | 50 | // Write to map. 51 | let key = contract_address_const::<'key'>(); 52 | let value: felt252 = 1; 53 | contract.set(key, value); 54 | 55 | // Read from map. 56 | let read_value = contract.get(key); 57 | assert_eq!(read_value, value); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /listings/getting-started/storage/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/getting-started/storage/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "storage" 6 | version.workspace = true 7 | -------------------------------------------------------------------------------- /listings/getting-started/storage/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "storage" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/getting-started/storage/src/contract.cairo: -------------------------------------------------------------------------------- 1 | // [!region contract] 2 | #[starknet::contract] 3 | mod Contract { 4 | #[storage] 5 | struct Storage { 6 | a: u128, 7 | b: u8, 8 | c: u256, 9 | } 10 | } 11 | // [!endregion contract] 12 | 13 | #[cfg(test)] 14 | mod test { 15 | use snforge_std::{ContractClassTrait, DeclareResultTrait, declare, load}; 16 | 17 | #[test] 18 | fn test_storage_members() { 19 | let contract = declare("Contract").unwrap().contract_class(); 20 | let (contract_address, _) = contract.deploy(@array![]).unwrap(); 21 | 22 | let mut loaded = load(contract_address, selector!("a"), 1).span(); 23 | assert_eq!(Serde::deserialize(ref loaded).unwrap(), 0); 24 | 25 | let mut loaded = load(contract_address, selector!("b"), 1).span(); 26 | assert_eq!(Serde::deserialize(ref loaded).unwrap(), 0); 27 | 28 | let mut loaded = load(contract_address, selector!("c"), 2).span(); 29 | assert_eq!(Serde::deserialize(ref loaded).unwrap(), 0); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /listings/getting-started/storage/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod minimal_contract; 2 | mod contract; 3 | -------------------------------------------------------------------------------- /listings/getting-started/storage/src/minimal_contract.cairo: -------------------------------------------------------------------------------- 1 | // [!region contract] 2 | #[starknet::contract] 3 | mod Contract { 4 | #[storage] 5 | struct Storage {} 6 | } 7 | // [!endregion contract] 8 | 9 | // [!region tests] 10 | #[cfg(test)] 11 | mod test { 12 | use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; 13 | 14 | #[test] 15 | fn test_can_deploy() { 16 | let contract = declare("Contract").unwrap().contract_class(); 17 | let (_contract_address, _) = contract.deploy(@array![]).unwrap(); 18 | // Not much to test 19 | } 20 | } 21 | // [!endregion tests] 22 | 23 | 24 | -------------------------------------------------------------------------------- /listings/getting-started/storing_custom_types/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/getting-started/storing_custom_types/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "storing_custom_types" 6 | version.workspace = true 7 | -------------------------------------------------------------------------------- /listings/getting-started/storing_custom_types/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "storing_custom_types" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/getting-started/storing_custom_types/src/contract.cairo: -------------------------------------------------------------------------------- 1 | // [!region contract] 2 | #[starknet::interface] 3 | trait IStoringCustomType { 4 | fn set_person(ref self: TContractState, person: Person); 5 | fn set_name(ref self: TContractState, name: felt252); 6 | } 7 | 8 | // Deriving the starknet::Store trait 9 | // allows us to store the `Person` struct in the contract's storage. 10 | #[derive(Drop, Serde, Copy, starknet::Store)] 11 | struct Person { 12 | age: u8, 13 | name: felt252, 14 | } 15 | 16 | #[starknet::contract] 17 | mod StoringCustomType { 18 | use starknet::storage::StoragePointerWriteAccess; 19 | use super::Person; 20 | use super::IStoringCustomType; 21 | 22 | #[storage] 23 | struct Storage { 24 | person: Person, 25 | } 26 | 27 | #[abi(embed_v0)] 28 | impl StoringCustomType of IStoringCustomType { 29 | fn set_person(ref self: ContractState, person: Person) { 30 | self.person.write(person); 31 | } 32 | 33 | // [!region set_name] 34 | fn set_name(ref self: ContractState, name: felt252) { 35 | self.person.name.write(name); 36 | } 37 | // [!endregion set_name] 38 | } 39 | } 40 | // [!endregion contract] 41 | 42 | #[cfg(test)] 43 | mod tests { 44 | use super::{IStoringCustomTypeDispatcherTrait, IStoringCustomTypeDispatcher, Person}; 45 | use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; 46 | 47 | fn deploy() -> IStoringCustomTypeDispatcher { 48 | let contract = declare("StoringCustomType").unwrap().contract_class(); 49 | let (contract_address, _) = contract.deploy(@array![]).unwrap(); 50 | IStoringCustomTypeDispatcher { contract_address } 51 | } 52 | 53 | 54 | #[test] 55 | fn can_call_set_person() { 56 | let mut contract = deploy(); 57 | let person = Person { age: 10, name: 'Joe' }; 58 | contract.set_person(person); 59 | } 60 | 61 | #[test] 62 | fn can_call_set_name() { 63 | let mut contract = deploy(); 64 | contract.set_name('John'); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /listings/getting-started/storing_custom_types/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod contract; 2 | -------------------------------------------------------------------------------- /listings/getting-started/testing_how_to/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/getting-started/testing_how_to/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "sbe_testing" 6 | version.workspace = true 7 | -------------------------------------------------------------------------------- /listings/getting-started/testing_how_to/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "testing_how_to" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/getting-started/testing_how_to/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod contract; 2 | pub use contract::{ 3 | InventoryContract, IInventoryContractDispatcher, IInventoryContractDispatcherTrait, 4 | }; 5 | 6 | #[cfg(test)] 7 | mod test_contract; 8 | -------------------------------------------------------------------------------- /listings/getting-started/variables/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/getting-started/variables/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "variables" 6 | version.workspace = true 7 | -------------------------------------------------------------------------------- /listings/getting-started/variables/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "variables" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/getting-started/variables/src/global_variables.cairo: -------------------------------------------------------------------------------- 1 | // [!region contract] 2 | #[starknet::contract] 3 | pub mod GlobalVariablesContract { 4 | // import the required functions from the starknet core library 5 | use starknet::get_caller_address; 6 | 7 | #[storage] 8 | struct Storage {} 9 | 10 | pub fn foo(ref self: ContractState) { 11 | // Call the get_caller_address function to get the sender address 12 | let _caller = get_caller_address(); 13 | // ... 14 | } 15 | } 16 | // [!endregion contract] 17 | 18 | // Not much to test 19 | 20 | 21 | -------------------------------------------------------------------------------- /listings/getting-started/variables/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod storage_variables; 2 | mod local_variables; 3 | mod global_variables; 4 | -------------------------------------------------------------------------------- /listings/getting-started/variables/src/local_variables.cairo: -------------------------------------------------------------------------------- 1 | // [!region contract] 2 | #[starknet::contract] 3 | mod LocalVariablesContract { 4 | #[storage] 5 | struct Storage {} 6 | 7 | pub fn do_something(value: u32) -> u32 { 8 | // This variable is local to the current block. 9 | // It can't be accessed once it goes out of scope. 10 | let increment = 10; 11 | 12 | { 13 | // The scope of a code block allows for local variable declaration 14 | // We can access variables defined in higher scopes. 15 | let sum = value + increment; 16 | sum 17 | } 18 | // We can't access the variable `sum` here, as it's out of scope. 19 | } 20 | } 21 | // [!endregion contract] 22 | 23 | #[cfg(test)] 24 | mod test { 25 | use super::LocalVariablesContract::do_something; 26 | 27 | #[test] 28 | fn test_can_do_something() { 29 | let value = 10; 30 | assert_eq!(do_something(value), value + 10); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /listings/getting-started/variables/src/storage_variables.cairo: -------------------------------------------------------------------------------- 1 | // [!region contract] 2 | #[starknet::interface] 3 | trait IStorageVariable { 4 | fn set(ref self: TContractState, value: u32); 5 | fn get(self: @TContractState) -> u32; 6 | } 7 | 8 | #[starknet::contract] 9 | mod StorageVariablesContract { 10 | // You need to import these storage functions to read and write to storage variables 11 | use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; 12 | use super::IStorageVariable; 13 | 14 | // All storage variables are contained in a struct called Storage 15 | // annotated with the `#[storage]` attribute 16 | #[storage] 17 | struct Storage { 18 | // Storage variable holding a number 19 | value: u32, 20 | } 21 | 22 | #[abi(embed_v0)] 23 | impl StorageVariables of IStorageVariable { 24 | // Write to storage variables by sending a transaction 25 | // that calls an external function 26 | fn set(ref self: ContractState, value: u32) { 27 | self.value.write(value); 28 | } 29 | 30 | // Read from storage variables without sending transactions 31 | fn get(self: @ContractState) -> u32 { 32 | self.value.read() 33 | } 34 | } 35 | } 36 | // [!endregion contract] 37 | 38 | #[cfg(test)] 39 | mod test { 40 | use super::{IStorageVariableDispatcher, IStorageVariableDispatcherTrait}; 41 | use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; 42 | 43 | #[test] 44 | fn test_can_deploy_and_mutate_storage() { 45 | let contract = declare("StorageVariablesContract").unwrap().contract_class(); 46 | let (contract_address, _) = contract.deploy(@array![]).unwrap(); 47 | let contract = IStorageVariableDispatcher { contract_address }; 48 | 49 | let initial_value = 10; 50 | 51 | contract.set(initial_value); 52 | assert_eq!(contract.get(), initial_value); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /listings/getting-started/visibility/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /listings/getting-started/visibility/Scarb.lock: -------------------------------------------------------------------------------- 1 | # Code generated by scarb DO NOT EDIT. 2 | version = 1 3 | 4 | [[package]] 5 | name = "visibility" 6 | version.workspace = true 7 | -------------------------------------------------------------------------------- /listings/getting-started/visibility/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "visibility" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | starknet.workspace = true 8 | 9 | [dev-dependencies] 10 | assert_macros.workspace = true 11 | snforge_std.workspace = true 12 | 13 | [scripts] 14 | test.workspace = true 15 | 16 | [[target.starknet-contract]] 17 | -------------------------------------------------------------------------------- /listings/getting-started/visibility/src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod visibility; 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starknet-by-example", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "scripts": { 6 | "dev": "vocs dev", 7 | "build": "vocs build", 8 | "preview": "vocs preview" 9 | }, 10 | "dependencies": { 11 | "@types/react": "latest", 12 | "react": "^19.0.0", 13 | "react-dom": "^19.0.0", 14 | "rehype-katex": "^7.0.1", 15 | "remark-math": "^6.0.0", 16 | "typescript": "latest", 17 | "vocs": "1.0.0-alpha.62" 18 | }, 19 | "devDependencies": { 20 | "@types/react-dom": "^18.3.1", 21 | "autoprefixer": "^10.4.20", 22 | "postcss": "^8.4.47", 23 | "tailwindcss": "^3.4.14" 24 | }, 25 | "pnpm": { 26 | "patchedDependencies": { 27 | "vocs": "patches/vocs.patch" 28 | }, 29 | "overrides": { 30 | "vocs>shiki": "^1.23.0 <1.24.0", 31 | "vocs>@shikijs/core": "^1.23.0 <1.24.0", 32 | "vocs>@shikijs/types": "^1.23.0 <1.24.0", 33 | "vocs>@shikijs/rehype": "^1.23.0 <1.24.0", 34 | "vocs>@shikijs/twoslash": "^1.23.0 <1.24.0", 35 | "vocs>@shikijs/transformers": "^1.23.0 <1.24.0" 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /pages/advanced-concepts/account_abstraction/account_spending_limits.md: -------------------------------------------------------------------------------- 1 | # Account Contract with Spending Limits 2 | 3 | In this example, we will write an account contract with spending limits. 4 | 5 | Before proceeding with this example, make sure that you read the [intro](/advanced-concepts/account_abstraction/account_contract) to account contracts on Starknet which will help you understand how Starknet account contracts work. 6 | 7 | ## Key Specifications 8 | 9 | The account contract will have the following features: 10 | 11 | - Spending limits can be added for any ERC-20 token with any amount by the account owner. 12 | - A limit will reset after a specified time. The account owner can set any time limit they want (daily, weekly or 12 hours), but once set, it cannot be changed by anyone. 13 | 14 | ### How to detect that a function call is a spending transaction? 15 | 16 | We need to identify the function calls that are spending transactions. 17 | The token standard (i.e. ERC-20) is defined in the [SNIP-2](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-2.md). 18 | In this example, we will consider the "approve" and "transfer" functions as spending transactions. 19 | 20 | - If one of the calls has the "approve" or "transfer" function selectors, the spending limit will decrease accordingly by the amount given in the function call. If there is no limit left, the transaction will revert. 21 | 22 | -------------------------------------------------------------------------------- /pages/advanced-concepts/account_abstraction/index.md: -------------------------------------------------------------------------------- 1 | # Account Abstraction on Starknet 2 | 3 | An account is an unique entity that can send transactions, users usually use wallets to manage their accounts. 4 | 5 | Historically, in Ethereum, all accounts were Externally Owned Accounts (_EOA_) and were controlled by private keys. This is a simple and secure way to manage accounts, but it has limitations as the account logic is hardcoded in the protocol. 6 | 7 | Account Abstraction (_AA_) is the concept behind abstracting parts of the account logic to allow for a more flexible account system. 8 | This replaces EOA with Account Contracts, which are smart contracts that implement the account logic. This opens up a lot of possibilities that can significantly improve the user experience when dealing with accounts. 9 | 10 | On Starknet, Account Abstraction is natively supported, and all accounts are Account Contracts. 11 | 12 | In this section we will how to implement an Account. 13 | 14 | -------------------------------------------------------------------------------- /pages/advanced-concepts/commit-reveal.md: -------------------------------------------------------------------------------- 1 | # Commit-Reveal 2 | 3 | The Commit-Reveal pattern is a fundamental blockchain pattern that enables to: 4 | 1. Commit to a value without revealing it *(commit phase)* 5 | 2. Reveal the value later to prove they knew it in advance *(reveal phase)* 6 | 7 | Some use cases: 8 | - **Blind Auctions**: Bidders commit to their bids first, then reveal them after the bidding period 9 | - **Voting Systems**: Voters commit their votes early, revealing them only after voting ends 10 | - **Knowledge Proofs/Attestations**: Proving you knew information at a specific time without revealing it immediately 11 | - **Fair Random Number Generation**: Players commit to random numbers that get combined later, making it harder to manipulate the outcome 12 | 13 | ## How It Works 14 | 15 | 1. **Commit Phase**: 16 | - User generates a value (`secret`) 17 | - User creates a hash of this value 18 | - User submits only the hash on-chain (`commit`) 19 | 20 | 2. **Reveal Phase**: 21 | - User submits the original value (`reveal`) 22 | - Contract verifies that the hash of the submitted value matches the previously committed hash 23 | - If it matches then it proves that the user knew the value at the commitment time 24 | 25 | ## Minimal commit-reveal contract: 26 | 27 | ```cairo 28 | // [!include ~/listings/advanced-concepts/commit_reveal/src/commit_reveal.cairo:contract] 29 | ``` 30 | 31 | Usage example: 32 | ```cairo 33 | // [!include ~/listings/advanced-concepts/commit_reveal/src/commit_reveal.cairo:offchain] 34 | ``` 35 | 36 | Some considerations: 37 | - The commit phase must complete before any reveals can start 38 | - Users might choose not to reveal if the outcome is unfavorable (consider adding stake/slashing mechanics to ensure reveals) 39 | -------------------------------------------------------------------------------- /pages/advanced-concepts/hash-solidity-compatible.md: -------------------------------------------------------------------------------- 1 | # Hash Solidity Compatible 2 | 3 | This contract demonstrates Keccak hashing in Cairo to match Solidity's keccak256. While both use Keccak, their endianness differs: Cairo is little-endian, Solidity big-endian. The contract achieves compatibility by hashing in big-endian using `keccak_u256s_be_inputs`, and reversing the bytes of the result with `u128_byte_reverse`. 4 | 5 | For example: 6 | 7 | ```cairo 8 | // [!include ~/listings/advanced-concepts/hash_solidity_compatible/src/contract.cairo] 9 | ``` 10 | -------------------------------------------------------------------------------- /pages/advanced-concepts/hashing.md: -------------------------------------------------------------------------------- 1 | # Hashing 2 | 3 | Hashing is a cryptographic technique that allows you to transform a variable length input into a fixed length output. 4 | The resulting output is called a hash and it's completely different from the input. 5 | Hash functions are deterministic, meaning that the same input will always produce the same output. 6 | 7 | The two hash functions provided by the Cairo library are `Poseidon` and `Pedersen`. 8 | Pedersen hashes were used in the past (but are still used in some scenarios for backward compatibility), while Poseidon hashes are the standard nowadays since they were designed to be very efficient for Zero Knowledge proof systems. 9 | 10 | In Cairo, it's possible to hash all types that can be converted to `felt252` since they natively implement the `Hash` trait. It's also possible to hash more complex types, like structs, by deriving the Hash trait with the `#[derive(Hash)]` attribute, but only if all the struct's fields are themselves hashable. 11 | 12 | To hash a value, you first need to initialize a hash state with the `new` method of the `HashStateTrait`. Then, you can update the hash state with the `update` method. You can accumulate multiple updates if necessary. Finally, the `finalize` method returns the final hash value as a `felt252`. 13 | 14 | ```cairo 15 | // [!include ~/listings/advanced-concepts/hash_trait/src/hash_trait.cairo:hash] 16 | ``` 17 | -------------------------------------------------------------------------------- /pages/advanced-concepts/library_calls.md: -------------------------------------------------------------------------------- 1 | # Library Calls 2 | 3 | External calls can be made on Starknet by two means: Contract dispatchers or Library dispatchers. Dispatchers are automatically created and exported by the compiler when a contract interface is defined. 4 | 5 | With Contract dispatcher we are calling an already deployed contract (with associated state), therefore contract address is passed to the dispatcher to make the call. However, with library dispatcher we are simply making function calls to declared contract **classes** (stateless). 6 | 7 | Contract dispatcher call is synonymous to external calls in Solidity, while library dispatcher call is synonymous to delegate call. 8 | 9 | For further reading: [Cairo book](https://book.cairo-lang.org/ch15-03-executing-code-from-another-class.html#library-calls) 10 | 11 | ```cairo 12 | // [!include ~/listings/advanced-concepts/library_calls/src/library_call.cairo:library_dispatcher] 13 | ``` 14 | -------------------------------------------------------------------------------- /pages/advanced-concepts/optimisations/store_using_packing.md: -------------------------------------------------------------------------------- 1 | # Storage optimisation 2 | 3 | A smart contract has a limited amount of **storage slots**. Each slot can store a single `felt252` value. 4 | Writing to a storage slot has a cost, so we want to use as few storage slots as possible. 5 | 6 | In Cairo, every type is derived from the `felt252` type, which uses 252 bits to store a value. 7 | This design is quite simple, but it does have a drawback: it is not storage efficient. For example, if we want to store a `u8` value, we need to use an entire slot, even though we only need 8 bits. 8 | 9 | ## Packing 10 | 11 | When storing multiple values, we can use a technique called **packing**. Packing is a technique that allows us to store multiple values in a single `felt252` value. This is done by using the bits of the `felt252` value to store multiple values. 12 | 13 | For example, if we want to store two `u8` values, we can use the first 8 bits of the `felt252` value to store the first `u8` value, and the last 8 bits to store the second `u8` value. This way, we can store two `u8` values in a single `felt252` value. 14 | 15 | Cairo provides a built-in store using packing that you can use with the `StorePacking` trait. 16 | 17 | ```cairo 18 | trait StorePacking { 19 | fn pack(value: T) -> PackedT; 20 | fn unpack(value: PackedT) -> T; 21 | } 22 | ``` 23 | 24 | This allows us to store the type `T` by first packing it into the type `PackedT` with the `pack` function, and then storing the `PackedT` value with it's `Store` implementation. When reading the value, we first retrieve the `PackedT` value, and then unpack it into the type `T` using the `unpack` function. 25 | 26 | Here's an example of storing a `Time` struct with two `u8` values using the `StorePacking` trait: 27 | 28 | ```cairo 29 | // [!include ~/listings/advanced-concepts/store_using_packing/src/contract.cairo] 30 | ``` 31 | -------------------------------------------------------------------------------- /pages/advanced-concepts/plugins.md: -------------------------------------------------------------------------------- 1 | # Plugins 2 | 3 | Compiler plugins in Cairo are a way to generate additional code for specific items during the compilation process. 4 | 5 | There are already a set of core plugins that you may have already used, such as `#[derive]`, `#[cfg]`, `#[generate_trait]`, etc. 6 | 7 | It is possible to create your own plugins in Cairo. You can learn more about how to do that in the [hello-cairo-plugin](https://github.com/piwonskp/hello-cairo-plugin) repository. 8 | -------------------------------------------------------------------------------- /pages/advanced-concepts/sierra_ir_storage_contract.md: -------------------------------------------------------------------------------- 1 | ```cairo 2 | // [!include ~/listings/getting-started/variables/storage_variables.sierra] 3 | ``` -------------------------------------------------------------------------------- /pages/advanced-concepts/signature_verification.md: -------------------------------------------------------------------------------- 1 | # ECDSA Verification 2 | 3 | This is the Cairo adaptation of the [Solidity by Example - Verifying Signature](https://solidity-by-example.org/signature/). 4 | Messages can be signed off chain and then verified on chain using a smart contract. 5 | 6 | ```cairo 7 | // [!include ~/listings/advanced-concepts/ecdsa_verification/src/ecdsa_verification.cairo:contract] 8 | ``` 9 | -------------------------------------------------------------------------------- /pages/advanced-concepts/struct-mapping-key.md: -------------------------------------------------------------------------------- 1 | # Structs as mapping keys 2 | 3 | In order to use structs as mapping keys, you can use `#[derive(Hash)]` on the struct definition. This will automatically generate a hash function for the struct that can be used to represent the struct as a key in a `Map`. 4 | 5 | Consider the following example in which we would like to use an object of 6 | type `Pet` as a key in a `Map`. The `Pet` struct has three fields: `name`, `age` and `owner`. We consider that the combination of these three fields uniquely identifies a pet. 7 | 8 | ```cairo 9 | // [!include ~/listings/advanced-concepts/struct_as_mapping_key/src/contract.cairo] 10 | ``` 11 | -------------------------------------------------------------------------------- /pages/advanced-concepts/write_to_any_slot.md: -------------------------------------------------------------------------------- 1 | # Writing to any storage slot 2 | 3 | On Starknet, a contract's storage is a map with $( 2^{251} )$ slots, where each slot is a `felt252` which is initialized to 0. 4 | The address of storage variables is computed at compile time using the formula: 5 | $$ 6 | storage\_variable\_address~\text{:=}~\text{pedersen}(\text{keccak}(variable\_name),~keys) 7 | $$ 8 | 9 | Interactions with storage variables are commonly performed using the `self.var.read()` and `self.var.write()` functions. 10 | 11 | Nevertheless, we can use the `storage_write_syscall` and `storage_read_syscall` syscalls, to write to and read from any storage slot. 12 | This is useful when writing to storage variables that are not known at compile time, or to ensure that even if the contract is upgraded and the computation method of storage variable addresses changes, they remain accessible. 13 | 14 | In the following example, we use the Poseidon hash function to compute the address of a storage variable. Poseidon is a ZK-friendly hash function that is cheaper and faster than Pedersen, making it an excellent choice for onchain computations. Once the address is computed, we use the storage syscalls to interact with it. 15 | 16 | ```cairo 17 | // [!include ~/listings/advanced-concepts/write_to_any_slot/src/contract.cairo] 18 | ``` 19 | -------------------------------------------------------------------------------- /pages/applications/advanced_factory.md: -------------------------------------------------------------------------------- 1 | # AdvancedFactory: Crowdfunding 2 | 3 | This is an example of an advanced factory contract that manages crowdfunding Campaign contracts created in the ["Crowdfunding" chapter](/applications/crowdfunding). The advanced factory allows for a centralized creation and management of `Campaign` contracts on the Starknet blockchain, ensuring that they adhere to a standard interface and can be easily upgraded. 4 | 5 | Key Features 6 | 1. **Campaign Creation**: Users can create new crowdfunding campaigns with specific details such as title, description, goal, and duration. 7 | 2. **Campaign Management**: The factory contract stores and manages the campaigns, allowing for upgrades and tracking. 8 | 3. **Upgrade Mechanism**: The factory owner can update the implementation of the campaign contract, ensuring that all campaigns benefit from improvements and bug fixes. 9 | - the factory only updates it's `Campaign` class hash and emits an event to notify any listeners, but the `Campaign` creators are in the end responsible for actually upgrading their contracts. 10 | 11 | ```cairo 12 | // [!include ~/listings/applications/advanced_factory/src/contract.cairo:contract] 13 | ``` 14 | -------------------------------------------------------------------------------- /pages/applications/constant-product-amm.md: -------------------------------------------------------------------------------- 1 | # Constant Product AMM 2 | 3 | This is the Cairo adaptation of the 4 | [Solidity by Example - Constant Product AMM](https://solidity-by-example.org/defi/constant-product-amm/). 5 | 6 | In this contract, we implement a simple Automated Market Maker (AMM) following 7 | the **constant product formula**: $( x \cdot y = k )$. This formula ensures 8 | that the product of the two token reserves ($x$ and $y$ representing the tokens 9 | being swapper) remains constant, regardless of trades. Here, we provide 10 | liquidity pools that allow users to trade between two tokens or add and remove 11 | liquidity from the pool. 12 | 13 | ## Key Concepts 14 | 15 | 1. **approve() before swap or adding liquidity**: 16 | Before interacting with the AMM (whether through swaps or adding liquidity), 17 | the user must approve the contract to spend their tokens. This is done by 18 | calling the `approve()` function on the ERC20 token contracts, allowing the 19 | AMM to transfer the required tokens on behalf of the user. 20 | 21 | 2. **Constant Product Formula for Swaps**: 22 | The swap function operates based on the constant product formula $( x \cdot 23 | y = k )$, where $x$ and $y$ are the token reserves. When a user swaps one 24 | token for another, the product of the reserves remains constant, which 25 | determines how much of the other token the user will receive. 26 | 27 | 3. **Shares and Token Ratios for Liquidity**: 28 | When adding liquidity, users provide both tokens in the ratio of the current 29 | reserves. The number of shares (liquidity tokens) the user receives 30 | represents their contribution to the pool. Similarly, when removing 31 | liquidity, users receive back tokens proportional to the number of shares 32 | they burn. 33 | 34 | ```cairo 35 | // [!include ~/listings/applications/constant_product_amm/src/contracts.cairo:ConstantProductAmmContract] 36 | ``` 37 | -------------------------------------------------------------------------------- /pages/applications/crowdfunding.md: -------------------------------------------------------------------------------- 1 | # Crowdfunding Campaign 2 | 3 | Crowdfunding is a method of raising capital through the collective effort of many individuals. It allows project creators to raise funds from a large number of people, usually through small contributions. 4 | 5 | 1. Contract admin creates a campaign in some user's name (i.e. creator). 6 | 2. Users can pledge, transferring their token to a campaign. 7 | 3. Users can "unpledge", retrieving their tokens. 8 | 4. The creator can at any point refund any of the users. 9 | 5. Once the total amount pledged is more than the campaign goal, the campaign funds are "locked" in the contract, meaning the users can no longer unpledge; they can still pledge though. 10 | 6. After the campaign ends, the campaign creator can claim the funds if the campaign goal is reached. 11 | 7. Otherwise, campaign did not reach it's goal, pledgers can retrieve their funds. 12 | 8. The creator can at any point cancel the campaign for whatever reason and refund all of the pledgers. 13 | 9. The contract admin can upgrade the contract implementation, refunding all of the users and resetting the campaign state (we will use this in the [Advanced Factory chapter](/applications/advanced_factory)). 14 | 15 | Because contract upgrades need to be able to refund all of the pledges, we need to be able to iterate over all of the pledgers and their amounts. Since iteration is not supported by `Map`, we need to create a custom storage type that will encompass pledge management. We use a component for this purpose. 16 | 17 | ```cairo 18 | // [!include ~/listings/applications/crowdfunding/src/campaign/pledgeable.cairo:component] 19 | ``` 20 | 21 | Now we can create the `Campaign` contract. 22 | 23 | 24 | ```cairo 25 | // [!include ~/listings/applications/crowdfunding/src/campaign.cairo:contract] 26 | ``` 27 | -------------------------------------------------------------------------------- /pages/applications/erc20.md: -------------------------------------------------------------------------------- 1 | # ERC20 Token 2 | 3 | Contracts that follow the [ERC20 Standard](https://eips.ethereum.org/EIPS/eip-20) are called ERC20 tokens. They are used to represent fungible assets. 4 | 5 | To create an ERC20 contract, it must implement the following interface: 6 | 7 | ```cairo 8 | // [!include ~/listings/applications/erc20/src/token.cairo:interface] 9 | ``` 10 | 11 | In Starknet, function names should be written in _snake_case_. This is not the case in Solidity, where function names are written in _camelCase_. 12 | The Starknet ERC20 interface is therefore slightly different from the Solidity ERC20 interface. 13 | 14 | Here's an implementation of the ERC20 interface in Cairo: 15 | 16 | ```cairo 17 | // [!include ~/listings/applications/erc20/src/token.cairo:erc20] 18 | ``` 19 | 20 | There's several other implementations, such as the [Open Zeppelin](https://docs.openzeppelin.com/contracts-cairo/erc20) or the [Cairo By Example](https://cairo-by-example.com/examples/erc20/) ones. 21 | -------------------------------------------------------------------------------- /pages/applications/erc721.md: -------------------------------------------------------------------------------- 1 | # ERC721 Token 2 | 3 | Contracts that follow the [ERC721 Standard](https://eips.ethereum.org/EIPS/eip-721) are called ERC721 tokens. They are used to represent non-fungible assets. 4 | 5 | :::note 6 | For a deeper understanding of the ERC721 interface specifications and its functionality, we highly recommend reading the EIP in detail. 7 | ::: 8 | 9 | To create an ERC721 contract, it must implement the following interface: 10 | 11 | ```cairo 12 | // [!include ~/listings/applications/erc721/src/interfaces.cairo:interface] 13 | ``` 14 | 15 | Because function names in Starknet should be written in _snake_case_, the Starknet ERC721 interface is slightly different from the Solidity ERC721 interface which uses _camelCase_. 16 | 17 | Here's an implementation of the ERC721 interface in Cairo: 18 | 19 | ```cairo 20 | // [!include ~/listings/applications/erc721/src/erc721.cairo] 21 | ``` 22 | 23 | There are other implementations, such as the [Open Zeppelin](https://docs.openzeppelin.com/contracts-cairo/erc721) one. 24 | -------------------------------------------------------------------------------- /pages/applications/nft_dutch_auction.md: -------------------------------------------------------------------------------- 1 | # NFT Dutch Auction 2 | 3 | This is the Cairo adaptation (with some modifications) of the [Solidity by example NFT Dutch Auction](https://solidity-by-example.org/app/dutch-auction/). 4 | 5 | Here's how it works: 6 | 7 | - The seller of the NFT deploys this contract with a startingPrice. 8 | - The auction lasts for a specified duration. 9 | - The price decreases over time. 10 | - Participants can purchase NFTs at any time as long as the totalSupply has not been reached. 11 | - The auction ends when either the totalSupply is reached or the duration has elapsed. 12 | 13 | ```cairo 14 | // [!include ~/listings/applications/nft_dutch_auction/src/nft_dutch_auction.cairo:contract] 15 | ``` 16 | -------------------------------------------------------------------------------- /pages/applications/signature_verification.md: -------------------------------------------------------------------------------- 1 | # Signature Verification 2 | -------------------------------------------------------------------------------- /pages/applications/simple_vault.md: -------------------------------------------------------------------------------- 1 | # Simple Defi Vault 2 | 3 | This is the Cairo adaptation of the [Solidity by Example - Vault](https://solidity-by-example.org/defi/vault/). 4 | Here's how it works: 5 | 6 | - When a user deposits a token, the contract calculates the amount of shares to mint. 7 | 8 | - When a user withdraws, the contract burns their shares, calculates the yield, and withdraws both the yield and the initial amount of tokens deposited. 9 | 10 | ```cairo 11 | // [!include ~/listings/applications/simple_vault/src/simple_vault.cairo] 12 | ``` 13 | -------------------------------------------------------------------------------- /pages/applications/staking.md: -------------------------------------------------------------------------------- 1 | # Staking contract 2 | 3 | The following staking contract is designed to allow users to stake tokens in exchange for reward tokens over a specified duration. Here's a quick summary of how it operates and what functionalities it supports: 4 | 5 | ### Key Features: 6 | 7 | 1. Token staking and unstaking: 8 | 9 | - Users can stake an ERC20 token, specified at deployment. 10 | - Users can withdraw their staked tokens at any time. 11 | 12 | 2. Reward calculation and distribution: 13 | 14 | - The rewards are distributed as an ERC20, also specified at deployment (can be different from the staking token). 15 | - Rewards are calculated based on the duration of staking and the amount the user staked relative to the total staked amount by all users. 16 | - A user’s reward accumulates over time up until the reward period's end and can be claimed anytime by the user. 17 | 18 | 3. Dynamic reward rates: 19 | 20 | - The reward rate is determined by the total amount of reward tokens over a set period (duration). 21 | - The reward rate can be adjusted during the rewards period if new rewards are added before the current reward period finishes. 22 | - Even after a reward period finishes, a new reward duration and new rewards can be set up if desired. 23 | 24 | 4. Ownership and administration: 25 | - Only the owner of the contract can set the rewards amount and duration. 26 | 27 | :::note 28 | The reward mechanism ensures that rewards are distributed fairly based on the amount and duration of tokens staked by each user. 29 | ::: 30 | 31 | The following implementation is the Cairo adaptation of the [Solidity by Example - Staking Rewards contract](https://solidity-by-example.org/defi/staking-rewards/). It includes a small adaptation to keep track of the amount of total distributed reward tokens and emit an event when the remaining reward token amount reaches 0. 32 | 33 | ```cairo 34 | // [!include ~/listings/applications/staking/src/contract.cairo] 35 | ``` 36 | -------------------------------------------------------------------------------- /pages/applications/timelock.md: -------------------------------------------------------------------------------- 1 | # TimeLock 2 | 3 | This is the Cairo adaptation of the [Solidity by example TimeLock](https://solidity-by-example.org/app/time-lock/). 4 | 5 | ```cairo 6 | // [!include ~/listings/applications/timelock/src/timelock.cairo] 7 | ``` 8 | -------------------------------------------------------------------------------- /pages/applications/upgradeable_contract.md: -------------------------------------------------------------------------------- 1 | # Upgradeable Contract 2 | 3 | In Starknet, contracts are divided into two parts: contract classes and contract 4 | instances. This division follows a similar concept used in object-oriented 5 | programming languages, where we distinguish between the definition and implementation 6 | of objects. 7 | 8 | A contract class is the definition of a contract: it specifies how the contract 9 | behaves. It contains essential information like the Cairo byte code, hint 10 | information, entry point names, and everything that defines its semantics 11 | unambiguously. 12 | 13 | To identify different contract classes, Starknet assigns a unique identifier to each 14 | class: the class hash. A contract instance is a deployed contract that corresponds to 15 | a specific contract class. Think of it as an instance of an object in languages like 16 | Java. 17 | 18 | Each class is identified by its class hash, which is analogous to a class name in an object-oriented programming language. A contract instance is a deployed contract corresponding to a class. 19 | 20 | You can upgrade a deployed contract to a newer version by calling the `replace_class_syscall` function. By using this function, you can update the class hash associated with a deployed contract, effectively upgrading its implementation. However, this will not modify the contract's storage, so all the data stored in the contract will remain the same. 21 | 22 | To illustrate this concept, let's consider an example with two contracts: `UpgradeableContract_V0`, and `UpgradeableContract_V1`. 23 | Start by deploying `UpgradeableContract_V0` as the initial version. Next, send a transaction that invokes the `upgrade` function, with the class hash of `UpgradeableContract_V1` as parameter to upgrade the class hash of the deployed contract to the `UpgradeableContract_V1` one. Then, call the `version` method on the contract to see that the contract was upgraded to the V1 version. 24 | 25 | ```cairo 26 | // [!include ~/listings/applications/upgradeable_contract/src/upgradeable_contract_v0.cairo:contract] 27 | ``` 28 | 29 | ```cairo 30 | // [!include ~/listings/applications/upgradeable_contract/src/upgradeable_contract_v1.cairo] 31 | ``` 32 | -------------------------------------------------------------------------------- /pages/cairo_cheatsheet/arrays.md: -------------------------------------------------------------------------------- 1 | # Arrays 2 | 3 | Arrays are collections of elements of the same type. 4 | The possible operations on arrays are defined with the `array::ArrayTrait` of the corelib: 5 | 6 | ```cairo 7 | trait ArrayTrait { 8 | fn new() -> Array; 9 | fn append(ref self: Array, value: T); 10 | fn pop_front(ref self: Array) -> Option nopanic; 11 | fn pop_front_consume(self: Array) -> Option<(Array, T)> nopanic; 12 | fn get(self: @Array, index: usize) -> Option>; 13 | fn at(self: @Array, index: usize) -> @T; 14 | fn len(self: @Array) -> usize; 15 | fn is_empty(self: @Array) -> bool; 16 | fn span(self: @Array) -> Span; 17 | } 18 | ``` 19 | 20 | For example: 21 | 22 | ```cairo 23 | // [!include ~/listings/cairo_cheatsheet/src/array_example.cairo] 24 | ``` 25 | -------------------------------------------------------------------------------- /pages/cairo_cheatsheet/dict.md: -------------------------------------------------------------------------------- 1 | # Dictionary 2 | 3 | A dictionary is a data structure used to store key-value pairs, enabling efficient data retrieval. The keys and values in a Cairo dictionary can be of various types, including Felt252. Dictionaries provide fast access to data, as they allow for quick lookups, insertions, and deletions based on the keys.The core functionality of a `Felt252Dict` is implemented in the trait `Felt252DictTrait`, which includes all basic operations. Among them, we can find: 4 | 5 | - `insert(felt252, T) -> ()` to write values to a dictionary instance. 6 | - `get(felt252) -> T` to read values from it. 7 | 8 | For example: 9 | 10 | ```cairo 11 | // [!include ~/listings/cairo_cheatsheet/src/dict_example.cairo:sheet] 12 | ``` 13 | -------------------------------------------------------------------------------- /pages/cairo_cheatsheet/enums.md: -------------------------------------------------------------------------------- 1 | # Enums 2 | 3 | Just like other programming languages, enums (enumerations) are used in cairo to define variables that can only hold a set of predefined variants (= enum options), enhancing code readability and safety. They facilitate strong type checking and are ideal for organizing related options and supporting structured logic through pattern matching for example, which is also described in the next chapter. 4 | 5 | In cairo, `enum variants` can hold different data types (the unit type, structs, other enums, tuples, default core library types, arrays, dictionaries, ...), as shown in the code snippet below. Furthermore, as a quick reminder, enums are expressions, meaning they can return values. 6 | 7 | ```cairo 8 | // [!include ~/listings/cairo_cheatsheet/src/enum_example.cairo:enums] 9 | ``` 10 | 11 | Enums can be declared both inside and outside a contract. If declared outside, they need to be imported inside using the `use` keyword, just like other imports. 12 | 13 | 1. Storing enums in contract 14 | 15 | - It is possible to store `enums` in the contract storage. But unlike most of the core library types which implement the `Store` trait, enums are custom types and therefore do not automatically implement the `Store` trait. The enum as well as all of its variants have to explicitly implement the `Store` trait in order for it to be stored inside a contract storage. 16 | 17 | - If all of its variants implement the `Store` trait, implementing the `Store` trait on the enum is as simple as deriving it, using `#[derive(starknet::Store)]` (as shown in example above). If not, the `Store` trait has to be manually implemented. 18 | 19 | 20 | 2. Enums as parameters and return values to entrypoints 21 | 22 | - It is possible to pass `enums` to contract entrypoints as parameters, as well as return them from entrypoints. For that purpose, the enum needs to be serializable and droppable, hence the derivation of traits `Serde` and `Drop` in the above code snippet. 23 | 24 | Here is an example of a contract illustrating the above statements : 25 | 26 | ```cairo 27 | // [!include ~/listings/cairo_cheatsheet/src/enum_example.cairo:enum_contract] 28 | ``` 29 | -------------------------------------------------------------------------------- /pages/cairo_cheatsheet/felt.md: -------------------------------------------------------------------------------- 1 | # Felt 2 | 3 | `felt252` is a fundamental data type in Cairo from which all other data types are derived. 4 | `felt252` can also be used to store 'short string representations' with a maximum length of 31 characters. 5 | 6 | For example: 7 | 8 | ```cairo 9 | // [!include ~/listings/cairo_cheatsheet/src/felt_example.cairo:sheet] 10 | ``` 11 | -------------------------------------------------------------------------------- /pages/cairo_cheatsheet/if_let.md: -------------------------------------------------------------------------------- 1 | # `if let` 2 | 3 | A `if let` statement is a combination of an `if` statement and a `let` statement. It allows you to execute the block only if the pattern matches. It's a cleaner way to handle a `match` statement with only one pattern that you want to handle. 4 | 5 | ```cairo 6 | // [!include ~/listings/cairo_cheatsheet/src/if_let_example.cairo:sheet] 7 | ``` 8 | 9 | ### See also 10 | 11 | [while let](/cairo_cheatsheet/while_let) 12 | 13 | -------------------------------------------------------------------------------- /pages/cairo_cheatsheet/loop.md: -------------------------------------------------------------------------------- 1 | # `loop` 2 | 3 | A `loop` specifies a block of code that will run repetitively until a halting condition is encountered. 4 | 5 | For example: 6 | 7 | ```cairo 8 | // [!include ~/listings/cairo_cheatsheet/src/loop_example.cairo:sheet] 9 | ``` 10 | 11 | ### See also 12 | 13 | [while](/cairo_cheatsheet/while) 14 | -------------------------------------------------------------------------------- /pages/cairo_cheatsheet/mapping.md: -------------------------------------------------------------------------------- 1 | # `Map` 2 | 3 | The `Map` type can be used to represent a collection of key-value. 4 | 5 | ```cairo 6 | // [!include ~/listings/cairo_cheatsheet/src/mapping_example.cairo] 7 | ``` 8 | -------------------------------------------------------------------------------- /pages/cairo_cheatsheet/match.md: -------------------------------------------------------------------------------- 1 | # Match 2 | 3 | The `match` expression in Cairo allows us to control the flow of our code by comparing a `felt252` data type or an enum against various patterns and then running specific code based on the pattern that matches. 4 | 5 | For example: 6 | 7 | ```cairo 8 | // [!include ~/listings/cairo_cheatsheet/src/match_example.cairo] 9 | ``` 10 | -------------------------------------------------------------------------------- /pages/cairo_cheatsheet/struct.md: -------------------------------------------------------------------------------- 1 | # Struct 2 | 3 | A struct is a data type similar to a tuple. Like tuples, they can be used to hold data of different types. 4 | 5 | For example: 6 | 7 | ```cairo 8 | // [!include ~/listings/cairo_cheatsheet/src/struct_example.cairo] 9 | ``` 10 | -------------------------------------------------------------------------------- /pages/cairo_cheatsheet/tuples.md: -------------------------------------------------------------------------------- 1 | # Tuples 2 | 3 | Tuples is a data type to group a fixed number of items of potentially different types into a single compound structure. Unlike arrays, tuples have a set length and can contain elements of varying types. Once a tuple is created, its size cannot change. 4 | 5 | For example: 6 | 7 | ```cairo 8 | // [!include ~/listings/cairo_cheatsheet/src/tuple_example.cairo:sheet] 9 | ``` 10 | -------------------------------------------------------------------------------- /pages/cairo_cheatsheet/type_casting.md: -------------------------------------------------------------------------------- 1 | # Type casting 2 | 3 | Cairo supports the conversion from one scalar type to another by using the `into` and `try_into` methods. 4 | The `into` method is used for conversion from a smaller data type to a larger data type, while `try_into` is used when converting from a larger to a smaller type that might not fit. 5 | 6 | For example: 7 | 8 | ```cairo 9 | // [!include ~/listings/cairo_cheatsheet/src/type_casting_example.cairo:sheet] 10 | ``` 11 | -------------------------------------------------------------------------------- /pages/cairo_cheatsheet/while.md: -------------------------------------------------------------------------------- 1 | # `while` 2 | 3 | A `while` loop allows you to specify a condition that must be true for the loop to continue. 4 | 5 | ```cairo 6 | // [!include ~/listings/cairo_cheatsheet/src/while_example.cairo:sheet] 7 | ``` 8 | 9 | ### See also 10 | 11 | - [loop](/cairo_cheatsheet/loop) 12 | - [while let](/cairo_cheatsheet/while_let) 13 | -------------------------------------------------------------------------------- /pages/cairo_cheatsheet/while_let.md: -------------------------------------------------------------------------------- 1 | # `while let` 2 | 3 | A `while let` loop is a combination of a `while` loop and a `let` statement. It allows you to execute the loop body only if the pattern matches. 4 | 5 | ```cairo 6 | // [!include ~/listings/cairo_cheatsheet/src/while_let_example.cairo:sheet] 7 | ``` 8 | 9 | ### See also 10 | 11 | - [while](/cairo_cheatsheet/while) 12 | - [if let](/cairo_cheatsheet/if_let) 13 | -------------------------------------------------------------------------------- /pages/components/collisions.md: -------------------------------------------------------------------------------- 1 | # Component-Contract Storage Collisions 2 | 3 | Components can declare their own storage variables. 4 | 5 | When a contract uses a component, the component storage is merged with the contract storage. 6 | The storage layout is only determined by the variables names, so variables with the same name will collide. 7 | 8 | :::note 9 | In a future release, the `#[substorage(v1)]` will determine the storage layout based on the component as well, so collisions will be avoided. 10 | ::: 11 | 12 | A good practice is to prefix the component storage variables with the component name, as shown in the [Switchable component example](/components/how_to). 13 | 14 | #### Example 15 | 16 | Here's an example of a collision on the `switchable_value` storage variable of the `Switchable` component. 17 | 18 | Interface: 19 | 20 | ```cairo 21 | // [!include ~/listings/applications/components/src/others/switch_collision.cairo:interface] 22 | ``` 23 | 24 | Here's the storage of the contract: 25 | 26 | ```cairo 27 | // [!include ~/listings/applications/components/src/others/switch_collision.cairo:storage] 28 | ``` 29 | 30 | Both the contract and the component have a `switchable_value` storage variable, so they collide: 31 | 32 | ```cairo 33 | // [!include ~/listings/applications/components/src/others/switch_collision.cairo:collision] 34 | ``` 35 | -------------------------------------------------------------------------------- /pages/components/ownable.md: -------------------------------------------------------------------------------- 1 | # Ownable 2 | 3 | The following `Ownable` component is a simple component that allows the contract to set an owner and provides an `_assert_is_owner` function that can be used to ensure that the caller is the owner. 4 | 5 | It can also be used to renounce ownership of a contract, meaning that no one will be able to satisfy the `_assert_is_owner` function. 6 | 7 | ```cairo 8 | // [!include ~/listings/applications/components/src/ownable.cairo:component] 9 | ``` 10 | 11 | A mock contract that uses the `Ownable` component: 12 | 13 | ```cairo 14 | // [!include ~/listings/applications/components/src/ownable.cairo:contract] 15 | ``` 16 | -------------------------------------------------------------------------------- /pages/getting-started/basics/constructor.md: -------------------------------------------------------------------------------- 1 | # Constructor 2 | 3 | A constructor is a special function that initializes a contract's state during deployment. It has several key characteristics: 4 | 5 | - Runs exactly once when the contract is deployed 6 | - Must be annotated with `#[constructor]` 7 | - Up to one constructor per contract 8 | - Function is conventionally named `constructor` 9 | 10 | Here's an example that shows how to initialize storage variables during contract deployment: 11 | 12 | ```cairo 13 | // [!include ~/listings/getting-started/constructor/src/constructor.cairo:contract] 14 | ``` 15 | 16 | In this example: 17 | 18 | - The constructor takes three parameters: `a`, `b`, and `c` 19 | - Each parameter corresponds to a storage variable of the same name, but you can specify any argument variable name 20 | - The values are written to storage using the `write()` method. You need to import the `StoragePointerWriteAccess` trait to be able to write to a specific storage pointer 21 | 22 | :::note 23 | **Best Practice** 24 | 25 | Constructors are ideal for: 26 | 27 | - Setting initial contract state 28 | - Storing deployment-time parameters 29 | - Initializing access control (e.g., setting an owner) 30 | 31 | ::: 32 | 33 | :::warning 34 | **Constructor values cannot be changed after deployment unless you specifically implement functions to modify them.** 35 | ::: 36 | -------------------------------------------------------------------------------- /pages/getting-started/basics/counter.md: -------------------------------------------------------------------------------- 1 | # Simple Counter 2 | 3 | We now understand how to create a contract with state variables and functions. Let's create a simple counter contract that increments and decrements a counter. 4 | 5 | Here's how it works: 6 | 7 | - The contract has a state variable `counter` that is initialized to `0`. 8 | 9 | - When a user calls the `increment` entrypoint, the contract increments `counter` by `1`. 10 | 11 | - When a user calls the `decrement`, the contract decrements `counter` by `1`. 12 | 13 | ```cairo 14 | // [!include ~/listings/getting-started/counter/src/counter.cairo:contract] 15 | ``` 16 | -------------------------------------------------------------------------------- /pages/getting-started/basics/custom-types-in-entrypoints.md: -------------------------------------------------------------------------------- 1 | # Custom Types in Entrypoints 2 | 3 | When using custom types in Starknet contract entrypoints, you need to handle serialization and deserialization of data. This is because: 4 | 5 | 1. Input parameters are sent to entrypoints as arrays of `felt252` 6 | 2. Return values must be converted back to arrays of `felt252` 7 | 3. Custom types need to be converted between these formats automatically 8 | 9 | ## Using the Serde Trait 10 | 11 | The `Serde` trait provides the necessary serialization and deserialization capabilities for your custom types. For most simple types, you can derive this trait automatically: 12 | 13 | ```cairo 14 | // [!include ~/listings/getting-started/custom_type_entrypoints/src/contract.cairo:contract] 15 | ``` 16 | 17 | :::note 18 | For some complex types, you might need to implement the `Serde` trait manually. This gives you control over how your type is serialized and deserialized. 19 | 20 | The `Serde` trait is distinct from the `Store` trait - `Serde` is for passing data in and out of entrypoints, while `Store` is for persisting data in contract storage. 21 | ::: 22 | -------------------------------------------------------------------------------- /pages/getting-started/basics/documentation.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | It's important to take the time to document your code. It will help developers and users to understand the contract and its functionalities. 4 | 5 | In Cairo, you can add comments with `//`. 6 | 7 | ### Best Practices 8 | 9 | Since Cairo 1, the community has adopted a [Rust-like documentation style](https://doc.rust-lang.org/rust-by-example/meta/doc.html). 10 | 11 | ### Contract Interface 12 | 13 | In smart contracts, you will often have a trait that defines the contract's interface (with `#[starknet::interface]`). 14 | This is the perfect place to include detailed documentation explaining the purpose and functionality of the contract entry points. You can follow this template: 15 | 16 | ```cairo 17 | #[starknet::interface] 18 | trait IContract { 19 | /// High-level description of the function 20 | /// 21 | /// # Arguments 22 | /// 23 | /// * `arg_1` - Description of the argument 24 | /// * `arg_n` - ... 25 | /// 26 | /// # Returns 27 | /// 28 | /// High-level description of the return value 29 | fn do_something(ref self: TContractState, arg_1: T_arg_1) -> T_return; 30 | } 31 | ``` 32 | 33 | Keep in mind that this should not describe the implementation details of the function, but rather the high-level purpose and functionality of the contract from the perspective of a user. 34 | 35 | ### Implementation Details 36 | 37 | When writing the contract logic, you can add comments to describe the technical implementation details of the functions. 38 | 39 | Avoid over-commenting: Comments should provide additional value and clarity. 40 | -------------------------------------------------------------------------------- /pages/getting-started/basics/errors.md: -------------------------------------------------------------------------------- 1 | # Error Handling in Cairo 2 | 3 | Cairo provides robust error handling mechanisms for smart contracts. When an error occurs during contract execution, the transaction is immediately reverted and all state changes are undone. 4 | 5 | ## Basic Error Functions 6 | 7 | Cairo offers two main functions for error handling: 8 | 9 | ### 1. `assert` 10 | 11 | - Used for condition validation (similar to Solidity's `require`) 12 | - Stops execution if the condition is false 13 | - Supports two formats: 14 | 15 | ```cairo 16 | assert(condition, 'error message'); // Basic assertion 17 | assert!(condition, "formatted error: {}", x); // Formatted string error 18 | ``` 19 | 20 | ### 2. `panic` 21 | 22 | - Used for immediate execution halt (similar to Solidity's `revert`) 23 | - Best for complex conditions or internal errors 24 | - Supports multiple formats: 25 | 26 | ```cairo 27 | panic_with_felt252('error message'); // Basic panic 28 | panic!("formatted error: value={}", value); // Formatted string error 29 | ``` 30 | 31 | :::warning 32 | While Cairo provides assertion macros like `assert_eq!` and `assert_ne!`, these are **only for testing**. In contract code, always use the standard `assert` function. 33 | ::: 34 | 35 | ## Simple Example 36 | 37 | Here's a basic example demonstrating both error handling approaches: 38 | 39 | ```cairo 40 | // [!include ~/listings/getting-started/errors/src/simple_errors.cairo:contract] 41 | ``` 42 | 43 | ## Custom Error Codes 44 | 45 | For better organization and consistency, you can define error messages in a dedicated module: 46 | 47 | ```cairo 48 | // [!include ~/listings/getting-started/errors/src/custom_errors.cairo:contract] 49 | ``` 50 | 51 | ## Real-World Example: Vault Contract 52 | 53 | Here's a practical example showing error handling in a vault contract that manages deposits and withdrawals: 54 | 55 | ```cairo 56 | // [!include ~/listings/getting-started/errors/src/vault_errors.cairo:contract] 57 | ``` 58 | 59 | In this example: 60 | 61 | 1. Custom errors are defined in a separate module 62 | 2. The `withdraw` function demonstrates both `assert` and `panic` approaches 63 | 3. Balance checks protect against underflow conditions 64 | -------------------------------------------------------------------------------- /pages/getting-started/basics/events.md: -------------------------------------------------------------------------------- 1 | # Events 2 | 3 | Events in Cairo smart contracts allow you to emit and record data on the Starknet blockchain. They are essential for tracking important state changes and providing transparency to users and other contracts. Events are also useful when building interfaces, to be notified about important state changes. 4 | 5 | To use events in your contract: 6 | 7 | 1. Create event structs that derive the `starknet::Event` trait 8 | 2. Define an `Event` enum in the contract, annotated with `#[event]`, where each variant is linked to an event struct 9 | 3. Emit events with the `emit` function 10 | 11 | You can make events searchable by adding the `#[key]` attribute to specific fields, which indexes them for efficient querying later. 12 | 13 | Events variant names and structs are recommended to be named consistently, even if it create some redundancy when emitting events. 14 | 15 | Here's a practical example of a contract that emits events when incrementing a counter: 16 | 17 | ```cairo 18 | // [!include ~/listings/getting-started/events/src/counter.cairo:contract] 19 | ``` 20 | 21 | :::note 22 | 23 | For better code organization, especially in larger contracts, you can define event structs outside of the contract module, as shown in the example here. 24 | While this allows you to group related events in separate modules or files, remember that you must still include all event variants in the contract's `Event` enum. 25 | 26 | ::: 27 | -------------------------------------------------------------------------------- /pages/getting-started/basics/mappings.md: -------------------------------------------------------------------------------- 1 | # Mappings 2 | 3 | Maps are a fundamental key-value data structure in Cairo smart contracts that allow you to store and retrieve values using unique keys. The `Map` type in `starknet::storage` is specifically designed for contract storage for this purpose. 4 | 5 | Here's a simple example that demonstrates how to use a `Map`: 6 | 7 | ```cairo 8 | // [!include ~/listings/getting-started/mappings/src/mappings.cairo:contract] 9 | ``` 10 | 11 | Let's break down the key components: 12 | 13 | - **Declaration**: Maps are declared using `Map` syntax 14 | - **Storage**: Maps must be declared inside the contract's `Storage` struct 15 | - You need to import the `StorageMapReadAccess` and `StorageMapWriteAccess` traits from `starknet::storage` 16 | - **Operations**: 17 | - `write(key, value)`: Stores a value for a given key 18 | - `read(key)`: Retrieves the value associated with a key 19 | - Maps automatically initialize all values to zero 20 | - Keys and values must be of valid storage types, see [Storing Custom Types](/getting-started/basics/storing_custom_types) 21 | 22 | ### Composite Keys 23 | 24 | For more complex scenarios, you can use composite keys by combining multiple values: 25 | 26 | ```cairo 27 | // Example: ERC20 allowance mapping 28 | Map<(ContractAddress, ContractAddress), felt252> // (owner, spender) -> amount 29 | ``` 30 | 31 | ### Storage Layout (advanced) 32 | 33 | Under the hood, Cairo maps use a deterministic storage layout: 34 | 35 | - Each key-value pair is stored at a unique address calculated using Pedersen hashes 36 | - The address formula is: $\text{h}(...\text{h}(\text{h}(\text{sn\_keccak}(name),k_1),k_2),...,k_n)$ mod $2^{251} - 256$ 37 | - Learn more in the [Starknet Documentation](https://docs.starknet.io/architecture-and-concepts/smart-contracts/contract-storage/#storage_variables) 38 | -------------------------------------------------------------------------------- /pages/getting-started/basics/storing_custom_types.md: -------------------------------------------------------------------------------- 1 | # Storing Custom Types 2 | 3 | In Starknet contracts, storing custom types in contract storage requires implementing the `Store` trait. While native types (like `felt252`, `u128`, etc.) can be stored directly, custom types need this additional step to generate the necessary implementation on how to handle their storage. 4 | 5 | To make a custom type storable: 6 | 7 | 1. Derive the `starknet::Store` trait for your struct 8 | 2. Add any other necessary traits like `Drop`, `Serde`, and `Copy` 9 | 3. Define your storage variables using the custom type 10 | 11 | Here's an example showing how to store a custom `Person` struct: 12 | 13 | ```cairo 14 | // [!include ~/listings/getting-started/storing_custom_types/src/contract.cairo:contract] 15 | ``` 16 | 17 | :::note 18 | 19 | For more complex types, you might need to implement the `Store` trait manually instead of deriving it. 20 | 21 | ::: 22 | 23 | ## Accessing Struct Members 24 | 25 | When you derive the `Store` trait, Cairo automatically generates the necessary storage pointers for each struct member. This allows you to access and modify individual fields of your stored struct directly: 26 | 27 | ```cairo 28 | // [!include ~/listings/getting-started/storing_custom_types/src/contract.cairo:set_name] 29 | ``` 30 | -------------------------------------------------------------------------------- /pages/getting-started/basics/variables.md: -------------------------------------------------------------------------------- 1 | # Variables 2 | 3 | Cairo contracts support three types of variables, each serving a different purpose: 4 | 5 | 1. **Local Variables** 6 | - Temporary variables within functions 7 | - Exist only during function execution 8 | - Not stored on the blockchain 9 | 10 | 2. **Storage Variables** 11 | - Defined in the contract's [Storage](/getting-started/basics/storage) 12 | - Persist between contract executions 13 | - Stored on the blockchain 14 | 15 | 3. **Global Variables** 16 | - Provide blockchain context and information 17 | - Accessible anywhere in the contract 18 | - Read-only system variables 19 | 20 | ## Local Variables 21 | 22 | Local variables are temporary variables that exist only within their defined scope (a function or code block). Key characteristics: 23 | 24 | - Stored in memory, not on the blockchain 25 | - Used for intermediate calculations and temporary data 26 | - Available only during function execution 27 | - Help improve code readability by naming values 28 | 29 | Here's an example demonstrating local variable scope: 30 | 31 | ```cairo 32 | // [!include ~/listings/getting-started/variables/src/local_variables.cairo:contract] 33 | ``` 34 | 35 | ## Storage Variables 36 | 37 | Storage variables provide persistent state for your contract on the blockchain. They have these properties: 38 | 39 | - Persist between contract executions 40 | - Can be read for free (no transaction needed) 41 | - Require a transaction to write to them 42 | - Must be defined in the contract's Storage struct 43 | 44 | Here's an example showing storage variable usage: 45 | 46 | ```cairo 47 | // [!include ~/listings/getting-started/variables/src/storage_variables.cairo:contract] 48 | ``` 49 | 50 | :::note 51 | **Storage Access** 52 | 53 | - Reading: Free operation, no transaction needed 54 | - Writing: Requires a transaction and costs gas 55 | 56 | ::: 57 | 58 | ## Global Variables 59 | 60 | Global variables provide access to blockchain context and system information. In Starknet: 61 | 62 | - Accessed through core library functions 63 | - Available anywhere in the contract 64 | - Provide critical blockchain context (e.g., caller address, block info) 65 | 66 | Example using global variables: 67 | 68 | ```cairo 69 | // [!include ~/listings/getting-started/variables/src/global_variables.cairo:contract] 70 | ``` 71 | -------------------------------------------------------------------------------- /pages/getting-started/interacting/calling_other_contracts.md: -------------------------------------------------------------------------------- 1 | # Calling Other Contracts 2 | 3 | In Starknet, contracts can interact with each other through contract calls. The recommended way to make these calls is using the dispatcher pattern, which provides type safety and better error handling. 4 | 5 | ## Understanding Dispatchers 6 | 7 | A dispatcher is an automatically generated struct that handles the serialization and deserialization of contract calls. To use dispatchers: 8 | 9 | 1. Define the target contract's interface as a trait with `#[starknet::interface]` (`IContract`) 10 | 2. Import the generated dispatcher types (`IContractDispatcher` and `IContractDispatcherTrait`) 11 | 3. Create a dispatcher instance with the target contract's address 12 | 13 | Let's look at a practical example where one contract (`Caller`) interacts with another (`Callee`). The `Callee` contract stores a value that can be set and retrieved: 14 | 15 | ```cairo 16 | // [!include ~/listings/getting-started/calling_other_contracts/src/caller.cairo:callee_contract] 17 | ``` 18 | 19 | The `Caller` contract demonstrates how to use the dispatcher to interact with `Callee`: 20 | 21 | ```cairo 22 | // [!include ~/listings/getting-started/calling_other_contracts/src/caller.cairo:caller_contract] 23 | ``` 24 | 25 | ### Key Points: 26 | 27 | - The `#[starknet::interface]` attribute automatically generates the dispatcher types 28 | - Dispatchers handle all the low-level details of contract interaction 29 | - Contract calls are type-safe and checked at compile time 30 | - Each contract maintains its own storage and state 31 | 32 | For more details about dispatchers, check out the [Cairo Book](https://book.cairo-lang.org/ch102-02-interacting-with-another-contract.html). 33 | 34 | :::note 35 | While you can use the low-level `call_contract_syscall` directly, it's not recommended as it: 36 | 37 | - Requires manual serialization/deserialization 38 | - Lacks compile-time type checking 39 | - Is more easy to make mistakes 40 | 41 | ::: 42 | -------------------------------------------------------------------------------- /pages/getting-started/interacting/factory.md: -------------------------------------------------------------------------------- 1 | # Factory Pattern 2 | 3 | The factory pattern is a well known pattern in object oriented programming. It provides an abstraction on how to instantiate a class. 4 | 5 | In the case of smart contracts, we can use this pattern by defining a factory contract that has the sole responsibility of creating and managing other contracts. 6 | 7 | ## Class hash and contract instance 8 | 9 | In Starknet, there's a separation between contract's classes and instances. A contract class serves as a blueprint, defined by the underlying Cairo bytecode, contract's entrypoints, ABI and Sierra program hash. The contract class is identified by a class hash. When you want to add a new class to the network, you first need to declare it. 10 | 11 | When deploying a contract, you need to specify the class hash of the contract you want to deploy. Each instance of a contract has their own storage regardless of the class hash. 12 | 13 | Using the factory pattern, we can deploy multiple instances of the same contract class and handle upgrades easily. 14 | 15 | ## Minimal example 16 | 17 | Here's a minimal example of a factory contract that deploys the `SimpleCounter` contract: 18 | 19 | ```cairo 20 | // [!include ~/listings/getting-started/factory/src/simple_factory.cairo:contract] 21 | ``` 22 | 23 | This factory can be used to deploy multiple instances of the `SimpleCounter` contract by calling the `create_counter` and `create_counter_at` functions. 24 | 25 | The `SimpleCounter` class hash is stored inside the factory, and can be upgraded with the `update_counter_class_hash` function which allows to reuse the same factory contract when the `SimpleCounter` contract is upgraded. 26 | 27 | :::note 28 | This minimal example lacks several useful features such as access control, tracking of deployed contracts, events etc. 29 | ::: 30 | 31 | 32 | -------------------------------------------------------------------------------- /pages/getting-started/testing/index.mdx: -------------------------------------------------------------------------------- 1 | # Testing Contracts 2 | 3 | Testing plays a crucial role in software development, especially for smart contracts. In this section, we'll guide you through the basics of creating test suites for your contracts. 4 | 5 | There's two main *test runners* for Cairo: 6 | 7 |
8 | | Test Runner | Scope | Best For | 9 | |----------------------------------------------------|---------------------|-------------------------------| 10 | | **[Snforge](/getting-started/testing/testing-snforge)** | Starknet contracts | Full smart contract testing with blockchain state | 11 | | **[Cairo Test](/getting-started/testing/testing-cairo-test)** | Pure Cairo packages | Non-blockchain-state logic validation / testing | 12 |
13 | 14 | :::warning[Recommendation] 15 | Start with **Snforge** for Starknet contracts! 16 | 17 | When you project is getting more complex, try to separate the logic into pure Cairo packages. 18 | You can then use **Cairo Test** to test these packages independently. 19 | ::: -------------------------------------------------------------------------------- /pages/getting-started/testing/testing-cairo-test.md: -------------------------------------------------------------------------------- 1 | # Testing with Cairo-test 2 | 3 | :::warning 4 | Starknet Foundry provides a more comprehensive testing runner with Snforge, specifically designed for Starknet smart contracts. 5 | It is highly recommended to use it instead of Cairo-test, see [Testing with Snforge](/getting-started/testing/testing-snforge). 6 | ::: 7 | 8 | Cairo-test is the included testing framework from Cairo, and can be run with `scarb test{:bash}` (or `scarb cairo-test{:bash}`). 9 | You need to add it as a dev dependency with the following line in your `Scarb.toml`: 10 | ```toml 11 | [dev-dependencies] 12 | cairo_test = "2.10.1" // Version should be same as your Starknet/Scarb version 13 | ``` 14 | 15 | Testing is done similarly as shown in the [Testing with Snforge](/getting-started/testing/testing-snforge) section, but all the snforge specific features are not available. 16 | 17 | You can learn more about using Cairo-test in the [Cairo Book - Testing Cairo Programs](https://book.cairo-lang.org/ch10-00-testing-cairo-programs.html#testing-cairo-programs). 18 | -------------------------------------------------------------------------------- /patches/vocs.patch: -------------------------------------------------------------------------------- 1 | diff --git a/_lib/vite/plugins/rehype/inline-shiki.js b/_lib/vite/plugins/rehype/inline-shiki.js 2 | index 1e2a7426a5a80afa1bdff24388a8849b49e04f2e..a8394cddd3968f6c153eed58481a80fb5a8e1b09 100644 3 | --- a/_lib/vite/plugins/rehype/inline-shiki.js 4 | +++ b/_lib/vite/plugins/rehype/inline-shiki.js 5 | @@ -13,16 +13,33 @@ export const rehypeInlineShiki = function (options = {}) { 6 | }); 7 | const highlighter = await promise; 8 | return visit(tree, 'element', (node, index, parent) => { 9 | - if (node.tagName !== 'code') 10 | - return; 11 | + if (node.tagName !== "code") return; 12 | + // ignore math 13 | + const classes = Array.isArray(node.properties.className) 14 | + ? node.properties.className 15 | + : []; 16 | + const languageMath = classes.includes("language-math"); 17 | + const mathDisplay = classes.includes("math-display"); 18 | + const mathInline = classes.includes("math-inline"); 19 | + if (languageMath || mathDisplay || mathInline) { 20 | + return; 21 | + } 22 | + 23 | const match = node.children[0]?.value?.match(inlineShikiRegex); 24 | - if (!match) 25 | - return; 26 | - const [, code, lang] = match; 27 | - const hast = highlighter.codeToHast(code, { ...options, lang }); 28 | + let hast; 29 | + if (match) { 30 | + const [, code, lang] = match; 31 | + hast = highlighter.codeToHast(code, { ...options, lang }); 32 | + } else { 33 | + if (!node.children[0] || node.children[0].type !== "text") return; 34 | + const code = node.children[0].value; 35 | + hast = highlighter.codeToHast(code, { 36 | + ...options, 37 | + lang: "cairo", 38 | + }); 39 | + } 40 | const inlineCode = hast.children[0].children[0]; 41 | - if (!inlineCode) 42 | - return; 43 | + if (!inlineCode) return; 44 | parent?.children.splice(index ?? 0, 1, inlineCode); 45 | }); 46 | }; 47 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /public/merkle_root.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NethermindEth/StarknetByExample/a2bb6e0c68a27a377b02d7ae7c36846e9349e982/public/merkle_root.png -------------------------------------------------------------------------------- /scripts/test_resolver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -f $(which scarb) ]; then 4 | echo -e "${RED}No scarb executable found!${NC}" 5 | exit 1 6 | fi 7 | 8 | if [ ! -f $(which snforge) ]; then 9 | echo -e "${RED}No snforge executable found!${NC}" 10 | exit 1 11 | fi 12 | 13 | if [ ! -f Scarb.toml ]; then 14 | echo -e "${RED}No Scarb.toml file found!${NC}" 15 | exit 1 16 | fi 17 | 18 | if [ -z "$(grep -E 'snforge_std' Scarb.toml)" ]; then 19 | scarb cairo-test 20 | else 21 | snforge test 22 | fi -------------------------------------------------------------------------------- /scripts/test_runner.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # root directory of the repository 4 | REPO_ROOT="$(git rev-parse --show-toplevel)" 5 | 6 | list_all_listings() { 7 | ls -d listings/ch*/* | grep -E 'listings/ch.*/*' | cut -d '/' -f 2-3 8 | } 9 | 10 | # function to process listing 11 | process_listing() { 12 | local listing="$1" 13 | local dir_path="${REPO_ROOT}/listings/${listing}" 14 | 15 | if ! cd "$dir_path"; then 16 | echo -e "${RED}Failed to change to directory: $dir_path ${NC}" 17 | return 18 | fi 19 | 20 | $REPO_ROOT/scripts/test_resolver.sh 21 | } 22 | 23 | for listing in $(list_all_listings); do 24 | process_listing "$listing" 25 | done 26 | 27 | wait -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: [ 4 | "./components/**/*.{js,ts,jsx,tsx}", 5 | "./pages/**/*.{md,mdx,js,ts,jsx,tsx}", 6 | "./footer.tsx", 7 | "./layout.tsx", 8 | ], 9 | theme: { 10 | extend: { 11 | colors: { 12 | primary: { 13 | light: "#ff4b01", 14 | dark: "#F69D50", 15 | }, 16 | secondary: "#1b1b50", 17 | }, 18 | }, 19 | }, 20 | plugins: [], 21 | }; 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["**/*.ts", "**/*.tsx"] 24 | } 25 | --------------------------------------------------------------------------------