diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0a9921611..ef91ff089 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,9 +3,11 @@ name: Lint and test on: pull_request: branches: + - main - cairo-2 push: branches: + - main - cairo-2 jobs: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 49e9b88d0..8adb5ee4b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,69 +6,11 @@ We really appreciate and value contributions to OpenZeppelin Contracts for Cairo Before starting development, please [create an issue](https://github.com/OpenZeppelin/cairo-contracts/issues/new/choose) to open the discussion, validate that the PR is wanted, and coordinate overall implementation details. -Also, consider that snake case is used for Cairo development in general due to its strong Python bias. -This project follows our [Extensibility pattern](https://docs.openzeppelin.com/contracts-cairo/extensibility), camelCasing all exposed function names and their parameters: - -```cairo -@external -func exposedFunc(paramOne, paramTwo){ -} -``` - -All internal and otherwise unexposed functions should resort to snake_case: - -```cairo -func internal_func(param_one, param_two){ -} -``` - -Compare our preset contracts with the libraries from which they're derived such as the [ERC20 preset](src/token/erc20/presets/ERC20.cairo) and [ERC20 library](src/token/erc20/presets/ERC20.cairo) for full examples. -See [Function names and coding style](https://docs.openzeppelin.com/contracts-cairo/0.4.0/extensibility#function_names_and_coding_style) for more information. - -And make sure to always include tests and documentation for the new developments. Please consider the following conventions: - -- Naming - - Libraries should be named `library.cairo`, e.g. `erc20/library.cairo` - - Contracts should be PascalCased i.e. `MyContract.cairo` - - Interfaces should be prefixed with an `I`, as in `IAccount.cairo` - - Test modules should begin with `test_` followed by the contract name i.e. `test_MyContract.py` - -- Structure - - Libraries should cede their names to their parent directory and are named `library.cairo` instead - - Interfaces should be alongside the library that the interface defines - - Preset contracts should be within a `presets` directory of the library to which they are a preset - - Here are example paths: - - `openzeppelin.token.erc20.library` - - `openzeppelin.token.erc20.IERC20` - - `openzeppelin.token.erc20.presets.ERC20Mintable` - - And a visual guide: - -```python - openzeppelin - └──token - └── erc20 - ├── library.cairo - ├── IERC20.cairo - └── presets - └── ERC20Mintable.cairo -``` - -- Preset contract testing - - Though, inheritance is not possible in Cairo, this repo utilizes inheritance for testing. This proves useful for testing multiple contracts that stem from the same base library. For example, the preset contracts [ERC20Mintable](src/token/erc20/presets/ERC20Mintable.cairo) and [ERC20Burnable](src/token/erc20/presets/ERC20Burnable.cairo) both share the base ERC20 functionality. To reduce code repetition, we follow these guidelines: - - `BaseSuites` - - module names are not prefixed with `test_` - - set base tests inside a class - - class name should not be prefixed with `Test`; otherwise, these tests run twice - - - test modules - - define the base fixture (`contract_factory`) and any other fixtures not used in the base suite i.e. `erc721_minted` - - define the test class and inherit the base class i.e. `class TestERC20(OwnableBase)` - - add tests specific to the preset flavor within the test class - - - fixtures - - are not defined in the base suite but are passed, unpacked, and used - - are defined in the tests where they are used - - for modularity, the basic contract factory fixture is always called `contract_factory` +### Coding style + +After a few radical changes in the Cairo language (mainly the transition to Cairo 1), our coding style guidelines became automatically deprecated. +That's why [we're working on setting new ones](https://github.com/OpenZeppelin/cairo-contracts/issues/696). +Feel free to read, contribute, discuss, and ask questions in the issue. ## Creating Pull Requests (PRs) @@ -98,32 +40,29 @@ As a contributor, you are expected to fork this repository, work on your own for 3. Make your changes, add your files, update documentation ([see Documentation section](#documentation)), commit, and push to your fork. ```sh - git add SomeFile.js + git add src/file.cairo git commit "Fix some bug short description #123" git push origin fix/some-bug-short-description-#123 ``` -4. Run tests, linter, etc. This can be done by running local continuous integration and make sure it passes. We recommend to use a [python virtual environment](https://docs.python.org/3/tutorial/venv.html). +4. Run tests and linter. This can be done by running local continuous integration and make sure it passes. ```bash - # install tox from testing dependencies - pip install .[testing] # '.[testing]' in zsh - # run tests - tox + scarb test - # stop the build if there are Markdown documentation errors - tox -e lint + # run linter + scarb fmt --check ``` -5. Go to [github.com/OpenZeppelin/cairo-contracts](https://github.com/OpenZeppelin/cairo-contracts) in your web browser and issue a new pull request. +5. Go to [OpenZeppelin/cairo-contracts](https://github.com/OpenZeppelin/cairo-contracts) in your web browser and issue a new pull request. Begin the body of the PR with "Fixes #123" or "Resolves #123" to link the PR to the issue that it is resolving. *IMPORTANT* Read the PR template very carefully and make sure to follow all the instructions. These instructions refer to some very important conditions that your PR must meet in order to be accepted, such as making sure that all PR checks pass. 6. Maintainers will review your code and possibly ask for changes before your code is pulled in to the main repository. We'll check that all tests pass, review the coding style, and check for general code correctness. If everything is OK, we'll merge your pull request and your code will be part of OpenZeppelin Contracts for Cairo. - *IMPORTANT* Please pay attention to the maintainer's feedback, since its a necessary step to keep up with the standards OpenZeppelin Contracts attains to. + *IMPORTANT* Please pay attention to the maintainer's feedback, since it's a necessary step to keep up with the standards OpenZeppelin Contracts attains to. ## Documentation @@ -145,15 +84,13 @@ If you want to run the documentation UI locally: ## Integration tests -Currently, [starknet's test suite](https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/starknet/testing/starknet.py) has important differences with public networks. Like [not checking signature hints toward the end of the tx flow](https://github.com/OpenZeppelin/cairo-contracts/issues/386). - -That's why we strongly suggest testing new features against a testnet before submitting the PR, to make sure that everything works as expected in a real environment. +Currently, Starknet's test suite has important differences with public networks. We strongly suggest testing new features against a testnet before submitting the PR, to make sure that everything works as expected in a real environment. We are looking into defining a better process for these integration tests, but for now the PR author/contributor must suggest an approach to test the feature when applicable, which has to be agreed and reproduced by the reviewer. ## All set -If you have any questions, feel free to post them to github.com/OpenZeppelin/cairo-contracts/issues. +If you have any questions, feel free to post them as an [issue](https://github.com/OpenZeppelin/cairo-contracts/issues). Finally, if you're looking to collaborate and want to find easy tasks to start, look at the issues we marked as ["Good first issue"](https://github.com/OpenZeppelin/cairo-contracts/labels/good%20first%20issue). diff --git a/README.md b/README.md index c5efd3044..3ac087332 100644 --- a/README.md +++ b/README.md @@ -1,117 +1,107 @@ # OpenZeppelin Contracts for Cairo -[![Tests and linter](https://github.com/OpenZeppelin/cairo-contracts/actions/workflows/coverage.yml/badge.svg)](https://github.com/OpenZeppelin/cairo-contracts/actions/workflows/coverage.yml) -[![codecov](https://codecov.io/github/OpenZeppelin/cairo-contracts/branch/main/graph/badge.svg?token=LFSZH8RPOL)](https://codecov.io/github/OpenZeppelin/cairo-contracts) +[![Lint and test](https://github.com/OpenZeppelin/cairo-contracts/actions/workflows/test.yml/badge.svg)](https://github.com/OpenZeppelin/cairo-contracts/actions/workflows/test.yml) -**A library for secure smart contract development** written in Cairo for [StarkNet](https://starkware.co/product/starknet/), a decentralized ZK Rollup. +**A library for secure smart contract development** written in Cairo for [Starknet](https://starkware.co/product/starknet/), a decentralized ZK Rollup. -## Usage - -> ## ⚠️ WARNING! ⚠️ -> +> **Warning** > This repo contains highly experimental code. -> Expect rapid iteration. +> It has no code coverage checks. +> It hasn't been audited. > **Use at your own risk.** -### First time? +## Usage -Before installing Cairo on your machine, you need to install `gmp`: +> **Warning** +> Expect rapid iteration. +> Some contracts or features are not ready to be deployed. +> Check the **Unsupported** section below. -```bash -sudo apt install -y libgmp3-dev # linux -brew install gmp # mac -``` +### Prepare the environment -> If you have any troubles installing gmp on your Apple M1 computer, [here’s a list of potential solutions](https://github.com/OpenZeppelin/nile/issues/22). +Simply [install Cairo and scarb](https://docs.swmansion.com/scarb/download). ### Set up your project -Create a directory for your project, then `cd` into it and create a Python virtual environment. +Create a new project and `cd` into it. ```bash -mkdir my-project -cd my-project -python3 -m venv env -source env/bin/activate +scarb new my_project && cd my_project ``` -Install the [Nile](https://github.com/OpenZeppelin/nile) development environment and then run `init` to kickstart a new project. Nile will create the project directory structure and install [the Cairo language](https://www.cairo-lang.org/docs/quickstart.html), a [local network](https://github.com/Shard-Labs/starknet-devnet/), and a [testing framework](https://docs.pytest.org/en/6.2.x/). +The contents of `my_project` should look like this: ```bash -pip install cairo-nile -nile init -``` - -### Install the library +$ ls -```bash -pip install openzeppelin-cairo-contracts +Scarb.toml src ``` -> ⚠️ Warning! ⚠️ -Installing directly the `main` branch may contain incomplete or breaking implementations, download [official releases](https://github.com/OpenZeppelin/cairo-contracts/releases/) only. - -### Use a basic preset +### Install the library -Presets are ready-to-use contracts that you can deploy right away. They also serve as examples of how to use library modules. [Read more about presets](https://docs.openzeppelin.com/contracts-cairo/0.6.1/extensibility#presets). +Edit `scarb.toml` and add: -```cairo -// contracts/MyToken.cairo - -%lang starknet - -from openzeppelin.token.erc20.presets.ERC20 import ( - constructor, - name, - symbol, - totalSupply, - decimals, - balanceOf, - allowance, - transfer, - transferFrom, - approve, - increaseAllowance, - decreaseAllowance -) +```toml +[dependencies] +openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.7.0" } ``` -Compile and deploy it right away: +Build the project to download it: ```bash -nile compile +$ scarb build -nile deploy MyToken --alias my_token +Updating git repository https://github.com/OpenZeppelin/cairo-contracts +Compiling my_project v0.1.0 (~/my_project/Scarb.toml) +Finished release target(s) in 6 seconds ``` -> Note that `` is expected to be two integers i.e. `1` `0`. See [Uint256](https://docs.openzeppelin.com/contracts-cairo/0.6.1/utilities#uint256) for more information. +### Using the library -### Write a custom contract using library modules +Open `src/lib.cairo` and write your contract. -[Read more about libraries](https://docs.openzeppelin.com/contracts-cairo/0.6.1/extensibility#libraries). +For example, this how to extend the ERC20 standard contract: ```cairo -%lang starknet - -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.uint256 import Uint256 -from openzeppelin.security.pausable.library import Pausable -from openzeppelin.token.erc20.library import ERC20 - -(...) - -@external -func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( - recipient: felt, amount: Uint256 -) -> (success: felt) { - Pausable.assert_not_paused(); - return ERC20.transfer(recipient, amount); +#[starknet::contract] +mod MyToken { + use starknet::ContractAddress; + use openzeppelin::token::erc20::ERC20; + + #[storage] + struct Storage {} + + #[constructor] + fn constructor( + ref self: ContractState, + initial_supply: u256, + recipient: ContractAddress + ) { + let name = 'MyToken'; + let symbol = 'MTK'; + + let mut unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::InternalImpl::initializer(ref unsafe_state, name, symbol); + ERC20::InternalImpl::_mint(ref unsafe_state, recipient, initial_supply); + } + + #[external(v0)] + fn name(self: @ContractState) -> felt252 { + let unsafe_state = ERC20::unsafe_new_contract_state(); + ERC20::ERC20Impl::name(@unsafe_state) + } + + ... } ``` +### Unsupported + +`DualCase` dispatchers rely on Sierra's ability to catch a revert to resume execution. Currently, Starknet live chains (testnets and mainnet) don't implement that behavior. Starknet's testing framework does support it. + ## Learn -### Documentation + ### Cairo -- [StarkNet official documentation](https://www.cairo-lang.org/docs/hello_starknet/index.html#hello-starknet) -- [Cairo language documentation](https://www.cairo-lang.org/docs/hello_cairo/index.html#hello-cairo) -- Perama's [Cairo by example](https://perama-v.github.io/cairo/by-example/) -- [Cairo 101 workshops](https://www.youtube.com/playlist?list=PLcIyXLwiPilV5RBZj43AX1FY4FJMWHFTY) +- [Cairo book](https://book.cairo-lang.org/) +- [Cairo language documentation](https://docs.cairo-lang.org/) +- [Starknet book](https://book.starknet.io/) +- [Starknet documentation](https://docs.starknet.io/documentation/) +- [Cairo 1.0 mini-docs](https://github.com/Starknet-Africa-Edu/Cairo1.0) +- [Cairopractice](https://cairopractice.com/) -### Nile +### Tooling -- [Getting started with StarkNet using Nile](https://medium.com/coinmonks/starknet-tutorial-for-beginners-using-nile-6af9c2270c15) -- [How to manage smart contract deployments with Nile](https://medium.com/@martriay/manage-your-starknet-deployments-with-nile-%EF%B8%8F-e849d40546dd) +- [Scarb](https://docs.swmansion.com/scarb) ## Development ### Set up the project -Clone the repository +Clone the repository: ```bash git clone git@github.com:OpenZeppelin/cairo-contracts.git ``` -`cd` into it and create a Python virtual environment: - -```bash -cd cairo-contracts -python3 -m venv env -source env/bin/activate -``` - -Install dependencies: +`cd` into it and build: ```bash -python -m pip install . -``` - -### Compile the contracts +$ cd cairo-contracts +$ scarb build -```bash -nile compile --directory src - -🤖 Compiling all Cairo contracts in the src directory -🔨 Compiling src/openzeppelin/token/erc20/library.cairo -🔨 Compiling src/openzeppelin/token/erc20/presets/ERC20Mintable.cairo -🔨 Compiling src/openzeppelin/token/erc20/presets/ERC20Pausable.cairo -🔨 Compiling src/openzeppelin/token/erc20/presets/ERC20Upgradeable.cairo -🔨 Compiling src/openzeppelin/token/erc20/presets/ERC20.cairo -🔨 Compiling src/openzeppelin/token/erc20/IERC20.cairo -🔨 Compiling src/openzeppelin/token/erc721/enumerable/library.cairo -🔨 Compiling src/openzeppelin/token/erc721/library.cairo -🔨 Compiling src/openzeppelin/token/erc721/utils/ERC721Holder.cairo -🔨 Compiling src/openzeppelin/token/erc721/presets/ERC721MintablePausable.cairo -🔨 Compiling src/openzeppelin/token/erc721/presets/ERC721MintableBurnable.cairo -🔨 Compiling src/openzeppelin/token/erc721/presets/ERC721EnumerableMintableBurnable.cairo -🔨 Compiling src/openzeppelin/token/erc721/IERC721.cairo -🔨 Compiling src/openzeppelin/token/erc721/IERC721Metadata.cairo -🔨 Compiling src/openzeppelin/token/erc721/IERC721Receiver.cairo -🔨 Compiling src/openzeppelin/token/erc721/enumerable/IERC721Enumerable.cairo -🔨 Compiling src/openzeppelin/access/ownable/library.cairo -🔨 Compiling src/openzeppelin/security/reentrancyguard/library.cairo -🔨 Compiling src/openzeppelin/security/safemath/library.cairo -🔨 Compiling src/openzeppelin/security/pausable/library.cairo -🔨 Compiling src/openzeppelin/security/initializable/library.cairo -🔨 Compiling src/openzeppelin/utils/constants/library.cairo -🔨 Compiling src/openzeppelin/introspection/erc165/library.cairo -🔨 Compiling src/openzeppelin/introspection/erc165/IERC165.cairo -🔨 Compiling src/openzeppelin/upgrades/library.cairo -🔨 Compiling src/openzeppelin/upgrades/presets/Proxy.cairo -🔨 Compiling src/openzeppelin/account/library.cairo -🔨 Compiling src/openzeppelin/account/presets/EthAccount.cairo -🔨 Compiling src/openzeppelin/account/presets/Account.cairo -🔨 Compiling src/openzeppelin/account/presets/AddressRegistry.cairo -🔨 Compiling src/openzeppelin/account/IAccount.cairo -✅ Done +Compiling lib(openzeppelin) openzeppelin v0.7.0 (~/cairo-contracts/Scarb.toml) +Compiling starknet-contract(openzeppelin) openzeppelin v0.7.0 (~/cairo-contracts/Scarb.toml) +Finished release target(s) in 16 seconds ``` ### Run tests -Run tests using [tox](https://tox.wiki/en/latest/), tox automatically creates an isolated testing environment: - -```bash -tox - -====================== test session starts ====================== -platform linux -- Python 3.7.2, pytest-7.1.2, py-1.11.0, pluggy-1.0.0 -rootdir: /home/readme/cairo-contracts, configfile: tox.ini -plugins: asyncio-0.18.3, xdist-2.5.0, forked-1.4.0, web3-5.29.0, typeguard-2.13.3 -asyncio: mode=auto -gw0 [185] / gw1 [185] -...................................................................................... -...................................................................................... -............ [100%] -``` - -### Run Tests in Docker - -For M1 users or those who are having trouble with library/python versions you can alternatively run the tests within a docker container. Using the following as a Dockerfile placed in the root directory of the project: - -```dockerfile -FROM python:3.7 - -RUN pip install tox -RUN mkdir cairo-contracts -COPY . cairo-contracts -WORKDIR cairo-contracts -ENTRYPOINT tox -``` - -After its placed there run: - ```bash -docker build -t cairo-tests . -docker run cairo-tests -``` - -### Parallel Testing - -This repo utilizes the [pytest-xdist](https://pytest-xdist.readthedocs.io/en/latest/) plugin which runs tests in parallel. This feature increases testing speed; however, conflicts with a shared state can occur since tests do not run in order. To overcome this, independent cached versions of contracts being tested should be provisioned to each test case. Here's a simple fixture example: - -```python -from utils import get_contract_class, cached_contract - -@pytest.fixture -def foo_factory(): - # get contract class - foo_cls = get_contract_class('Foo') - - # deploy contract - starknet = await Starknet.empty() - foo = await starknet.deploy(contract_class=foo_cls) - - # copy the state and cache contract - state = starknet.state.copy() - cached_foo = cached_contract(state, foo_cls, foo) - - return cached_foo +scarb test ``` -See [Memoization](https://docs.openzeppelin.com/contracts-cairo/0.6.1/utilities#memoization) in the Utilities documentation for a more thorough example on caching contracts. - -> Note that this does not apply for stateless libraries such as SafeMath. - ## Security > ⚠️ Warning! ⚠️ @@ -277,24 +165,6 @@ Refer to [SECURITY.md](SECURITY.md) for more details. OpenZeppelin Contracts for Cairo exists thanks to its contributors. There are many ways you can participate and help build high quality software. Check out the [contribution](CONTRIBUTING.md) guide! -### Markdown linter - -To keep the markdown files neat and easy to edit, we utilize DavidAnson's [markdownlint](https://github.com/DavidAnson/markdownlint) linter. You can find the listed rules [here](https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md). Note that the following rules are disabled: - -- `MD013: line length` - - - to enable paragraphs without internal line breaks - -- `MD033: inline HTML` - - - to enable .md files to have duplicate headers and separate them by identifiers - -Before creating a PR, check that documentation changes are compliant with our markdown rules by running: - -```bash -tox -e lint -``` - ## License OpenZeppelin Contracts for Cairo is released under the [MIT License](LICENSE). diff --git a/Scarb.toml b/Scarb.toml index e36e29c60..bf42b24ee 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -1,6 +1,6 @@ [package] name = "openzeppelin" -version = "0.1.0" +version = "0.7.0" cairo-version = "2.1.0-rc1" authors = ["OpenZeppelin Community "] description = "OpenZeppelin Contracts written in Cairo for StarkNet, a decentralized ZK Rollup"