From 4b286287b5e5ff7bc494eb970b836c23030d5a4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Tue, 12 Jul 2022 21:26:28 -0300 Subject: [PATCH 01/39] migrate docs to antora --- docs/antora.yml | 5 + docs/modules/ROOT/nav.adoc | 15 +++ .../ROOT/pages/access.adoc} | 0 .../ROOT/pages/accounts.adoc} | 0 .../ROOT/pages/erc20.adoc} | 0 .../ROOT/pages/erc721.adoc} | 0 .../ROOT/pages/extensibility.adoc} | 0 docs/modules/ROOT/pages/index.adoc | 98 +++++++++++++++++++ .../ROOT/pages/introspection.adoc} | 0 .../ROOT/pages/proxies.adoc} | 0 .../ROOT/pages/security.adoc} | 0 .../ROOT/pages/utilities.adoc} | 0 12 files changed, 118 insertions(+) create mode 100644 docs/antora.yml create mode 100644 docs/modules/ROOT/nav.adoc rename docs/{Access.md => modules/ROOT/pages/access.adoc} (100%) rename docs/{Account.md => modules/ROOT/pages/accounts.adoc} (100%) rename docs/{ERC20.md => modules/ROOT/pages/erc20.adoc} (100%) rename docs/{ERC721.md => modules/ROOT/pages/erc721.adoc} (100%) rename docs/{Extensibility.md => modules/ROOT/pages/extensibility.adoc} (100%) create mode 100644 docs/modules/ROOT/pages/index.adoc rename docs/{Introspection.md => modules/ROOT/pages/introspection.adoc} (100%) rename docs/{Proxies.md => modules/ROOT/pages/proxies.adoc} (100%) rename docs/{Security.md => modules/ROOT/pages/security.adoc} (100%) rename docs/{Utilities.md => modules/ROOT/pages/utilities.adoc} (100%) diff --git a/docs/antora.yml b/docs/antora.yml new file mode 100644 index 000000000..575842e54 --- /dev/null +++ b/docs/antora.yml @@ -0,0 +1,5 @@ +name: cairo-contracts +title: Contracts for Cairo +version: 0.2 +nav: + - modules/ROOT/nav.adoc diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc new file mode 100644 index 000000000..d7fc52392 --- /dev/null +++ b/docs/modules/ROOT/nav.adoc @@ -0,0 +1,15 @@ +* xref:index.adoc[Overview] +// * xref:wizard.adoc[Wizard] +* xref:extensibility.adoc[Extensibility] +* xref:proxies.adoc[Proxies and Upgrades] + +* xref:accounts.adoc[Accounts] +* xref:access.adoc[Access Control] + +* Tokens +** xref:erc20.adoc[ERC20] +** xref:erc721.adoc[ERC721] + +* xref:security.adoc[Security] +* xref:introspection.adoc[Introspection] +* xref:utilities.adoc[Utilities] diff --git a/docs/Access.md b/docs/modules/ROOT/pages/access.adoc similarity index 100% rename from docs/Access.md rename to docs/modules/ROOT/pages/access.adoc diff --git a/docs/Account.md b/docs/modules/ROOT/pages/accounts.adoc similarity index 100% rename from docs/Account.md rename to docs/modules/ROOT/pages/accounts.adoc diff --git a/docs/ERC20.md b/docs/modules/ROOT/pages/erc20.adoc similarity index 100% rename from docs/ERC20.md rename to docs/modules/ROOT/pages/erc20.adoc diff --git a/docs/ERC721.md b/docs/modules/ROOT/pages/erc721.adoc similarity index 100% rename from docs/ERC721.md rename to docs/modules/ROOT/pages/erc721.adoc diff --git a/docs/Extensibility.md b/docs/modules/ROOT/pages/extensibility.adoc similarity index 100% rename from docs/Extensibility.md rename to docs/modules/ROOT/pages/extensibility.adoc diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc new file mode 100644 index 000000000..22cb64219 --- /dev/null +++ b/docs/modules/ROOT/pages/index.adoc @@ -0,0 +1,98 @@ += Contracts for Cairo + +**A library for secure smart contract development** written in Cairo for [StarkNet](https://starkware.co/product/starknet/), a decentralized ZK Rollup. + +## Security Advisory ⚠️ + +- A critical [vulnerability](https://github.com/OpenZeppelin/cairo-contracts/issues/344) was found in an **unreleased** version of the Account contract. It was [introduced in March 25th](https://github.com/OpenZeppelin/cairo-contracts/pull/233) and has been [patched as of June 1st](https://github.com/OpenZeppelin/cairo-contracts/pull/347). If you copied the Account contract code into your project during that period, please update to the patched version. Note that 0.1.0 users are not affected. + +## Usage + +> ## ⚠️ WARNING! ⚠️ +> +> This repo contains highly experimental code. +> Expect rapid iteration. +> **Use at your own risk.** + +### First time? + +Before installing Cairo on your machine, you need to install `gmp`: + +```bash +sudo apt install -y libgmp3-dev # linux +brew install gmp # mac +``` + +> 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). + +### Set up your project + +Create a directory for your project, then `cd` into it and create a Python virtual environment. + +```bash +mkdir my-project +cd my-project +python3 -m venv env +source env/bin/activate +``` + +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/). + +```bash +pip install cairo-nile +nile init +``` + +### Install the library + +```bash +pip install openzeppelin-cairo-contracts +``` + +### Use a basic preset + +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](docs/Extensibility.md#presets). + +```cairo +# contracts/MyToken.cairo + +%lang starknet + +from openzeppelin.token.erc20.ERC20 import constructor +``` + +Compile and deploy it right away: + +```bash +nile compile + +nile deploy MyToken --alias my_token +``` + +> Note that `` is expected to be two integers i.e. `1` `0`. See [Uint256](docs/Utilities.md#Uint256) for more information. + +### Write a custom contract using library modules + +[Read more about libraries](docs/Extensibility.md#libraries). + +```cairo +%lang starknet + +from starkware.cairo.common.cairo_builtins import HashBuiltin +from starkware.cairo.common.uint256 import Uint256 +from openzeppelin.security.pausable 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() + ERC20.transfer(recipient, amount) + return (TRUE) +end +``` diff --git a/docs/Introspection.md b/docs/modules/ROOT/pages/introspection.adoc similarity index 100% rename from docs/Introspection.md rename to docs/modules/ROOT/pages/introspection.adoc diff --git a/docs/Proxies.md b/docs/modules/ROOT/pages/proxies.adoc similarity index 100% rename from docs/Proxies.md rename to docs/modules/ROOT/pages/proxies.adoc diff --git a/docs/Security.md b/docs/modules/ROOT/pages/security.adoc similarity index 100% rename from docs/Security.md rename to docs/modules/ROOT/pages/security.adoc diff --git a/docs/Utilities.md b/docs/modules/ROOT/pages/utilities.adoc similarity index 100% rename from docs/Utilities.md rename to docs/modules/ROOT/pages/utilities.adoc From c65892e3b96ba5d98deb4bdb500a8d97c9f0e562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Wed, 13 Jul 2022 10:15:20 -0300 Subject: [PATCH 02/39] add docs bump to RELEASING --- RELEASING.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 2cc4c85fc..78d3a2ec7 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -18,14 +18,32 @@ to # OpenZeppelin Contracts for Cairo v0.2.0 (account/Account.cairo) ``` -(3) Create a release branch and add a tag to it. This branch can be useful if we need to push a hot fix on top of an existing release in the case of a bug. +(3) Update documentation version in `docs/antora.yml` + +```yml +name: cairo-contracts +title: Contracts for Cairo +version: 0.1 +(...) +``` + +to + +```yml +name: cairo-contracts +title: Contracts for Cairo +version: 0.2 +(...) +``` + +(4) Create a release branch and add a tag to it. This branch can be useful if we need to push a hot fix on top of an existing release in the case of a bug. ```sh git checkout -b release-0.2.0 git tag v0.2.0 ``` -(4) Push the tag to the main repository, [triggering the CI and release process](https://github.com/OpenZeppelin/cairo-contracts/blob/b27101eb826fae73f49751fa384c2a0ff3377af2/.github/workflows/python-app.yml#L60). +(5) Push the tag to the main repository, [triggering the CI and release process](https://github.com/OpenZeppelin/cairo-contracts/blob/b27101eb826fae73f49751fa384c2a0ff3377af2/.github/workflows/python-app.yml#L60). ```sh git push origin v0.2.0 From e1d9d786ef41e39b3e8bfc918d96da34ace99111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Wed, 13 Jul 2022 15:47:08 -0300 Subject: [PATCH 03/39] migrate from markdown to adoc format --- docs/modules/ROOT/pages/access.adoc | 129 +++-- docs/modules/ROOT/pages/accounts.adoc | 426 +++++++++------ docs/modules/ROOT/pages/erc20.adoc | 302 ++++++----- docs/modules/ROOT/pages/erc721.adoc | 580 ++++++++++++--------- docs/modules/ROOT/pages/extensibility.adoc | 116 +++-- docs/modules/ROOT/pages/introspection.adoc | 130 +++-- docs/modules/ROOT/pages/proxies.adoc | 257 +++++---- docs/modules/ROOT/pages/security.adoc | 89 ++-- docs/modules/ROOT/pages/utilities.adoc | 207 +++++--- 9 files changed, 1326 insertions(+), 910 deletions(-) diff --git a/docs/modules/ROOT/pages/access.adoc b/docs/modules/ROOT/pages/access.adoc index 1d6788bef..7117e9a86 100644 --- a/docs/modules/ROOT/pages/access.adoc +++ b/docs/modules/ROOT/pages/access.adoc @@ -1,34 +1,41 @@ -# Access += Access -> Expect these modules to evolve. +____ +Expect these modules to evolve. +____ -Access control—that is, "who is allowed to do this thing"—is incredibly important in the world of smart contracts. The access control of your contract may govern who can mint tokens, vote on proposals, freeze transfers, and many other things. It is therefore critical to understand how you implement it, lest someone else [steals your whole system](https://blog.openzeppelin.com/on-the-parity-wallet-multisig-hack-405a8c12e8f7/). +Access control--that is, "who is allowed to do this thing"--is incredibly important in the world of smart contracts. +The access control of your contract may govern who can mint tokens, vote on proposals, freeze transfers, and many other things. +It is therefore critical to understand how you implement it, lest someone else https://blog.openzeppelin.com/on-the-parity-wallet-multisig-hack-405a8c12e8f7/[steals your whole system]. -## Table of Contents +== Table of Contents -* [Ownable](#ownable) - * [Quickstart](#quickstart) - * [Ownable library API](#ownable-library-api) - * [`initializer`](#initializer) - * [`assert_only_owner`](#assert_only_owner) - * [`owner`](#owner) - * [`transfer_ownership`](#transfer_ownership) - * [`renounce_ownership`](#renounce_ownership) - * [`_transfer_ownership`](#transfer-ownership-internal) - * [Ownable events](#ownable-events) - * [`OwnershipTransferred`](#ownershiptransferred) +* <> + ** <> + ** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + ** <> + *** <> -## Ownable +== Ownable -The most common and basic form of access control is the concept of ownership: there’s an account that is the `owner` of a contract and can do administrative tasks on it. This approach is perfectly reasonable for contracts that have a single administrative user. +The most common and basic form of access control is the concept of ownership: there's an account that is the `owner` of a contract and can do administrative tasks on it. +This approach is perfectly reasonable for contracts that have a single administrative user. -OpenZeppelin Contracts for Cairo provides [Ownable](../src/openzeppelin/access/ownable.cairo) for implementing ownership in your contracts. +OpenZeppelin Contracts for Cairo provides link:../src/openzeppelin/access/ownable.cairo[Ownable] for implementing ownership in your contracts. -### Quickstart +=== Quickstart -Integrating [Ownable](../src/openzeppelin/access/ownable.cairo) into a contract first requires assigning an owner. The implementing contract's constructor should set the initial owner by passing the owner's address to Ownable's [initializer](#initializer) like this: +Integrating link:../src/openzeppelin/access/ownable.cairo[Ownable] into a contract first requires assigning an owner. +The implementing contract's constructor should set the initial owner by passing the owner's address to Ownable's <> like this: -```cairo +[,cairo] +---- from openzeppelin.access.ownable import Ownable @constructor @@ -40,11 +47,12 @@ func constructor{ Ownable.initializer(owner) return () end -``` +---- To restrict a function's access to the owner only, add in the `assert_only_owner` method: -```cairo +[,cairo] +---- from openzeppelin.access.ownable import Ownable func protected_function{ @@ -55,11 +63,12 @@ func protected_function{ Ownable.assert_only_owner() return () end -``` +---- -### Ownable library API +=== Ownable library API -```cairo +[,cairo] +---- func initializer(owner: felt): end @@ -77,25 +86,27 @@ end func _transfer_ownership(new_owner: felt): end -``` +---- -#### `initializer` +==== `initializer` -Initializes Ownable access control and should be called in the implementing contract's constructor. Assigns `owner` as the initial owner address of the contract. +Initializes Ownable access control and should be called in the implementing contract's constructor. +Assigns `owner` as the initial owner address of the contract. This must be called only once. Parameters: -```cairo +[,cairo] +---- owner: felt -``` +---- Returns: None. -#### `assert_only_owner` +==== `assert_only_owner` Reverts if called by any account other than the owner. @@ -107,7 +118,7 @@ Returns: None. -#### `owner` +==== `owner` Returns the address of the current owner. @@ -117,31 +128,36 @@ None. Returns: -```cairo +[,cairo] +---- owner: felt -``` +---- -#### `transfer_ownership` +==== `transfer_ownership` -Transfers ownership of the contract to a new account (`new_owner`). Can only be called by the current owner. +Transfers ownership of the contract to a new account (`new_owner`). +Can only be called by the current owner. -Emits a [`OwnershipTransferred`](#ownershiptransferred) event. +Emits a <> event. Parameters: -```cairo +[,cairo] +---- new_owner: felt -``` +---- Returns: None. -#### `renounce_ownership` +==== `renounce_ownership` -Leaves the contract without owner. It will not be possible to call functions with `assert_only_owner` anymore. Can only be called by the current owner. +Leaves the contract without owner. +It will not be possible to call functions with `assert_only_owner` anymore. +Can only be called by the current owner. -Emits a [`OwnershipTransferred`](#ownershiptransferred) event. +Emits a <> event. Parameters: @@ -151,36 +167,41 @@ Returns: None. -

_transfer_ownership

+[#transfer-ownership-internal] +==== `_transfer_ownership` -Transfers ownership of the contract to a new account (`new_owner`). Unprotected method without access restriction. +Transfers ownership of the contract to a new account (`new_owner`). +Unprotected method without access restriction. -Emits a [`OwnershipTransferred`](#ownershiptransferred) event. +Emits a <> event. Parameters: -```cairo +[,cairo] +---- new_owner: felt -``` +---- Returns: None. -### Ownable events +=== Ownable events -```cairo +[,cairo] +---- func OwnershipTransferred(previousOwner: felt, newOwner: felt): end -``` +---- -#### OwnershipTransferred +==== OwnershipTransferred Emitted when ownership of a contract is transferred from `previousOwner` to `newOwner`. Parameters: -```cairo +[,cairo] +---- previousOwner: felt newOwner: felt -``` +---- diff --git a/docs/modules/ROOT/pages/accounts.adoc b/docs/modules/ROOT/pages/accounts.adoc index 7aff2a0e9..52c41fdac 100644 --- a/docs/modules/ROOT/pages/accounts.adoc +++ b/docs/modules/ROOT/pages/accounts.adoc @@ -1,51 +1,53 @@ -# Accounts += Accounts Unlike Ethereum where accounts are directly derived from a private key, there's no native account concept on StarkNet. -Instead, signature validation has to be done at the contract level. To relieve smart contract applications such as ERC20 tokens or exchanges from this responsibility, we make use of Account contracts to deal with transaction authentication. - -A more detailed writeup on the topic can be found on [Perama's blogpost](https://perama-v.github.io/cairo/account-abstraction/). - -## Table of Contents - -* [Quickstart](#quickstart) -* [Standard Interface](#standard-interface) -* [Keys, signatures and signers](#keys-signatures-and-signers) - * [Signer](#signer) - * [MockSigner utility](#mocksigner-utility) - * [MockEthSigner utility](#mockethsigner-utility) -* [Account entrypoint](#account-entrypoint) -* [Call and AccountCallArray format](#call-and-accountcallarray-format) - * [Call](#call) - * [AccountCallArray](#accountcallarray) -* [Multicall transactions](#multicall-transactions) -* [API Specification](#api-specification) - * [`get_public_key`](#get_public_key) - * [`get_nonce`](#get_nonce) - * [`set_public_key`](#set_public_key) - * [`is_valid_signature`](#is_valid_signature) - * [`__execute__`](#__execute__) - * [`is_valid_eth_signature`](#is_valid_eth_signature) - * [`eth_execute`](#eth_execute) - * [`_unsafe_execute`](#_unsafe_execute) -* [Presets](#presets) - * [Account](#account) - * [Eth Account](#eth-account) -* [Account differentiation with ERC165](#account-differentiation-with-erc165) -* [Extending the Account contract](#extending-the-account-contract) -* [L1 escape hatch mechanism](#l1-escape-hatch-mechanism) -* [Paying for gas](#paying-for-gas) - -## Quickstart +Instead, signature validation has to be done at the contract level. +To relieve smart contract applications such as ERC20 tokens or exchanges from this responsibility, we make use of Account contracts to deal with transaction authentication. + +A more detailed writeup on the topic can be found on https://perama-v.github.io/cairo/account-abstraction/[Perama's blogpost]. + +== Table of Contents + +* <> +* <> +* <> + ** <> + ** <> + ** <> +* <> +* <> + ** <> + ** <> +* <> +* <> + ** <> + ** <> + ** <> + ** <> + ** <<__execute__,`__execute__`>> + ** <> + ** <> + ** <<_unsafe_execute,`_unsafe_execute`>> +* <> + ** <> + ** <> +* <> +* <> +* <> +* <> + +== Quickstart The general workflow is: -1. Account contract is deployed to StarkNet -2. Signed transactions can now be sent to the Account contract which validates and executes them +. Account contract is deployed to StarkNet +. Signed transactions can now be sent to the Account contract which validates and executes them In Python, this would look as follows: -```python +[,python] +---- from starkware.starknet.testing.starknet import Starknet signer = MockSigner(123456789987654321) starknet = await Starknet.empty() @@ -58,13 +60,15 @@ account = await starknet.deploy( # 2. Send transaction through Account await signer.send_transaction(account, some_contract_address, 'some_function', [some_parameter]) -``` +---- -## Standard Interface +== Standard Interface -The [`IAccount.cairo`](../src/openzeppelin/account/IAccount.cairo) contract interface contains the standard account interface proposed in [#41](https://github.com/OpenZeppelin/cairo-contracts/discussions/41) and adopted by OpenZeppelin and Argent. It implements [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) and it is agnostic of signature validation and nonce management strategies. +The link:../src/openzeppelin/account/IAccount.cairo[`IAccount.cairo`] contract interface contains the standard account interface proposed in https://github.com/OpenZeppelin/cairo-contracts/discussions/41[#41] and adopted by OpenZeppelin and Argent. +It implements https://eips.ethereum.org/EIPS/eip-1271[EIP-1271] and it is agnostic of signature validation and nonce management strategies. -```cairo +[,cairo] +---- @contract_interface namespace IAccount: # @@ -94,26 +98,31 @@ namespace IAccount: ) -> (response_len: felt, response: felt*): end end -``` +---- -## Keys, signatures and signers +== Keys, signatures and signers -While the interface is agnostic of signature validation schemes, this implementation assumes there's a public-private key pair controlling the Account. That's why the `constructor` function expects a `public_key` parameter to set it. Since there's also a `set_public_key()` method, accounts can be effectively transferred. +While the interface is agnostic of signature validation schemes, this implementation assumes there's a public-private key pair controlling the Account. +That's why the `constructor` function expects a `public_key` parameter to set it. +Since there's also a `set_public_key()` method, accounts can be effectively transferred. Note that although the current implementation works only with StarkKeys, support for Ethereum's ECDSA algorithm will be added in the future. -### Signer +=== Signer -The signer is responsible for creating a transaction signature with the user's private key for a given transaction. This implementation utilizes [Nile's Signer](https://github.com/OpenZeppelin/nile/blob/main/src/nile/signer.py) class to create transaction signatures through the `Signer` method `sign_transaction`. +The signer is responsible for creating a transaction signature with the user's private key for a given transaction. +This implementation utilizes https://github.com/OpenZeppelin/nile/blob/main/src/nile/signer.py[Nile's Signer] class to create transaction signatures through the `Signer` method `sign_transaction`. `sign_transaction` expects the following parameters per transaction: * `sender` the contract address invoking the tx -* `calls` a list containing a sublist of each call to be sent. Each sublist must consist of: - 1. `to` the address of the target contract of the message - 2. `selector` the function to be called on the target contract - 3. `calldata` the parameters for the given `selector` -* `nonce` an unique identifier of this message to prevent transaction replays. Current implementation requires nonces to be incremental +* `calls` a list containing a sublist of each call to be sent. +Each sublist must consist of: + .. `to` the address of the target contract of the message + .. `selector` the function to be called on the target contract + .. `calldata` the parameters for the given `selector` +* `nonce` an unique identifier of this message to prevent transaction replays. +Current implementation requires nonces to be incremental * `max_fee` the maximum fee a user will pay Which returns: @@ -123,40 +132,46 @@ Which returns: * `sig_r` the transaction signature * `sig_s` the transaction signature -While the `Signer` class performs much of the work for a transaction to be sent, it neither manages nonces nor invokes the actual transaction on the Account contract. To simplify Account management, most of this is abstracted away with `MockSigner`. +While the `Signer` class performs much of the work for a transaction to be sent, it neither manages nonces nor invokes the actual transaction on the Account contract. +To simplify Account management, most of this is abstracted away with `MockSigner`. -### MockSigner utility +=== MockSigner utility -The `MockSigner` class in [utils.py](../tests/utils.py) is used to perform transactions on a given Account, crafting the transaction and managing nonces. +The `MockSigner` class in link:../tests/utils.py[utils.py] is used to perform transactions on a given Account, crafting the transaction and managing nonces. -The flow of a transaction starts with checking the nonce and converting the `to` contract address of each call to hexadecimal format. The hexadecimal conversion is necessary because Nile's `Signer` converts the address to a base-16 integer (which requires a string argument). Note that directly converting `to` to a string will ultimately result in an integer exceeding Cairo's `FIELD_PRIME`. +The flow of a transaction starts with checking the nonce and converting the `to` contract address of each call to hexadecimal format. +The hexadecimal conversion is necessary because Nile's `Signer` converts the address to a base-16 integer (which requires a string argument). +Note that directly converting `to` to a string will ultimately result in an integer exceeding Cairo's `FIELD_PRIME`. -The values included in the transaction are passed to the `sign_transaction` method of Nile's `Signer` which creates and returns a signature. Finally, the `MockSigner` instance invokes the account contract's `__execute__` with the transaction data. +The values included in the transaction are passed to the `sign_transaction` method of Nile's `Signer` which creates and returns a signature. +Finally, the `MockSigner` instance invokes the account contract's `__execute__` with the transaction data. Users only need to interact with the following exposed methods to perform a transaction: * `send_transaction(account, to, selector_name, calldata, nonce=None, max_fee=0)` returns a future of a signed transaction, ready to be sent. - * `send_transactions(account, calls, nonce=None, max_fee=0)` returns a future of batched signed transactions, ready to be sent. To use `MockSigner`, pass a private key when instantiating the class: -```python +[,python] +---- from utils import MockSigner PRIVATE_KEY = 123456789987654321 signer = MockSigner(PRIVATE_KEY) -``` +---- Then send single transactions with the `send_transaction` method. -```python +[,python] +---- await signer.send_transaction(account, contract_address, 'method_name', []) -``` +---- If utilizing multicall, send multiple transactions with the `send_transactions` method. -```python +[,python] +---- await signer.send_transactions( account, [ @@ -164,34 +179,40 @@ If utilizing multicall, send multiple transactions with the `send_transactions` (contract_address, 'another_method', []) ] ) -``` +---- -### MockEthSigner utility +=== MockEthSigner utility -The `MockEthSigner` class in [utils.py](../tests/utils.py) is used to perform transactions on a given Account with a secp256k1 curve key pair, crafting the transaction and managing nonces. It differs from the `MockSigner` implementation by: +The `MockEthSigner` class in link:../tests/utils.py[utils.py] is used to perform transactions on a given Account with a secp256k1 curve key pair, crafting the transaction and managing nonces. +It differs from the `MockSigner` implementation by: * not using the public key but its derived address instead (the last 20 bytes of the keccak256 hash of the public key and adding `0x` to the beginning) * signing the message with a secp256k1 curve address -## Account entrypoint +== Account entrypoint -`__execute__` acts as a single entrypoint for all user interaction with any contract, including managing the account contract itself. That's why if you want to change the public key controlling the Account, you would send a transaction targeting the very Account contract: +`__execute__` acts as a single entrypoint for all user interaction with any contract, including managing the account contract itself. +That's why if you want to change the public key controlling the Account, you would send a transaction targeting the very Account contract: -```python +[,python] +---- await signer.send_transaction(account, account.contract_address, 'set_public_key', [NEW_KEY]) -``` +---- Or if you want to update the Account's L1 address on the `AccountRegistry` contract, you would -```python +[,python] +---- await signer.send_transaction(account, registry.contract_address, 'set_L1_address', [NEW_ADDRESS]) -``` +---- -You can read more about how messages are structured and hashed in the [Account message scheme discussion](https://github.com/OpenZeppelin/cairo-contracts/discussions/24). For more information on the design choices and implementation of multicall, you can read the [How should Account multicall work discussion](https://github.com/OpenZeppelin/cairo-contracts/discussions/27). +You can read more about how messages are structured and hashed in the https://github.com/OpenZeppelin/cairo-contracts/discussions/24[Account message scheme discussion]. +For more information on the design choices and implementation of multicall, you can read the https://github.com/OpenZeppelin/cairo-contracts/discussions/27[How should Account multicall work discussion]. The `__execute__` method has the following interface: -```cairo +[,cairo] +---- func __execute__( call_array_len: felt, call_array: AccountCallArray*, @@ -200,7 +221,7 @@ func __execute__( nonce: felt ) -> (response_len: felt, response: felt*): end -``` +---- Where: @@ -208,26 +229,35 @@ Where: * `call_array` is an array representing each `Call` * `calldata_len` is the number of calldata parameters * `calldata` is an array representing the function parameters -* `nonce` is an unique identifier of this message to prevent transaction replays. Current implementation requires nonces to be incremental +* `nonce` is an unique identifier of this message to prevent transaction replays. +Current implementation requires nonces to be incremental -> Note that the scheme of building multicall transactions within the `__execute__` method will change once StarkNet allows for pointers in struct arrays. In which case, multiple transactions can be passed to (as opposed to built within) `__execute__`. +____ +Note that the scheme of building multicall transactions within the `__execute__` method will change once StarkNet allows for pointers in struct arrays. +In which case, multiple transactions can be passed to (as opposed to built within) `__execute__`. +____ -## `Call` and `AccountCallArray` format +== `Call` and `AccountCallArray` format -The idea is for all user intent to be encoded into a `Call` representing a smart contract call. Users can also pack multiple messages into a single transaction (creating a multicall transaction). Cairo currently does not support arrays of structs with pointers which means the `__execute__` function cannot properly iterate through mutiple `Call`s. Instead, this implementation utilizes a workaround with the `AccountCallArray` struct. See [Multicall transactions](#multicall-transactions). +The idea is for all user intent to be encoded into a `Call` representing a smart contract call. +Users can also pack multiple messages into a single transaction (creating a multicall transaction). +Cairo currently does not support arrays of structs with pointers which means the `__execute__` function cannot properly iterate through mutiple ``Call``s. +Instead, this implementation utilizes a workaround with the `AccountCallArray` struct. +See <>. -### `Call` +=== `Call` A single `Call` is structured as follows: -```cairo +[,cairo] +---- struct Call: member to: felt member selector: felt member calldata_len: felt member calldata: felt* end -``` +---- Where: @@ -236,54 +266,64 @@ Where: * `calldata_len` is the number of calldata parameters * `calldata` is an array representing the function parameters -### `AccountCallArray` +=== `AccountCallArray` `AccountCallArray` is structured as: -```cairo +[,cairo] +---- struct AccountCallArray: member to: felt member selector: felt member data_offset: felt member data_len: felt end -``` +---- Where: * `to` is the address of the target contract of the message * `selector` is the selector of the function to be called on the target contract -* `data_offset` is the starting position of the calldata array that holds the `Call`'s calldata +* `data_offset` is the starting position of the calldata array that holds the ``Call``'s calldata * `data_len` is the number of calldata elements in the `Call` -## Multicall transactions +== Multicall transactions -A multicall transaction packs the `to`, `selector`, `calldata_offset`, and `calldata_len` of each call into the `AccountCallArray` struct and keeps the cumulative calldata for every call in a separate array. The `__execute__` function rebuilds each message by combining the `AccountCallArray` with its calldata (demarcated by the offset and calldata length specified for that particular call). The rebuilding logic is set in the internal `_from_call_array_to_call`. +A multicall transaction packs the `to`, `selector`, `calldata_offset`, and `calldata_len` of each call into the `AccountCallArray` struct and keeps the cumulative calldata for every call in a separate array. +The `__execute__` function rebuilds each message by combining the `AccountCallArray` with its calldata (demarcated by the offset and calldata length specified for that particular call). +The rebuilding logic is set in the internal `_from_call_array_to_call`. This is the basic flow: -1. The user sends the messages for the transaction through a Signer instantiation which looks like this: - - ```python - await signer.send_transaction( - account, [ - (contract_address, 'contract_method', [arg_1]), - (contract_address, 'another_method', [arg_1, arg_2]) - ] - ) - ``` - - The `_from_call_to_call_array` method in [utils.py](../tests/utils.py) converts each call into the `AccountCallArray` format and cumulatively stores the calldata of every call into a single array. Next, both arrays (as well as the `sender`, `nonce`, and `max_fee`) are used to create the transaction hash. The Signer then invokes `__execute__` with the signature and passes `AccountCallArray`, calldata, and nonce as arguments. - -2. The `__execute__` method takes the `AccountCallArray` and calldata and builds an array of `Call`s (MultiCall). - -> It should be noted that every transaction utilizes `AccountCallArray`. A single `Call` is treated as a bundle with one message. - -## API Specification +. The user sends the messages for the transaction through a Signer instantiation which looks like this: ++ +[,python] +---- + await signer.send_transaction( + account, [ + (contract_address, 'contract_method', [arg_1]), + (contract_address, 'another_method', [arg_1, arg_2]) + ] + ) +---- ++ +The `_from_call_to_call_array` method in link:../tests/utils.py[utils.py] converts each call into the `AccountCallArray` format and cumulatively stores the calldata of every call into a single array. +Next, both arrays (as well as the `sender`, `nonce`, and `max_fee`) are used to create the transaction hash. +The Signer then invokes `__execute__` with the signature and passes `AccountCallArray`, calldata, and nonce as arguments. + +. The `__execute__` method takes the `AccountCallArray` and calldata and builds an array of ``Call``s (MultiCall). + +____ +It should be noted that every transaction utilizes `AccountCallArray`. +A single `Call` is treated as a bundle with one message. +____ + +== API Specification This in a nutshell is the Account contract public API: -```cairo +[,cairo] +---- func get_public_key() -> (res: felt): end @@ -307,9 +347,9 @@ func __execute__( nonce: felt ) -> (response_len: felt, response: felt*): end -``` +---- -### `get_public_key` +=== `get_public_key` Returns the public key associated with the Account contract. @@ -319,11 +359,12 @@ None. Returns: -```cairo +[,cairo] +---- public_key: felt -``` +---- -### `get_nonce` +=== `get_nonce` Returns the current transaction nonce for the Account. @@ -333,162 +374,201 @@ None. Returns: -```cairo +[,cairo] +---- nonce: felt -``` +---- -### `set_public_key` +=== `set_public_key` -Sets the public key that will control this Account. It can be used to rotate keys for security, change them in case of compromised keys or even transferring ownership of the account. +Sets the public key that will control this Account. +It can be used to rotate keys for security, change them in case of compromised keys or even transferring ownership of the account. Parameters: -```cairo +[,cairo] +---- public_key: felt -``` +---- Returns: None. -### `is_valid_signature` +=== `is_valid_signature` -This function is inspired by [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) and returns `TRUE` if a given signature is valid, otherwise it reverts. In the future it will return `FALSE` if a given signature is invalid (for more info please check [this issue](https://github.com/OpenZeppelin/cairo-contracts/issues/327)). +This function is inspired by https://eips.ethereum.org/EIPS/eip-1271[EIP-1271] and returns `TRUE` if a given signature is valid, otherwise it reverts. +In the future it will return `FALSE` if a given signature is invalid (for more info please check https://github.com/OpenZeppelin/cairo-contracts/issues/327[this issue]). Parameters: -```cairo +[,cairo] +---- hash: felt signature_len: felt signature: felt* -``` +---- Returns: -```cairo +[,cairo] +---- is_valid: felt -``` +---- -> returns `TRUE` if a given signature is valid. Otherwise, reverts. In the future it will return `FALSE` if a given signature is invalid (for more info please check [this issue](https://github.com/OpenZeppelin/cairo-contracts/issues/327)). +____ +returns `TRUE` if a given signature is valid. +Otherwise, reverts. +In the future it will return `FALSE` if a given signature is invalid (for more info please check https://github.com/OpenZeppelin/cairo-contracts/issues/327[this issue]). +____ -### `__execute__` +=== `__execute__` -This is the only external entrypoint to interact with the Account contract. It: +This is the only external entrypoint to interact with the Account contract. +It: -1. Validates the transaction signature matches the message (including the nonce) -2. Increments the nonce -3. Calls the target contract with the intended function selector and calldata parameters -4. Forwards the contract call response data as return value +. Validates the transaction signature matches the message (including the nonce) +. Increments the nonce +. Calls the target contract with the intended function selector and calldata parameters +. Forwards the contract call response data as return value Parameters: -```cairo +[,cairo] +---- call_array_len: felt call_array: AccountCallArray* calldata_len: felt calldata: felt* nonce: felt -``` +---- -> Note that the current signature scheme expects a 2-element array like `[sig_r, sig_s]`. +____ +Note that the current signature scheme expects a 2-element array like `[sig_r, sig_s]`. +____ Returns: -```cairo +[,cairo] +---- response_len: felt response: felt* -``` +---- -### `is_valid_eth_signature` +=== `is_valid_eth_signature` -Returns `TRUE` if a given signature in the secp256k1 curve is valid, otherwise it reverts. In the future it will return `FALSE` if a given signature is invalid (for more info please check [this issue](https://github.com/OpenZeppelin/cairo-contracts/issues/327)). +Returns `TRUE` if a given signature in the secp256k1 curve is valid, otherwise it reverts. +In the future it will return `FALSE` if a given signature is invalid (for more info please check https://github.com/OpenZeppelin/cairo-contracts/issues/327[this issue]). Parameters: -```cairo +[,cairo] +---- signature_len: felt signature: felt* -``` +---- Returns: -```cairo +[,cairo] +---- is_valid: felt -``` +---- -> returns `TRUE` if a given signature is valid. Otherwise, reverts. In the future it will return `FALSE` if a given signature is invalid (for more info please check [this issue](https://github.com/OpenZeppelin/cairo-contracts/issues/327)). +____ +returns `TRUE` if a given signature is valid. +Otherwise, reverts. +In the future it will return `FALSE` if a given signature is invalid (for more info please check https://github.com/OpenZeppelin/cairo-contracts/issues/327[this issue]). +____ -### `eth_execute` +=== `eth_execute` This follows the same idea as the vanilla version of `execute` with the sole difference that signature verification is on the secp256k1 curve. Parameters: -```cairo +[,cairo] +---- call_array_len: felt call_array: AccountCallArray* calldata_len: felt calldata: felt* nonce: felt -``` +---- -> Note that the current signature scheme expects a 7-element array like `[sig_v, uint256_sig_r_low, uint256_sig_r_high, uint256_sig_s_low, uint256_sig_s_high, uint256_hash_low, uint256_hash_high]` given that the parameters of the verification are bigger than a felt. +____ +Note that the current signature scheme expects a 7-element array like `[sig_v, uint256_sig_r_low, uint256_sig_r_high, uint256_sig_s_low, uint256_sig_s_high, uint256_hash_low, uint256_hash_high]` given that the parameters of the verification are bigger than a felt. +____ Returns: -```cairo +[,cairo] +---- response_len: felt response: felt* -``` +---- -### `_unsafe_execute` +=== `_unsafe_execute` It's an internal method that performs the following tasks: -1. Increments the nonce. -2. Takes the input and builds a `Call` for each iterated message. See [Multicall transactions](#multicall-transactions) for more information. -3. Calls the target contract with the intended function selector and calldata parameters -4. Forwards the contract call response data as return value +. Increments the nonce. +. Takes the input and builds a `Call` for each iterated message. +See <> for more information. +. Calls the target contract with the intended function selector and calldata parameters +. Forwards the contract call response data as return value -## Presets +== Presets -The following contract presets are ready to deploy and can be used as-is for quick prototyping and testing. Each preset differs on the signature type being used by the Account. +The following contract presets are ready to deploy and can be used as-is for quick prototyping and testing. +Each preset differs on the signature type being used by the Account. -### Account +=== Account -The [`Account`](../src/openzeppelin/account/Account.cairo) preset uses StarkNet keys to validate transactions. +The link:../src/openzeppelin/account/Account.cairo[`Account`] preset uses StarkNet keys to validate transactions. -### Eth Account +=== Eth Account -The [`EthAccount`](../src/openzeppelin/account/EthAccount.cairo) preset supports Ethereum addresses, validating transactions with secp256k1 keys. +The link:../src/openzeppelin/account/EthAccount.cairo[`EthAccount`] preset supports Ethereum addresses, validating transactions with secp256k1 keys. -## Account differentiation with ERC165 +== Account differentiation with ERC165 -Certain contracts like ERC721 require a means to differentiate between account contracts and non-account contracts. For a contract to declare itself as an account, it should implement [ERC165](https://eips.ethereum.org/EIPS/eip-165) as proposed in [#100](https://github.com/OpenZeppelin/cairo-contracts/discussions/100). To be in compliance with ERC165 specifications, the idea is to calculate the XOR of `IAccount`'s EVM selectors (not StarkNet selectors). The resulting magic value of `IAccount` is 0x50b70dcb. +Certain contracts like ERC721 require a means to differentiate between account contracts and non-account contracts. +For a contract to declare itself as an account, it should implement https://eips.ethereum.org/EIPS/eip-165[ERC165] as proposed in https://github.com/OpenZeppelin/cairo-contracts/discussions/100[#100]. +To be in compliance with ERC165 specifications, the idea is to calculate the XOR of ``IAccount``'s EVM selectors (not StarkNet selectors). +The resulting magic value of `IAccount` is 0x50b70dcb. -Our ERC165 integration on StarkNet is inspired by OpenZeppelin's Solidity implementation of [ERC165Storage](https://docs.openzeppelin.com/contracts/4.x/api/utils#ERC165Storage) which stores the interfaces that the implementing contract supports. In the case of account contracts, querying `supportsInterface` of an account's address with the `IAccount` magic value should return `TRUE`. +Our ERC165 integration on StarkNet is inspired by OpenZeppelin's Solidity implementation of https://docs.openzeppelin.com/contracts/4.x/api/utils#ERC165Storage[ERC165Storage] which stores the interfaces that the implementing contract supports. +In the case of account contracts, querying `supportsInterface` of an account's address with the `IAccount` magic value should return `TRUE`. -## Extending the Account contract +== Extending the Account contract -Account contracts can be extended by following the [extensibility pattern](../docs/Extensibility.md#the-pattern). +Account contracts can be extended by following the link:../docs/Extensibility.md#the-pattern[extensibility pattern]. -To implement custom account contracts, a pair of `validate` and `execute` functions should be exposed. This is why the Account library comes with different flavors of such pairs, like the vanilla `is_valid_signature` and `execute`, or the Ethereum flavored `is_valid_eth_signature` and `eth_execute` pair. +To implement custom account contracts, a pair of `validate` and `execute` functions should be exposed. +This is why the Account library comes with different flavors of such pairs, like the vanilla `is_valid_signature` and `execute`, or the Ethereum flavored `is_valid_eth_signature` and `eth_execute` pair. -Account contract developers are encouraged to implement the [standard Account interface](https://github.com/OpenZeppelin/cairo-contracts/discussions/41) and incorporate the custom logic thereafter. +Account contract developers are encouraged to implement the https://github.com/OpenZeppelin/cairo-contracts/discussions/41[standard Account interface] and incorporate the custom logic thereafter. -To implement alternative `execute` functions, make sure to check their corresponding `validate` function before calling the `_unsafe_execute` building block, as each of the current presets is doing. Do not expose `_unsafe_execute` directly. +To implement alternative `execute` functions, make sure to check their corresponding `validate` function before calling the `_unsafe_execute` building block, as each of the current presets is doing. +Do not expose `_unsafe_execute` directly. -> Please note that the `ecdsa_ptr` implicit argument should be included in new methods that invoke `_unsafe_execute` (even if the `ecdsa_ptr` is not being used). Otherwise, it's possible that an account's functionalty can work in both the testing and local devnet environments; however, it could fail on public networks on account of the [SignatureBuiltinRunner](https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/cairo/lang/builtins/signature/signature_builtin_runner.py). See [issue #386](https://github.com/OpenZeppelin/cairo-contracts/issues/386) for more information. +____ +Please note that the `ecdsa_ptr` implicit argument should be included in new methods that invoke `_unsafe_execute` (even if the `ecdsa_ptr` is not being used). +Otherwise, it's possible that an account's functionalty can work in both the testing and local devnet environments; +however, it could fail on public networks on account of the https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/cairo/lang/builtins/signature/signature_builtin_runner.py[SignatureBuiltinRunner]. +See https://github.com/OpenZeppelin/cairo-contracts/issues/386[issue #386] for more information. +____ Some other validation schemes to look out for in the future: * multisig -* guardian logic like in [Argent's account](https://github.com/argentlabs/argent-contracts-starknet/blob/de5654555309fa76160ba3d7393d32d2b12e7349/contracts/ArgentAccount.cairo) +* guardian logic like in https://github.com/argentlabs/argent-contracts-starknet/blob/de5654555309fa76160ba3d7393d32d2b12e7349/contracts/ArgentAccount.cairo[Argent's account] -## L1 escape hatch mechanism +== L1 escape hatch mechanism [unknown, to be defined] -## Paying for gas +== Paying for gas [unknown, to be defined] diff --git a/docs/modules/ROOT/pages/erc20.adoc b/docs/modules/ROOT/pages/erc20.adoc index 9c3550726..3ab7593fe 100644 --- a/docs/modules/ROOT/pages/erc20.adoc +++ b/docs/modules/ROOT/pages/erc20.adoc @@ -1,36 +1,38 @@ -# ERC20 - -The ERC20 token standard is a specification for [fungible tokens](https://docs.openzeppelin.com/contracts/4.x/tokens#different-kinds-of-tokens), a type of token where all the units are exactly equal to each other. The `ERC20.cairo` contract implements an approximation of [EIP-20](https://eips.ethereum.org/EIPS/eip-20) in Cairo for StarkNet. - -## Table of Contents - -- [Interface](#interface) - - [ERC20 compatibility](#erc20-compatibility) -- [Usage](#usage) -- [Extensibility](#extensibility) -- [Presets](#presets) - - [ERC20 (basic)](#erc20-basic) - - [ERC20_Mintable](#erc20_mintable) - - [ERC20_Pausable](#erc20_pausable) - - [ERC20_Upgradeable](#erc20_upgradeable) -- [API Specification](#api-specification) - - [Methods](#methods) - - [`name`](#name) - - [`symbol`](#symbol) - - [`decimals`](#decimals) - - [`totalSupply`](#totalsupply) - - [`balanceOf`](#balanceof) - - [`allowance`](#allowance) - - [`transfer`](#transfer) - - [`transferFrom`](#transferfrom) - - [`approve`](#approve) - - [Events](#events) - - [`Transfer (event)`](#transfer-event) - - [`Approval (event)`](#approval-event) - -## Interface - -```cairo += ERC20 + +The ERC20 token standard is a specification for https://docs.openzeppelin.com/contracts/4.x/tokens#different-kinds-of-tokens[fungible tokens], a type of token where all the units are exactly equal to each other. +The `ERC20.cairo` contract implements an approximation of https://eips.ethereum.org/EIPS/eip-20[EIP-20] in Cairo for StarkNet. + +== Table of Contents + +* <> + ** <> +* <> +* <> +* <> + ** <> + ** <> + ** <> + ** <> +* <> + ** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + ** <> + *** <> + *** <> + +== Interface + +[,cairo] +---- @contract_interface namespace IERC20: func name() -> (name: felt): @@ -64,29 +66,30 @@ namespace IERC20: func approve(spender: felt, amount: Uint256) -> (success: felt): end end -``` +---- -### ERC20 compatibility +=== ERC20 compatibility Although StarkNet is not EVM compatible, this implementation aims to be as close as possible to the ERC20 standard, in the following ways: -- it uses Cairo's `uint256` instead of `felt` -- it returns `TRUE` as success -- it accepts a `felt` argument for `decimals` in the constructor calldata with a max value of 2^8 (imitating `uint8` type) -- it makes use of Cairo's short strings to simulate `name` and `symbol` +* it uses Cairo's `uint256` instead of `felt` +* it returns `TRUE` as success +* it accepts a `felt` argument for `decimals` in the constructor calldata with a max value of 2{caret}8 (imitating `uint8` type) +* it makes use of Cairo's short strings to simulate `name` and `symbol` But some differences can still be found, such as: -- `transfer`, `transferFrom` and `approve` will never return anything different from `TRUE` because they will revert on any error -- function selectors are calculated differently between [Cairo](https://github.com/starkware-libs/cairo-lang/blob/7712b21fc3b1cb02321a58d0c0579f5370147a8b/src/starkware/starknet/public/abi.py#L25) and [Solidity](https://solidity-by-example.org/function-selector/) +* `transfer`, `transferFrom` and `approve` will never return anything different from `TRUE` because they will revert on any error +* function selectors are calculated differently between https://github.com/starkware-libs/cairo-lang/blob/7712b21fc3b1cb02321a58d0c0579f5370147a8b/src/starkware/starknet/public/abi.py#L25[Cairo] and https://solidity-by-example.org/function-selector/[Solidity] -## Usage +== Usage Use cases go from medium of exchange currency to voting rights, staking, and more. Considering that the constructor method looks like this: -```python +[,python] +---- func constructor( name: felt, # Token name as Cairo short string symbol: felt, # Token symbol as Cairo short string @@ -94,11 +97,12 @@ func constructor( initial_supply: Uint256, # Amount to be minted recipient: felt # Address where to send initial supply to ): -``` +---- To create a token you need to deploy it like this: -```python +[,python] +---- erc20 = await starknet.deploy( "contracts/token/ERC20.cairo", constructor_calldata=[ @@ -109,11 +113,14 @@ erc20 = await starknet.deploy( account.contract_address # recipient ] ) -``` +---- -As most StarkNet contracts, it expects to be called by another contract and it identifies it through `get_caller_address` (analogous to Solidity's `this.address`). This is why we need an Account contract to interact with it. For example: +As most StarkNet contracts, it expects to be called by another contract and it identifies it through `get_caller_address` (analogous to Solidity's `this.address`). +This is why we need an Account contract to interact with it. +For example: -```python +[,python] +---- signer = MockSigner(PRIVATE_KEY) amount = uint(100) @@ -123,13 +130,19 @@ account = await starknet.deploy( ) await signer.send_transaction(account, erc20.contract_address, 'transfer', [recipient_address, *amount]) -``` +---- -## Extensibility +== Extensibility -ERC20 contracts can be extended by following the [extensibility pattern](../docs/Extensibility.md#the-pattern). The basic idea behind integrating the pattern is to import the requisite ERC20 methods from the ERC20 library and incorporate the extended logic thereafter. For example, let's say you wanted to implement a pausing mechanism. The contract should first import the ERC20 methods and the extended logic from the [pausable library](../src/openzeppelin/security/pausable.cairo) i.e. `Pausable_pause`, `Pausable_unpause`. Next, the contract should expose the methods with the extended logic therein like this: +ERC20 contracts can be extended by following the link:../docs/Extensibility.md#the-pattern[extensibility pattern]. +The basic idea behind integrating the pattern is to import the requisite ERC20 methods from the ERC20 library and incorporate the extended logic thereafter. +For example, let's say you wanted to implement a pausing mechanism. +The contract should first import the ERC20 methods and the extended logic from the link:../src/openzeppelin/security/pausable.cairo[pausable library] i.e. +`Pausable_pause`, `Pausable_unpause`. +Next, the contract should expose the methods with the extended logic therein like this: -```python +[,python] +---- @external func transfer{ syscall_ptr : felt*, @@ -140,43 +153,50 @@ func transfer{ ERC20_transfer(recipient, amount) # imported library method return (TRUE) end -``` +---- -Note that extensibility does not have to be only library-based like in the above example. For instance, an ERC20 contract with a pausing mechanism can define the pausing methods directly in the contract or even import the `pausable` methods from the library and tailor them further. +Note that extensibility does not have to be only library-based like in the above example. +For instance, an ERC20 contract with a pausing mechanism can define the pausing methods directly in the contract or even import the `pausable` methods from the library and tailor them further. Some other ways to extend ERC20 contracts may include: -- Implementing a minting mechanism -- Creating a timelock -- Adding roles such as owner or minter +* Implementing a minting mechanism +* Creating a timelock +* Adding roles such as owner or minter -For full examples of the extensibility pattern being used in ERC20 contracts, see [Presets](#presets). +For full examples of the extensibility pattern being used in ERC20 contracts, see <>. -## Presets +== Presets -The following contract presets are ready to deploy and can be used as-is for quick prototyping and testing. Each preset mints an initial supply which is especially necessary for presets that do not expose a `mint` method. +The following contract presets are ready to deploy and can be used as-is for quick prototyping and testing. +Each preset mints an initial supply which is especially necessary for presets that do not expose a `mint` method. -### ERC20 (basic) +=== ERC20 (basic) -The [`ERC20`](../src/openzeppelin/token/erc20/ERC20.cairo) preset offers a quick and easy setup for deploying a basic ERC20 token. +The link:../src/openzeppelin/token/erc20/ERC20.cairo[`ERC20`] preset offers a quick and easy setup for deploying a basic ERC20 token. -### ERC20_Mintable +=== ERC20_Mintable -The [`ERC20_Mintable`](../src/openzeppelin/token/erc20/ERC20_Mintable.cairo) preset allows the contract owner to mint new tokens. +The link:../src/openzeppelin/token/erc20/ERC20_Mintable.cairo[`ERC20_Mintable`] preset allows the contract owner to mint new tokens. -### ERC20_Pausable +=== ERC20_Pausable -The [`ERC20_Pausable`](../src/openzeppelin/token/erc20/ERC20_Pausable.cairo) preset allows the contract owner to pause/unpause all state-modifying methods i.e. `transfer`, `approve`, etc. This preset proves useful for scenarios such as preventing trades until the end of an evaluation period and having an emergency switch for freezing all token transfers in the event of a large bug. +The link:../src/openzeppelin/token/erc20/ERC20_Pausable.cairo[`ERC20_Pausable`] preset allows the contract owner to pause/unpause all state-modifying methods i.e. +`transfer`, `approve`, etc. +This preset proves useful for scenarios such as preventing trades until the end of an evaluation period and having an emergency switch for freezing all token transfers in the event of a large bug. -### ERC20_Upgradeable +=== ERC20_Upgradeable -The [`ERC20_Upgradeable`](../src/openzeppelin/token/erc20/ERC20_Upgradeable.cairo) preset allows the contract owner to upgrade a contract by deploying a new ERC20 implementation contract while also maintaing the contract's state. This preset proves useful for scenarios such as eliminating bugs and adding new features. For more on upgradeability, see [Contract upgrades](Proxies.md#contract-upgrades). +The link:../src/openzeppelin/token/erc20/ERC20_Upgradeable.cairo[`ERC20_Upgradeable`] preset allows the contract owner to upgrade a contract by deploying a new ERC20 implementation contract while also maintaing the contract's state. +This preset proves useful for scenarios such as eliminating bugs and adding new features. +For more on upgradeability, see link:Proxies.md#contract-upgrades[Contract upgrades]. -## API Specification +== API Specification -### Methods +=== Methods -```cairo +[,cairo] +---- func name() -> (name: felt): end @@ -207,9 +227,9 @@ end func approve(spender: felt, amount: Uint256) -> (success: felt): end -``` +---- -#### `name` +==== `name` Returns the name of the token. @@ -217,11 +237,12 @@ Parameters: None. Returns: -```cairo +[,cairo] +---- name: felt -``` +---- -#### `symbol` +==== `symbol` Returns the ticker symbol of the token. @@ -229,23 +250,26 @@ Parameters: None. Returns: -```cairo +[,cairo] +---- symbol: felt -``` +---- -#### `decimals` +==== `decimals` -Returns the number of decimals the token uses - e.g. 8 means to divide the token amount by 100000000 to get its user representation. +Returns the number of decimals the token uses - e.g. +8 means to divide the token amount by 100000000 to get its user representation. Parameters: None. Returns: -```cairo +[,cairo] +---- decimals: felt -``` +---- -#### `totalSupply` +==== `totalSupply` Returns the amount of tokens in existence. @@ -253,114 +277,131 @@ Parameters: None. Returns: -```cairo +[,cairo] +---- totalSupply: Uint256 -``` +---- -#### `balanceOf` +==== `balanceOf` -Returns the amount of tokens owned by `account`. +Returns the amount of tokens owned by `account`. Parameters: -```cairo +[,cairo] +---- account: felt -``` +---- Returns: -```cairo +[,cairo] +---- balance: Uint256 -``` +---- -#### `allowance` +==== `allowance` -Returns the remaining number of tokens that `spender` will be allowed to spend on behalf of `owner` through `transferFrom`. This is zero by default. +Returns the remaining number of tokens that `spender` will be allowed to spend on behalf of `owner` through `transferFrom`. +This is zero by default. -This value changes when `approve` or `transferFrom` are called. +This value changes when `approve` or `transferFrom` are called. Parameters: -```cairo +[,cairo] +---- owner: felt spender: felt -``` +---- Returns: -```cairo +[,cairo] +---- remaining: Uint256 -``` +---- -#### `transfer` +==== `transfer` -Moves `amount` tokens from the caller’s account to `recipient`. It returns `1` representing a bool if it succeeds. +Moves `amount` tokens from the caller's account to `recipient`. +It returns `1` representing a bool if it succeeds. -Emits a [Transfer](#transfer-event) event. +Emits a <> event. Parameters: -```cairo +[,cairo] +---- recipient: felt amount: Uint256 -``` +---- Returns: -```cairo +[,cairo] +---- success: felt -``` +---- -#### `transferFrom` +==== `transferFrom` -Moves `amount` tokens from `sender` to `recipient` using the allowance mechanism. `amount` is then deducted from the caller’s allowance. It returns `1` representing a bool if it succeeds. +Moves `amount` tokens from `sender` to `recipient` using the allowance mechanism. +`amount` is then deducted from the caller's allowance. +It returns `1` representing a bool if it succeeds. -Emits a [Transfer](#transfer-event) event. +Emits a <> event. Parameters: -```cairo +[,cairo] +---- sender: felt recipient: felt amount: Uint256 -``` +---- Returns: -```cairo +[,cairo] +---- success: felt -``` +---- -#### `approve` +==== `approve` -Sets `amount` as the allowance of `spender` over the caller’s tokens. It returns `1` representing a bool if it succeeds. +Sets `amount` as the allowance of `spender` over the caller's tokens. +It returns `1` representing a bool if it succeeds. -Emits an [Approval](#approval-event) event. +Emits an <> event. Parameters: -```cairo +[,cairo] +---- spender: felt amount: Uint256 -``` +---- Returns: -```cairo +[,cairo] +---- success: felt -``` +---- -### Events +=== Events -```cairo +[,cairo] +---- func Transfer(from_: felt, to: felt, value: Uint256): end func Approval(owner: felt, spender: felt, value: Uint256): end -``` +---- -#### `Transfer (event)` +==== `Transfer (event)` Emitted when `value` tokens are moved from one account (`from_`) to another (`to`). @@ -368,20 +409,23 @@ Note that `value` may be zero. Parameters: -```cairo +[,cairo] +---- from_: felt to: felt value: Uint256 -``` +---- -#### `Approval (event)` +==== `Approval (event)` -Emitted when the allowance of a `spender` for an `owner` is set by a call to [approve](#approve). `value` is the new allowance. +Emitted when the allowance of a `spender` for an `owner` is set by a call to <>. +`value` is the new allowance. Parameters: -```cairo +[,cairo] +---- owner: felt spender: felt value: Uint256 -``` +---- diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index c43e1fa26..a9c41a86e 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -1,56 +1,58 @@ -# ERC721 - -The ERC721 token standard is a specification for [non-fungible tokens](https://docs.openzeppelin.com/contracts/4.x/tokens#different-kinds-of-tokens), or more colloquially: NFTs. The `ERC721.cairo` contract implements an approximation of [EIP-721](https://eips.ethereum.org/EIPS/eip-721) in Cairo for StarkNet. - -## Table of Contents - -- [IERC721](#ierc721) -- [ERC721 Compatibility](#erc721-compatibility) -- [Usage](#usage) - - [Token Transfers](#token-transfers) - - [Interpreting ERC721 URIs](#interpreting-erc721-uris) - - [ERC721Received](#erc721received) - - [IERC721_Receiver](#ierc721_receiver) - - [Supporting Interfaces](#supporting-interfaces) - - [Ready-to-Use Presets](#ready-to-use-presets) -- [Extensibility](#extensibility) -- [Presets](#presets) - - [ERC721_Mintable_Burnable](#erc721_mintable_burnable) - - [ERC721_Mintable_Pausable](#erc721_mintable_pausable) - - [ERC721_Enumerable_Mintable_Burnable](#erc721_enumerable_mintable_burnable) - - [IERC721_Enumerable](#ierc721_enumerable) - - [ERC721_Metadata](#erc721_metadata) - - [IERC721_Metadata](#ierc721_metadata) -- [Utilities](#utilities) - - [ERC721_Holder](#erc721_holder) -- [API Specification](#api-specification) - - [`IERC721`](#ierc721-api) - - [`balanceOf`](#balanceof) - - [`ownerOf`](#ownerof) - - [`safeTransferFrom`](#safetransferfrom) - - [`transferFrom`](#transferfrom) - - [`approve`](#approve) - - [`setApprovalForAll`](#setapprovalforall) - - [`getApproved`](#getapproved) - - [`isApprovedForAll`](#isapprovedforall) - - [Events](#events) - - [`Approval (event)`](#approval-event) - - [`ApprovalForAll (event)`](#approvalforall-event) - - [`Transfer (event)`](#transfer-event) - - [`IERC721_Metadata`](#ierc721_metadata) - - [`name`](#name) - - [`symbol`](#symbol) - - [`tokenURI`](#tokenuri) - - [`IERC721_Enumerable`](#ierc721_enumerable) - - [`totalSupply`](#totalsupply) - - [`tokenByIndex`](#tokenbyindex) - - [`tokenOfOwnerByIndex`](#tokenofownerbyindex) - - [`IERC721_Receiver`](#ierc721_receiver) - - [`onERC721Received`](#onerc721received) - -## IERC721 - -```cairo += ERC721 + +The ERC721 token standard is a specification for https://docs.openzeppelin.com/contracts/4.x/tokens#different-kinds-of-tokens[non-fungible tokens], or more colloquially: NFTs. +The `ERC721.cairo` contract implements an approximation of https://eips.ethereum.org/EIPS/eip-721[EIP-721] in Cairo for StarkNet. + +== Table of Contents + +* <> +* <> +* <> + ** <> + ** <> + ** <> + *** <> + ** <> + ** <> +* <> +* <> + ** <> + ** <> + ** <> + *** <> + ** <> + *** <> +* <> + ** <> +* <> + ** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + ** <> + *** <> + *** <> + *** <> + ** <> + *** <> + *** <> + *** <> + ** <> + *** <> + *** <> + *** <> + ** <> + *** <> + +== IERC721 + +[,cairo] +---- @contract_interface namespace IERC721: func balanceOf(owner: felt) -> (balance: Uint256): @@ -87,48 +89,68 @@ namespace IERC721: func supportsInterface(interfaceId: felt) -> (success: felt): end end -``` +---- -### ERC721 Compatibility +=== ERC721 Compatibility Although StarkNet is not EVM compatible, this implementation aims to be as close as possible to the ERC721 standard in the following ways: -- it uses Cairo's `uint256` instead of `felt` -- it returns `TRUE` as success -- it makes use of Cairo's short strings to simulate `name` and `symbol` +* it uses Cairo's `uint256` instead of `felt` +* it returns `TRUE` as success +* it makes use of Cairo's short strings to simulate `name` and `symbol` But some differences can still be found, such as: -- `tokenURI` returns a felt representation of the queried token's URI. The EIP721 standard, however, states that the return value should be of type string. If a token's URI is not set, the returned value is `0`. Note that URIs cannot exceed 31 characters. See [Interpreting ERC721 URIs](#interpreting-erc721-uris) -- `interface_id`s are hardcoded and initialized by the constructor. The hardcoded values derive from Solidity's selector calculations. See [Supporting Interfaces](#supporting-interfaces) -- `safeTransferFrom` can only be expressed as a single function in Cairo as opposed to the two functions declared in EIP721. The difference between both functions consists of accepting `data` as an argument. Because function overloading is currently not possible in Cairo, `safeTransferFrom` by default accepts the `data` argument. If `data` is not used, simply insert `0`. -- `safeTransferFrom` is specified such that the optional `data` argument should be of type bytes. In Solidity, this means a dynamically-sized array. To be as close as possible to the standard, it accepts a dynamic array of felts. In Cairo, arrays are expressed with the array length preceding the actual array; hence, the method accepts `data_len` and `data` respectively as types `felt` and `felt*` -- `ERC165.register_interface` allows contracts to set and communicate which interfaces they support. This follows OpenZeppelin's [ERC165Storage](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/introspection/ERC165Storage.sol) -- `IERC721_Receiver` compliant contracts (`ERC721_Holder`) return a hardcoded selector id according to EVM selectors, since selectors are calculated differently in Cairo. This is in line with the ERC165 interfaces design choice towards EVM compatibility. See the [Introspection docs](./Introspection.md) for more info - -- `IERC721_Receiver` compliant contracts (`ERC721_Holder`) must support ERC165 by registering the `IERC721_Receiver` selector id in its constructor and exposing the `supportsInterface` method. In doing so, recipient contracts (both accounts and non-accounts) can be verified that they support ERC721 transfers - -- `ERC721_Enumerable` tracks the total number of tokens with the `all_tokens` and `all_tokens_len` storage variables mimicking the array of the Solidity implementation. - -## Usage +* `tokenURI` returns a felt representation of the queried token's URI. +The EIP721 standard, however, states that the return value should be of type string. +If a token's URI is not set, the returned value is `0`. +Note that URIs cannot exceed 31 characters. +See <> +* ``interface_id``s are hardcoded and initialized by the constructor. +The hardcoded values derive from Solidity's selector calculations. +See <> +* `safeTransferFrom` can only be expressed as a single function in Cairo as opposed to the two functions declared in EIP721. +The difference between both functions consists of accepting `data` as an argument. +Because function overloading is currently not possible in Cairo, `safeTransferFrom` by default accepts the `data` argument. +If `data` is not used, simply insert `0`. +* `safeTransferFrom` is specified such that the optional `data` argument should be of type bytes. +In Solidity, this means a dynamically-sized array. +To be as close as possible to the standard, it accepts a dynamic array of felts. +In Cairo, arrays are expressed with the array length preceding the actual array; +hence, the method accepts `data_len` and `data` respectively as types `felt` and `felt*` +* `ERC165.register_interface` allows contracts to set and communicate which interfaces they support. +This follows OpenZeppelin's https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/introspection/ERC165Storage.sol[ERC165Storage] +* `IERC721_Receiver` compliant contracts (`ERC721_Holder`) return a hardcoded selector id according to EVM selectors, since selectors are calculated differently in Cairo. +This is in line with the ERC165 interfaces design choice towards EVM compatibility. +See the xref:./Introspection.adoc[Introspection docs] for more info +* `IERC721_Receiver` compliant contracts (`ERC721_Holder`) must support ERC165 by registering the `IERC721_Receiver` selector id in its constructor and exposing the `supportsInterface` method. +In doing so, recipient contracts (both accounts and non-accounts) can be verified that they support ERC721 transfers +* `ERC721_Enumerable` tracks the total number of tokens with the `all_tokens` and `all_tokens_len` storage variables mimicking the array of the Solidity implementation. + +== Usage Use cases go from artwork, digital collectibles, physical property, and many more. -To show a standard use case, we'll use the `ERC721_Mintable` preset which allows for only the owner to `mint` and `burn` tokens. To create a token you need to first deploy both Account and ERC721 contracts respectively. As most StarkNet contracts, ERC721 expects to be called by another contract and it identifies it through `get_caller_address` (analogous to Solidity's `this.address`). This is why we need an Account contract to interact with it. +To show a standard use case, we'll use the `ERC721_Mintable` preset which allows for only the owner to `mint` and `burn` tokens. +To create a token you need to first deploy both Account and ERC721 contracts respectively. +As most StarkNet contracts, ERC721 expects to be called by another contract and it identifies it through `get_caller_address` (analogous to Solidity's `this.address`). +This is why we need an Account contract to interact with it. Considering that the ERC721 constructor method looks like this: -```python +[,python] +---- func constructor( name: felt, # Token name as Cairo short string symbol: felt, # Token symbol as Cairo short string owner: felt # Address designated as the contract owner ): -``` +---- Deployment of both contracts looks like this: -```python +[,python] +---- account = await starknet.deploy( "contracts/Account.cairo", constructor_calldata=[signer.public_key] @@ -142,11 +164,12 @@ erc721 = await starknet.deploy( account.contract_address # owner ] ) -``` +---- To mint a non-fungible token, send a transaction like this: -```python +[,python] +---- signer = MockSigner(PRIVATE_KEY) tokenId = uint(1) @@ -156,27 +179,38 @@ await signer.send_transaction( *tokenId ] ) -``` +---- -### Token Transfers +=== Token Transfers -This library includes `transferFrom` and `safeTransferFrom` to transfer NFTs. If using `transferFrom`, **the caller is responsible to confirm that the recipient is capable of receiving NFTs or else they may be permanently lost.** +This library includes `transferFrom` and `safeTransferFrom` to transfer NFTs. +If using `transferFrom`, *the caller is responsible to confirm that the recipient is capable of receiving NFTs or else they may be permanently lost.* The `safeTransferFrom` method incorporates the following conditional logic: -1. if the calling address is an account contract, the token transfer will behave as if `transferFrom` was called -2. if the calling address is not an account contract, the safe function will check that the contract supports ERC721 tokens +. if the calling address is an account contract, the token transfer will behave as if `transferFrom` was called +. if the calling address is not an account contract, the safe function will check that the contract supports ERC721 tokens -The current implementation of `safeTansferFrom` checks for `onERC721Received` and requires that the recipient contract supports ERC165 and exposes the `supportsInterface` method. See [ERC721Received](#erc721received) +The current implementation of `safeTansferFrom` checks for `onERC721Received` and requires that the recipient contract supports ERC165 and exposes the `supportsInterface` method. +See <> -### Interpreting ERC721 URIs +=== Interpreting ERC721 URIs -Token URIs in Cairo are stored as single field elements. Each field element equates to 252-bits (or 31.5 bytes) which means that a token's URI can be no longer than 31 characters. -> Note that storing the URI as an array of felts was considered to accommodate larger strings. While this approach is more flexible regarding URIs, a returned array further deviates from the standard set in [EIP721](https://eips.ethereum.org/EIPS/eip-721). Therefore, this library's ERC721 implementation sets URIs as a single field element. +Token URIs in Cairo are stored as single field elements. +Each field element equates to 252-bits (or 31.5 bytes) which means that a token's URI can be no longer than 31 characters. -The `utils.py` module includes utility methods for converting to/from Cairo field elements. To properly interpret a URI from ERC721, simply trim the null bytes and decode the remaining bits as an ASCII string. For example: +____ +Note that storing the URI as an array of felts was considered to accommodate larger strings. +While this approach is more flexible regarding URIs, a returned array further deviates from the standard set in https://eips.ethereum.org/EIPS/eip-721[EIP721]. +Therefore, this library's ERC721 implementation sets URIs as a single field element. +____ -```python +The `utils.py` module includes utility methods for converting to/from Cairo field elements. +To properly interpret a URI from ERC721, simply trim the null bytes and decode the remaining bits as an ASCII string. +For example: + +[,python] +---- # HELPER METHODS def str_to_felt(text): b_text = bytes(text, 'ascii') @@ -196,20 +230,28 @@ await signer.send_transaction( felt_uri = await erc721.tokenURI(first_token_id).call() string_uri = felt_to_str(felt_uri) -``` +---- -### ERC721Received +=== ERC721Received -In order to be sure a contract can safely accept ERC721 tokens, said contract must implement the `ERC721_Receiver` interface (as expressed in the EIP721 specification). Methods such as `safeTransferFrom` and `safeMint` call the recipient contract's `onERC721Received` method. If the contract fails to return the correct magic value, the transaction fails. +In order to be sure a contract can safely accept ERC721 tokens, said contract must implement the `ERC721_Receiver` interface (as expressed in the EIP721 specification). +Methods such as `safeTransferFrom` and `safeMint` call the recipient contract's `onERC721Received` method. +If the contract fails to return the correct magic value, the transaction fails. -StarkNet contracts that support safe transfers, however, must also support [ERC165](./Introspection.md#erc165) and include `supportsInterface` as proposed in [#100](https://github.com/OpenZeppelin/cairo-contracts/discussions/100). `safeTransferFrom` requires a means of differentiating between account and non-account contracts. Currently, StarkNet does not support error handling from the contract level; -therefore, the current ERC721 implementation requires that all contracts that support safe ERC721 transfers (both accounts and non-accounts) include the `supportsInterface` method. Further, `supportsInterface` should return `TRUE` if the recipient contract supports the `IERC721_Receiver` magic value `0x150b7a02` (which invokes `onERC721Received`). If the recipient contract supports the `IAccount` magic value `0x50b70dcb`, `supportsInterface` should return `TRUE`. Otherwise, `safeTransferFrom` should fail. +StarkNet contracts that support safe transfers, however, must also support link:./Introspection.md#erc165[ERC165] and include `supportsInterface` as proposed in https://github.com/OpenZeppelin/cairo-contracts/discussions/100[#100]. +`safeTransferFrom` requires a means of differentiating between account and non-account contracts. +Currently, StarkNet does not support error handling from the contract level; +therefore, the current ERC721 implementation requires that all contracts that support safe ERC721 transfers (both accounts and non-accounts) include the `supportsInterface` method. +Further, `supportsInterface` should return `TRUE` if the recipient contract supports the `IERC721_Receiver` magic value `0x150b7a02` (which invokes `onERC721Received`). +If the recipient contract supports the `IAccount` magic value `0x50b70dcb`, `supportsInterface` should return `TRUE`. +Otherwise, `safeTransferFrom` should fail. -#### IERC721_Receiver +==== IERC721_Receiver Interface for any contract that wants to support safeTransfers from ERC721 asset contracts. -```cairo +[,cairo] +---- @contract_interface namespace IERC721_Receiver: func onERC721Received( @@ -221,35 +263,47 @@ namespace IERC721_Receiver: ) -> (selector: felt): end end -``` +---- -### Supporting Interfaces +=== Supporting Interfaces -In order to ensure EVM/StarkNet compatibility, this ERC721 implementation does not calculate interface identifiers. Instead, the interface IDs are hardcoded from their EVM calculations. On the EVM, the interface ID is calculated from the selector's first four bytes of the hash of the function's signature while Cairo selectors are 252 bytes long. Due to this difference, hardcoding EVM's already-calculated interface IDs is the most consistent approach to both follow the EIP165 standard and EVM compatibility. +In order to ensure EVM/StarkNet compatibility, this ERC721 implementation does not calculate interface identifiers. +Instead, the interface IDs are hardcoded from their EVM calculations. +On the EVM, the interface ID is calculated from the selector's first four bytes of the hash of the function's signature while Cairo selectors are 252 bytes long. +Due to this difference, hardcoding EVM's already-calculated interface IDs is the most consistent approach to both follow the EIP165 standard and EVM compatibility. -Further, this implementation stores supported interfaces in a mapping (similar to OpenZeppelin's [ERC165Storage](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/introspection/ERC165Storage.sol)). +Further, this implementation stores supported interfaces in a mapping (similar to OpenZeppelin's https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/introspection/ERC165Storage.sol[ERC165Storage]). -### Ready-to-Use Presets +=== Ready-to-Use Presets -ERC721 presets have been created to allow for quick deployments as-is. To be as explicit as possible, each preset includes the additional features they offer in the contract name. For example: +ERC721 presets have been created to allow for quick deployments as-is. +To be as explicit as possible, each preset includes the additional features they offer in the contract name. +For example: -- `ERC721_Mintable_Burnable` includes `mint` and `burn` -- `ERC721_Mintable_Pausable` includes `mint`, `pause`, and `unpause` -- `ERC721_Enumerable_Mintable_Burnable` includes `mint`, `burn`, and [IERC721_Enumerable](#ierc721_enumerable) methods +* `ERC721_Mintable_Burnable` includes `mint` and `burn` +* `ERC721_Mintable_Pausable` includes `mint`, `pause`, and `unpause` +* `ERC721_Enumerable_Mintable_Burnable` includes `mint`, `burn`, and <> methods -Ready-to-use presets are a great option for testing and prototyping. See [Presets](#presets). +Ready-to-use presets are a great option for testing and prototyping. +See <>. -## Extensibility +== Extensibility -Following the [contracts extensibility pattern](Extensibility.md), this implementation is set up to include all ERC721 related storage and business logic under a namespace. Developers should be mindful of manually exposing the required methods from the namespace to comply with the standard interface. This is already done in the [preset contracts](#presets); however, additional functionality can be added. For instance, you could: +Following the xref:Extensibility.adoc[contracts extensibility pattern], this implementation is set up to include all ERC721 related storage and business logic under a namespace. +Developers should be mindful of manually exposing the required methods from the namespace to comply with the standard interface. +This is already done in the <>; +however, additional functionality can be added. +For instance, you could: -- Implement a pausing mechanism -- Add roles such as _owner_ or _minter_ -- Modify the `transferFrom` function to mimic the [`_beforeTokenTransfer` and `_afterTokenTransfer` hooks](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol#L335) +* Implement a pausing mechanism +* Add roles such as _owner_ or _minter_ +* Modify the `transferFrom` function to mimic the https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol#L335[`_beforeTokenTransfer` and `_afterTokenTransfer` hooks] -Just be sure that the exposed `external` methods invoke their imported function logic a la `approve` invokes `ERC721.approve`. As an example, see below. +Just be sure that the exposed `external` methods invoke their imported function logic a la `approve` invokes `ERC721.approve`. +As an example, see below. -```python +[,python] +---- from openzeppelin.token.erc721.library import ERC721 @external @@ -261,41 +315,46 @@ func approve{ ERC721.approve(to, tokenId) return() end +---- -``` - -## Presets +== Presets -The following contract presets are ready to deploy and can be used as-is for quick prototyping and testing. Each preset includes a contract owner, which is set in the `constructor`, to offer simple access control on sensitive methods such as `mint` and `burn`. +The following contract presets are ready to deploy and can be used as-is for quick prototyping and testing. +Each preset includes a contract owner, which is set in the `constructor`, to offer simple access control on sensitive methods such as `mint` and `burn`. -### ERC721_Mintable_Burnable +=== ERC721_Mintable_Burnable -The `ERC721_Mintable_Burnable` preset offers a quick and easy setup for creating NFTs. The contract owner can create tokens with `mint`, whereas token owners can destroy their tokens with `burn`. +The `ERC721_Mintable_Burnable` preset offers a quick and easy setup for creating NFTs. +The contract owner can create tokens with `mint`, whereas token owners can destroy their tokens with `burn`. -### ERC721_Mintable_Pausable +=== ERC721_Mintable_Pausable -The `ERC721_Mintable_Pausable` preset creates a contract with pausable token transfers and minting capabilities. This preset proves useful for scenarios such as preventing trades until the end of an evaluation period and having an emergency switch for freezing all token transfers in the event of a large bug. In this preset, only the contract owner can `mint`, `pause`, and `unpause`. +The `ERC721_Mintable_Pausable` preset creates a contract with pausable token transfers and minting capabilities. +This preset proves useful for scenarios such as preventing trades until the end of an evaluation period and having an emergency switch for freezing all token transfers in the event of a large bug. +In this preset, only the contract owner can `mint`, `pause`, and `unpause`. -### ERC721_Enumerable_Mintable_Burnable +=== ERC721_Enumerable_Mintable_Burnable -The `ERC721_Enumerable_Mintable_Burnable` preset adds enumerability of all the token ids in the contract as well as all token ids owned by each account. This allows contracts to publish its full list of NFTs and make them discoverable. +The `ERC721_Enumerable_Mintable_Burnable` preset adds enumerability of all the token ids in the contract as well as all token ids owned by each account. +This allows contracts to publish its full list of NFTs and make them discoverable. In regard to implementation, contracts should expose the following view methods: -- `ERC721_Enumerable.total_supply` -- `ERC721_Enumerable.token_by_index` -- `ERC721_Enumerable.token_of_owner_by_index` +* `ERC721_Enumerable.total_supply` +* `ERC721_Enumerable.token_by_index` +* `ERC721_Enumerable.token_of_owner_by_index` In order for the tokens to be correctly indexed, the contract should also use the following methods (which supercede some of the base `ERC721` methods): -- `ERC721_Enumerable.transfer_from` -- `ERC721_Enumerable.safe_transfer_from` -- `ERC721_Enumerable._mint` -- `ERC721_Enumerable._burn` +* `ERC721_Enumerable.transfer_from` +* `ERC721_Enumerable.safe_transfer_from` +* `ERC721_Enumerable._mint` +* `ERC721_Enumerable._burn` -#### IERC721_Enumerable +==== IERC721_Enumerable -```cairo +[,cairo] +---- @contract_interface namespace IERC721_Enumerable: func totalSupply() -> (totalSupply: Uint256): @@ -307,17 +366,20 @@ namespace IERC721_Enumerable: func tokenOfOwnerByIndex(owner: felt, index: Uint256) -> (tokenId: Uint256): end end -``` +---- -### ERC721_Metadata +=== ERC721_Metadata The `ERC721_Metadata` extension allows your smart contract to be interrogated for its name and for details about the assets which your NFTs represent. -We follow OpenZeppelin's Solidity approach of integrating the Metadata methods `name`, `symbol`, and `tokenURI` into all ERC721 implementations. If preferred, a contract can be created that does not import the Metadata methods from the `ERC721_base` library. Note that the `IERC721_Metadata` interface id should be removed from the constructor as well. +We follow OpenZeppelin's Solidity approach of integrating the Metadata methods `name`, `symbol`, and `tokenURI` into all ERC721 implementations. +If preferred, a contract can be created that does not import the Metadata methods from the `ERC721_base` library. +Note that the `IERC721_Metadata` interface id should be removed from the constructor as well. -#### IERC721_Metadata +==== IERC721_Metadata -```cairo +[,cairo] +---- @contract_interface namespace IERC721_Metadata: func name() -> (name: felt): @@ -329,23 +391,26 @@ namespace IERC721_Metadata: func tokenURI(tokenId: Uint256) -> (tokenURI: felt): end end -``` +---- -## Utilities +== Utilities -### ERC721_Holder +=== ERC721_Holder Implementation of the `IERC721Receiver` interface. -Accepts all token transfers. Make sure the contract is able to use its token with `IERC721.safeTransferFrom`, `IERC721.approve` or `IERC721.setApprovalForAll`. +Accepts all token transfers. +Make sure the contract is able to use its token with `IERC721.safeTransferFrom`, `IERC721.approve` or `IERC721.setApprovalForAll`. -Also utilizes the ERC165 method `supportsInterface` to determine if the contract is an account. See [ERC721Received](#erc721received) +Also utilizes the ERC165 method `supportsInterface` to determine if the contract is an account. +See <> -## API Specification +== API Specification -### IERC721 API +=== IERC721 API -```cairo +[,cairo] +---- func balanceOf(owner: felt) -> (balance: Uint256): end @@ -375,188 +440,207 @@ end func isApprovedForAll(owner: felt, operator: felt) -> (isApproved: felt): end +---- -``` - -#### `balanceOf` +==== `balanceOf` -Returns the number of tokens in `owner`'s account. +Returns the number of tokens in ``owner``'s account. Parameters: -```cairo +[,cairo] +---- owner: felt -``` +---- Returns: -```cairo +[,cairo] +---- balance: Uint256 -``` +---- -#### `ownerOf` +==== `ownerOf` Returns the owner of the `tokenId` token. Parameters: -```cairo +[,cairo] +---- tokenId: Uint256 -``` +---- Returns: -```cairo +[,cairo] +---- owner: felt -``` +---- -#### `safeTransferFrom` +==== `safeTransferFrom` -Safely transfers `tokenId` token from `from_` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. For information regarding how contracts communicate their awareness of the ERC721 protocol, see [ERC721Received](#erc721received). +Safely transfers `tokenId` token from `from_` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. +For information regarding how contracts communicate their awareness of the ERC721 protocol, see <>. -Emits a [Transfer](#transfer-event) event. +Emits a <> event. Parameters: -```cairo +[,cairo] +---- from_: felt to: felt tokenId: Uint256 data_len: felt data: felt* -``` +---- Returns: None. -#### `transferFrom` +==== `transferFrom` -Transfers `tokenId` token from `from_` to `to`. **The caller is responsible to confirm that `to` is capable of receiving NFTs or else they may be permanently lost**. +Transfers `tokenId` token from `from_` to `to`. +*The caller is responsible to confirm that `to` is capable of receiving NFTs or else they may be permanently lost*. -Emits a [Transfer](#transfer-event) event. +Emits a <> event. Parameters: -```cairo +[,cairo] +---- from_: felt to: felt tokenId: Uint256 -``` +---- Returns: None. -#### `approve` +==== `approve` -Gives permission to `to` to transfer `tokenId` token to another account. The approval is cleared when the token is transferred. +Gives permission to `to` to transfer `tokenId` token to another account. +The approval is cleared when the token is transferred. -Emits an [Approval](#approval-event) event. +Emits an <> event. Parameters: -```cairo +[,cairo] +---- to: felt tokenId: Uint256 -``` +---- Returns: None. -#### `getApproved` +==== `getApproved` Returns the account approved for `tokenId` token. Parameters: -```cairo +[,cairo] +---- tokenId: Uint256 -``` +---- Returns: -```cairo +[,cairo] +---- operator: felt -``` +---- -#### `setApprovalForAll` +==== `setApprovalForAll` -Approve or remove `operator` as an operator for the caller. Operators can call `transferFrom` or `safeTransferFrom` for any token owned by the caller. +Approve or remove `operator` as an operator for the caller. +Operators can call `transferFrom` or `safeTransferFrom` for any token owned by the caller. -Emits an [ApprovalForAll](#approvalforall-event) event. +Emits an <> event. Parameters: -```cairo +[,cairo] +---- operator: felt -``` +---- Returns: None. -#### `isApprovedForAll` +==== `isApprovedForAll` Returns if the `operator` is allowed to manage all of the assets of `owner`. Parameters: -```cairo +[,cairo] +---- owner: felt operator: felt -``` +---- Returns: -```cairo +[,cairo] +---- isApproved: felt -``` +---- -### Events +=== Events -#### `Approval (Event)` +==== `Approval (Event)` Emitted when `owner` enables `approved` to manage the `tokenId` token. Parameters: -```cairo +[,cairo] +---- owner: felt approved: felt tokenId: Uint256 -``` +---- -#### `ApprovalForAll (Event)` +==== `ApprovalForAll (Event)` Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. Parameters: -```cairo +[,cairo] +---- owner: felt operator: felt approved: felt -``` +---- -#### `Transfer (Event)` +==== `Transfer (Event)` Emitted when `tokenId` token is transferred from `from_` to `to`. Parameters: -```cairo +[,cairo] +---- from_: felt to: felt tokenId: Uint256 -``` +---- ---- +''' -### IERC721_Metadata API +=== IERC721_Metadata API -```cairo +[,cairo] +---- func name() -> (name: felt): end @@ -565,9 +649,9 @@ end func tokenURI(tokenId: Uint256) -> (tokenURI: felt): end -``` +---- -#### `name` +==== `name` Returns the token collection name. @@ -577,11 +661,12 @@ None. Returns: -```cairo +[,cairo] +---- name: felt -``` +---- -#### `symbol` +==== `symbol` Returns the token collection symbol. @@ -591,31 +676,36 @@ None. Returns: -```cairo +[,cairo] +---- symbol: felt -``` +---- -#### `tokenURI` +==== `tokenURI` -Returns the Uniform Resource Identifier (URI) for `tokenID` token. If the URI is not set for the `tokenId`, the return value will be `0`. +Returns the Uniform Resource Identifier (URI) for `tokenID` token. +If the URI is not set for the `tokenId`, the return value will be `0`. Parameters: -```cairo +[,cairo] +---- tokenId: Uint256 -``` +---- Returns: -```cairo +[,cairo] +---- tokenURI: felt -``` +---- ---- +''' -### IERC721_Enumerable API +=== IERC721_Enumerable API -```cairo +[,cairo] +---- func totalSupply() -> (totalSupply: Uint256): end @@ -625,9 +715,9 @@ end func tokenOfOwnerByIndex(owner: felt, index: Uint256) -> (tokenId: Uint256): end -``` +---- -#### `totalSupply` +==== `totalSupply` Returns the total amount of tokens stored by the contract. @@ -635,48 +725,56 @@ Parameters: None Returns: -```cairo +[,cairo] +---- totalSupply: Uint256 -``` +---- -#### `tokenByIndex` +==== `tokenByIndex` -Returns a token ID owned by `owner` at a given `index` of its token list. Use along with [balanceOf](#balanceof) to enumerate all of `owner`'s tokens. +Returns a token ID owned by `owner` at a given `index` of its token list. +Use along with <> to enumerate all of ``owner``'s tokens. Parameters: -```cairo +[,cairo] +---- index: Uint256 -``` +---- Returns: -```cairo +[,cairo] +---- tokenId: Uint256 -``` +---- -#### `tokenOfOwnerByIndex` +==== `tokenOfOwnerByIndex` -Returns a token ID at a given `index` of all the tokens stored by the contract. Use along with [totalSupply](#totalsupply) to enumerate all tokens. +Returns a token ID at a given `index` of all the tokens stored by the contract. +Use along with <> to enumerate all tokens. Parameters: -```cairo +[,cairo] +---- owner: felt index: Uint256 -``` +---- Returns: -```cairo +[,cairo] +---- tokenId: Uint256 -``` +---- ---- +''' -### IERC721_Receiver API +=== IERC721_Receiver API -```cairo +[,cairo] +---- func onERC721Received( operator: felt, from_: felt, @@ -685,24 +783,26 @@ func onERC721Received( data: felt* ) -> (selector: felt): end -``` +---- -#### `onERC721Received` +==== `onERC721Received` Whenever an IERC721 `tokenId` token is transferred to this non-account contract via `safeTransferFrom` by `operator` from `from_`, this function is called. Parameters: -```cairo +[,cairo] +---- operator: felt from_: felt tokenId: Uint256 data_len: felt data: felt* -``` +---- Returns: -```cairo +[,cairo] +---- selector: felt -``` +---- diff --git a/docs/modules/ROOT/pages/extensibility.adoc b/docs/modules/ROOT/pages/extensibility.adoc index a743efa6e..147fad22c 100644 --- a/docs/modules/ROOT/pages/extensibility.adoc +++ b/docs/modules/ROOT/pages/extensibility.adoc @@ -1,92 +1,114 @@ -# Extensibility += Extensibility -> Expect this pattern to evolve (as it has already done) or even disappear if [proper extensibility features](https://community.starknet.io/t/contract-extensibility-pattern/210/11?u=martriay) are implemented into Cairo. +____ +Expect this pattern to evolve (as it has already done) or even disappear if https://community.starknet.io/t/contract-extensibility-pattern/210/11?u=martriay[proper extensibility features] are implemented into Cairo. +____ -* [The extensibility problem](#the-extensibility-problem) -* [The pattern ™️](#the-pattern) - * [Libraries](#libraries) - * [Contracts](#contracts) -* [Presets](#presets) -* [Function names and coding style](#function-names-and-coding-style) -* [Emulating hooks](#emulating-hooks) +* <> +* <> + ** <> + ** <> +* <> +* <> +* <> -## The extensibility problem +== The extensibility problem -Smart contract development is a critical task. As with all software development, it is error prone; but unlike most scenarios, a bug can result in major losses for organizations as well as individuals. Therefore writing complex smart contracts is a delicate task. +Smart contract development is a critical task. +As with all software development, it is error prone; +but unlike most scenarios, a bug can result in major losses for organizations as well as individuals. +Therefore writing complex smart contracts is a delicate task. -One of the best approaches to minimize introducing bugs is to reuse existing, battle-tested code, a.k.a. using libraries. But code reutilization in StarkNet’s smart contracts is not easy: +One of the best approaches to minimize introducing bugs is to reuse existing, battle-tested code, a.k.a. +using libraries. +But code reutilization in StarkNet's smart contracts is not easy: * Cairo has no explicit smart contract extension mechanisms such as inheritance or composability * Using imports for modularity can result in clashes (more so given that arguments are not part of the selector), and lack of overrides or aliasing leaves no way to resolve them -* Any `@external` function defined in an imported module will be automatically re-exposed by the importer (i.e. the smart contract) +* Any `@external` function defined in an imported module will be automatically re-exposed by the importer (i.e. +the smart contract) To overcome these problems, this project builds on the following guidelines™. -## The pattern +== The pattern -The idea is to have two types of Cairo modules: libraries and contracts. Libraries define reusable logic and storage variables which can then be extended and exposed by contracts. Contracts can be deployed, libraries cannot. +The idea is to have two types of Cairo modules: libraries and contracts. +Libraries define reusable logic and storage variables which can then be extended and exposed by contracts. +Contracts can be deployed, libraries cannot. To minimize risk, boilerplate, and avoid function naming clashes, we follow these rules: -### Libraries +=== Libraries Considering the following types of functions: * `private`: private to a library, not meant to be used outside the module or imported * `public`: part of the public API of a library -* `internal`: subset of `public` that is either discouraged or potentially unsafe (e.g. `_transfer` on ERC20) -* `external`: subset of `public` that is ready to be exported as-is by contracts (e.g. `transfer` on ERC20) +* `internal`: subset of `public` that is either discouraged or potentially unsafe (e.g. +`_transfer` on ERC20) +* `external`: subset of `public` that is ready to be exported as-is by contracts (e.g. +`transfer` on ERC20) * `storage`: storage variable functions Then: * Must implement `public` and `external` functions under a namespace * Must implement `private` functions outside the namespace to avoid exposing them -* Must prefix `internal` functions with an underscore (e.g. `ERC20._mint`) -* Must not prefix `external` functions with an underscore (e.g. `ERC20.transfer`) -* Must prefix `storage` functions with the name of the namespace to prevent clashing with other libraries (e.g. `ERC20_balances`) +* Must prefix `internal` functions with an underscore (e.g. +`ERC20._mint`) +* Must not prefix `external` functions with an underscore (e.g. +`ERC20.transfer`) +* Must prefix `storage` functions with the name of the namespace to prevent clashing with other libraries (e.g. +`ERC20_balances`) * Must not implement any `@external`, `@view`, or `@constructor` functions * Can implement initializers (never as `@constructor` or `@external`) * Must not call initializers on any function -### Contracts +=== Contracts * Can import from libraries * Should implement `@external` functions if needed * Should implement a `@constructor` function that calls initializers * Must not call initializers in any function beside the constructor -Note that since initializers will never be marked as `@external` and they won’t be called from anywhere but the constructor, there’s no risk of re-initialization after deployment. It’s up to the library developers not to make initializers interdependent to avoid weird dependency paths that may lead to double construction of libraries. +Note that since initializers will never be marked as `@external` and they won't be called from anywhere but the constructor, there's no risk of re-initialization after deployment. +It's up to the library developers not to make initializers interdependent to avoid weird dependency paths that may lead to double construction of libraries. -## Presets +== Presets -Presets are pre-written contracts that extend from our library of contracts. They can be deployed as-is or used as templates for customization. +Presets are pre-written contracts that extend from our library of contracts. +They can be deployed as-is or used as templates for customization. Some presets are: -* [Account](../src/openzeppelin/account/Account.cairo) -* [ERC165](../tests/mocks/ERC165.cairo) -* [ERC20_Mintable](../src/openzeppelin/token/erc20/ERC20_Mintable.cairo) -* [ERC20_Pausable](../src/openzeppelin/token/erc20/ERC20_Pausable.cairo) -* [ERC20_Upgradeable](../src/openzeppelin/token/erc20/ERC20_Upgradeable.cairo) -* [ERC20](../src/openzeppelin/token/erc20/ERC20.cairo) -* [ERC721_Mintable_Burnable](../src/openzeppelin/token/erc721/ERC721_Mintable_Burnable.cairo) -* [ERC721_Mintable_Pausable](../src/openzeppelin/token/erc721/ERC721_Mintable_Pausable.cairo) -* [ERC721_Enumerable_Mintable_Burnable](../src/openzeppelin/token/erc721_enumerable/ERC721_Enumerable_Mintable_Burnable.cairo) +* link:../src/openzeppelin/account/Account.cairo[Account] +* link:../tests/mocks/ERC165.cairo[ERC165] +* link:../src/openzeppelin/token/erc20/ERC20_Mintable.cairo[ERC20_Mintable] +* link:../src/openzeppelin/token/erc20/ERC20_Pausable.cairo[ERC20_Pausable] +* link:../src/openzeppelin/token/erc20/ERC20_Upgradeable.cairo[ERC20_Upgradeable] +* link:../src/openzeppelin/token/erc20/ERC20.cairo[ERC20] +* link:../src/openzeppelin/token/erc721/ERC721_Mintable_Burnable.cairo[ERC721_Mintable_Burnable] +* link:../src/openzeppelin/token/erc721/ERC721_Mintable_Pausable.cairo[ERC721_Mintable_Pausable] +* link:../src/openzeppelin/token/erc721_enumerable/ERC721_Enumerable_Mintable_Burnable.cairo[ERC721_Enumerable_Mintable_Burnable] -## Function names and coding style +== Function names and coding style -* Following Cairo's programming style, we use `snake_case` for library APIs (e.g. `ERC20.transfer_from`, `ERC721.safe_transfer_from`). -* But for standard EVM ecosystem compatibility, we implement external functions in contracts using `camelCase` (e.g. `transferFrom` in a ERC20 contract). -* Guard functions such as the so-called "only owner" are prefixed with `assert_` (e.g. `Ownable.assert_only_owner`). +* Following Cairo's programming style, we use `snake_case` for library APIs (e.g. +`ERC20.transfer_from`, `ERC721.safe_transfer_from`). +* But for standard EVM ecosystem compatibility, we implement external functions in contracts using `camelCase` (e.g. +`transferFrom` in a ERC20 contract). +* Guard functions such as the so-called "only owner" are prefixed with `assert_` (e.g. +`Ownable.assert_only_owner`). -## Emulating hooks +== Emulating hooks -Unlike the Solidity version of [OpenZeppelin Contracts](https://github.com/OpenZeppelin/openzeppelin-contracts), this library does not implement [hooks](https://docs.openzeppelin.com/contracts/4.x/extending-contracts#using-hooks). The main reason being that Cairo does not support overriding functions. +Unlike the Solidity version of https://github.com/OpenZeppelin/openzeppelin-contracts[OpenZeppelin Contracts], this library does not implement https://docs.openzeppelin.com/contracts/4.x/extending-contracts#using-hooks[hooks]. +The main reason being that Cairo does not support overriding functions. This is what a hook looks like in Solidity: -```js +[,js] +---- abstract contract ERC20Pausable is ERC20, Pausable { function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override { super._beforeTokenTransfer(from, to, amount); @@ -94,14 +116,16 @@ abstract contract ERC20Pausable is ERC20, Pausable { require(!paused(), "ERC20Pausable: token transfer while paused"); } } -``` +---- -Instead, the extensibility pattern allows us to simply extend the library implementation of a function (namely `transfer`) by adding lines before or after calling it. This way, we can get away with: +Instead, the extensibility pattern allows us to simply extend the library implementation of a function (namely `transfer`) by adding lines before or after calling it. +This way, we can get away with: -```python +[,python] +---- @external func transfer{ - syscall_ptr : felt*, + syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr }(recipient: felt, amount: Uint256) -> (success: felt): @@ -109,4 +133,4 @@ func transfer{ ERC20.transfer(recipient, amount) return (TRUE) end -``` +---- diff --git a/docs/modules/ROOT/pages/introspection.adoc b/docs/modules/ROOT/pages/introspection.adoc index 042d4e48f..ec1bb0b64 100644 --- a/docs/modules/ROOT/pages/introspection.adoc +++ b/docs/modules/ROOT/pages/introspection.adoc @@ -1,37 +1,52 @@ -# Introspection += Introspection -> Expect this module to evolve. +____ +Expect this module to evolve. +____ -## Table of Contents +== Table of Contents -* [ERC165](#erc165) - * [Interface calculations](#interface-calculations) - * [Registering interfaces](#registering-interfaces) - * [Querying interfaces](#querying-interfaces) - * [IERC165](#ierc165) - * [IERC165 API Specification](#ierc165-api-specification) - * [`supportsInterface`](#supportsinterface) - * [ERC165 Library Functions](#erc165-library-functions) - * [`supports_interface`](#supportsinterface2) - * [`register_interface`](#register_interface) +* <> + ** <> + ** <> + ** <> + ** <> + ** <> + *** <> + ** <> + *** <> + *** <> -## ERC165 +== ERC165 -The ERC165 standard allows smart contracts to exercise [type introspection](https://en.wikipedia.org/wiki/Type_introspection) on other contracts, that is, examining which functions can be called on them. This is usually referred to as a contract’s interface. +The ERC165 standard allows smart contracts to exercise https://en.wikipedia.org/wiki/Type_introspection[type introspection] on other contracts, that is, examining which functions can be called on them. +This is usually referred to as a contract's interface. -Cairo contracts, like Ethereum contracts, have no native concept of an interface, so applications must usually simply trust they are not making an incorrect call. For trusted setups this is a non-issue, but often unknown and untrusted third-party addresses need to be interacted with. There may even not be any direct calls to them! (e.g. ERC20 tokens may be sent to a contract that lacks a way to transfer them out of it, locking them forever). In these cases, a contract declaring its interface can be very helpful in preventing errors. +Cairo contracts, like Ethereum contracts, have no native concept of an interface, so applications must usually simply trust they are not making an incorrect call. +For trusted setups this is a non-issue, but often unknown and untrusted third-party addresses need to be interacted with. +There may even not be any direct calls to them! +(e.g. +ERC20 tokens may be sent to a contract that lacks a way to transfer them out of it, locking them forever). +In these cases, a contract declaring its interface can be very helpful in preventing errors. -It should be noted that the [constants library](../src/openzeppelin/utils/constants.cairo) includes constant variables referencing all of the interface ids used in these contracts. This allows for more legible code i.e. using `IERC165_ID` instead of `0x01ffc9a7`. +It should be noted that the link:../src/openzeppelin/utils/constants.cairo[constants library] includes constant variables referencing all of the interface ids used in these contracts. +This allows for more legible code i.e. +using `IERC165_ID` instead of `0x01ffc9a7`. -### Interface calculations +=== Interface calculations -In order to ensure EVM/StarkNet compatibility, interface identifiers are not calculated on StarkNet. Instead, the interface IDs are hardcoded from their EVM calculations. On the EVM, the interface ID is calculated from the selector's first four bytes of the hash of the function's signature while Cairo selectors are 252 bytes long. Due to this difference, hardcoding EVM's already-calculated interface IDs is the most consistent approach to both follow the EIP165 standard and EVM compatibility. +In order to ensure EVM/StarkNet compatibility, interface identifiers are not calculated on StarkNet. +Instead, the interface IDs are hardcoded from their EVM calculations. +On the EVM, the interface ID is calculated from the selector's first four bytes of the hash of the function's signature while Cairo selectors are 252 bytes long. +Due to this difference, hardcoding EVM's already-calculated interface IDs is the most consistent approach to both follow the EIP165 standard and EVM compatibility. -### Registering interfaces +=== Registering interfaces -For a contract to declare its support for a given interface, the contract should import the ERC165 library and register its support. It's recommended to register interface support upon contract deployment through a constructor either directly or indirectly (as an initializer) like this: +For a contract to declare its support for a given interface, the contract should import the ERC165 library and register its support. +It's recommended to register interface support upon contract deployment through a constructor either directly or indirectly (as an initializer) like this: -```cairo +[,cairo] +---- from openzeppelin.introspection.ERC165 import ERC165 INTERFACE_ID = 0x12345678 @@ -45,13 +60,15 @@ func constructor{ ERC165.register_interface(INTERFACE_ID) return () end -``` +---- -### Querying interfaces +=== Querying interfaces -To query a target contract's support for an interface, the querying contract should call `supportsInterface` through IERC165 with the target contract's address and the queried interface id. Here's an example: +To query a target contract's support for an interface, the querying contract should call `supportsInterface` through IERC165 with the target contract's address and the queried interface id. +Here's an example: -```cairo +[,cairo] +---- from openzeppelin.introspection.IERC165 import IERC165 INTERFACE_ID = 0x12345678 @@ -66,78 +83,91 @@ func check_support{ let (is_supported) = IERC165.supportsInterface(target_contract, INTERFACE_ID) return (is_supported) end -``` +---- -> Please note that `supportsInterface` is camelCased because it is an exposed contract method as part of ERC165's interface. This differs from library methods (such as `supports_interface` from the [ERC165 library](../src/openzeppelin/introspection/ERC165.cairo)) which are snake_cased and not exposed. See the [Function names and coding style](../docs/Extensibility.md#function-names-and-coding-style) for more details. +____ +Please note that `supportsInterface` is camelCased because it is an exposed contract method as part of ERC165's interface. +This differs from library methods (such as `supports_interface` from the link:../src/openzeppelin/introspection/ERC165.cairo[ERC165 library]) which are snake_cased and not exposed. +See the link:../docs/Extensibility.md#function-names-and-coding-style[Function names and coding style] for more details. +____ -### IERC165 +=== IERC165 -```cairo +[,cairo] +---- @contract_interface namespace IERC165: func supportsInterface(interfaceId: felt) -> (success: felt): end end -``` +---- -### IERC165 API Specification +=== IERC165 API Specification -```cairo +[,cairo] +---- func supportsInterface(interfaceId: felt) -> (success: felt): end -``` +---- -#### `supportsInterface` +==== `supportsInterface` Returns true if this contract implements the interface defined by `interfaceId`. Parameters: -```cairo +[,cairo] +---- interfaceId: felt -``` +---- Returns: -```cairo +[,cairo] +---- success: felt -``` +---- -### ERC165 Library Functions +=== ERC165 Library Functions -```cairo +[,cairo] +---- func supports_interface(interface_id: felt) -> (success: felt): end func register_interface(interface_id: felt): end -``` +---- -

supports_interface

+[#supportsinterface2] +==== `supports_interface` Returns true if this contract implements the interface defined by `interface_id`. Parameters: -```cairo +[,cairo] +---- interface_id: felt -``` +---- Returns: -```cairo +[,cairo] +---- success: felt -``` +---- -#### `register_interface` +==== `register_interface` Calling contract declares support for a specific interface defined by `interface_id`. Parameters: -```cairo +[,cairo] +---- interface_id: felt -``` +---- Returns: diff --git a/docs/modules/ROOT/pages/proxies.adoc b/docs/modules/ROOT/pages/proxies.adoc index b9b12914d..628691865 100644 --- a/docs/modules/ROOT/pages/proxies.adoc +++ b/docs/modules/ROOT/pages/proxies.adoc @@ -1,36 +1,40 @@ -# Proxies - -> Expect rapid iteration as this pattern matures and more patterns potentially emerge. - -## Table of Contents - -* [Quickstart](#quickstart) -* [Solidity/Cairo upgrades comparison](#solidity/cairo-upgrades-comparison) - * [Constructors](#constructors) - * [Storage](#storage) -* [Proxies](#proxies2) - * [Proxy contract](#proxy-contract) - * [Implementation contract](#implementation-contract) -* [Upgrades library API](#upgrades-library-api) - * [Methods](#methods) - * [Events](#events) -* [Using proxies](#using-proxies) - * [Contract upgrades](#contract-upgrades) - * [Declaring contracts](#declaring-contracts) - * [Handling method calls](#handling-method-calls) -* [Presets](#presets) - -## Quickstart += Proxies + +____ +Expect rapid iteration as this pattern matures and more patterns potentially emerge. +____ + +== Table of Contents + +* <> +* <> + ** <> + ** <> +* <> + ** <> + ** <> +* <> + ** <> + ** <> +* <> + ** <> + ** <> + ** <> +* <> + +== Quickstart The general workflow is: -1. declare an implementation [contract class](https://starknet.io/docs/hello_starknet/intro.html#declare-the-contract-on-the-starknet-testnet) -2. deploy proxy contract with the implementation contract's class hash set in the proxy's constructor calldata -3. initialize the implementation contract by sending a call to the proxy contract. This will redirect the call to the implementation contract class and behave like the implementation contract's constructor +. declare an implementation https://starknet.io/docs/hello_starknet/intro.html#declare-the-contract-on-the-starknet-testnet[contract class] +. deploy proxy contract with the implementation contract's class hash set in the proxy's constructor calldata +. initialize the implementation contract by sending a call to the proxy contract. +This will redirect the call to the implementation contract class and behave like the implementation contract's constructor In Python, this would look as follows: -```python +[,python] +---- # declare implementation contract IMPLEMENTATION = await starknet.declare( "path/to/implementation.cairo", @@ -50,46 +54,69 @@ In Python, this would look as follows: proxy_admin ] ) -``` +---- -## Solidity/Cairo upgrades comparison +== Solidity/Cairo upgrades comparison -### Constructors +=== Constructors -OpenZeppelin Contracts for Solidity requires the use of an alternative library for upgradeable contracts. Consider that in Solidity constructors are not part of the deployed contract's runtime bytecode; rather, a constructor's logic is executed only once when the contract instance is deployed and then discarded. This is why proxies can't imitate the construction of its implementation, therefore requiring a different initialization mechanism. +OpenZeppelin Contracts for Solidity requires the use of an alternative library for upgradeable contracts. +Consider that in Solidity constructors are not part of the deployed contract's runtime bytecode; +rather, a constructor's logic is executed only once when the contract instance is deployed and then discarded. +This is why proxies can't imitate the construction of its implementation, therefore requiring a different initialization mechanism. -The constructor problem in upgradeable contracts is resolved by the use of initializer methods. Initializer methods are essentially regular methods that execute the logic that would have been in the constructor. Care needs to be exercised with initializers to ensure they can only be called once. Thus, OpenZeppelin offers an [upgradeable contracts library](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable) where much of this process is abstracted away. -See OpenZeppelin's [Writing Upgradeable Contracts](https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable) for more info. +The constructor problem in upgradeable contracts is resolved by the use of initializer methods. +Initializer methods are essentially regular methods that execute the logic that would have been in the constructor. +Care needs to be exercised with initializers to ensure they can only be called once. +Thus, OpenZeppelin offers an https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable[upgradeable contracts library] where much of this process is abstracted away. +See OpenZeppelin's https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable[Writing Upgradeable Contracts] for more info. -The Cairo programming language does not support inheritance. Instead, Cairo contracts follow the [Extensibility Pattern](../docs/Extensibility.md) which already uses initializer methods to mimic constructors. Upgradeable contracts do not, therefore, require a separate library with refactored constructor logic. +The Cairo programming language does not support inheritance. +Instead, Cairo contracts follow the xref:../docs/Extensibility.adoc[Extensibility Pattern] which already uses initializer methods to mimic constructors. +Upgradeable contracts do not, therefore, require a separate library with refactored constructor logic. -### Storage +=== Storage -OpenZeppelin's alternative Upgrades library also implements [unstructured storage](https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies#unstructured-storage-proxies) for its upgradeable contracts. The basic idea behind unstructured storage is to pseudo-randomize the storage structure of the upgradeable contract so it's based on variable names instead of declaration order, which makes the chances of storage collision during an upgrade extremely unlikely. +OpenZeppelin's alternative Upgrades library also implements https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies#unstructured-storage-proxies[unstructured storage] for its upgradeable contracts. +The basic idea behind unstructured storage is to pseudo-randomize the storage structure of the upgradeable contract so it's based on variable names instead of declaration order, which makes the chances of storage collision during an upgrade extremely unlikely. -The StarkNet compiler, meanwhile, already creates pseudo-random storage addresses by hashing the storage variable names (and keys in mappings) by default. In other words, StarkNet already uses unstructured storage and does not need a second library to modify how storage is set. See StarkNet's [Contracts Storage documentation](https://starknet.io/documentation/contracts/#contracts_storage) for more information. +The StarkNet compiler, meanwhile, already creates pseudo-random storage addresses by hashing the storage variable names (and keys in mappings) by default. +In other words, StarkNet already uses unstructured storage and does not need a second library to modify how storage is set. +See StarkNet's https://starknet.io/documentation/contracts/#contracts_storage[Contracts Storage documentation] for more information. -

Proxies

+[#proxies2] +== Proxies -A proxy contract is a contract that delegates function calls to another contract. This type of pattern decouples state and logic. Proxy contracts store the state and redirect function calls to an implementation contract that handles the logic. This allows for different patterns such as upgrades, where implementation contracts can change but the proxy contract (and thus the state) does not; as well as deploying multiple proxy instances pointing to the same implementation. This can be useful to deploy many contracts with identical logic but unique initialization data. +A proxy contract is a contract that delegates function calls to another contract. +This type of pattern decouples state and logic. +Proxy contracts store the state and redirect function calls to an implementation contract that handles the logic. +This allows for different patterns such as upgrades, where implementation contracts can change but the proxy contract (and thus the state) does not; +as well as deploying multiple proxy instances pointing to the same implementation. +This can be useful to deploy many contracts with identical logic but unique initialization data. -In the case of contract upgrades, it is achieved by simply changing the proxy's reference to the class hash of the declared implementation. This allows developers to add features, update logic, and fix bugs without touching the state or the contract address to interact with the application. +In the case of contract upgrades, it is achieved by simply changing the proxy's reference to the class hash of the declared implementation. +This allows developers to add features, update logic, and fix bugs without touching the state or the contract address to interact with the application. -### Proxy contract +=== Proxy contract -The [Proxy contract](../src/openzeppelin/upgrades/Proxy.cairo) includes two core methods: +The link:../src/openzeppelin/upgrades/Proxy.cairo[Proxy contract] includes two core methods: -1. The `__default__` method is a fallback method that redirects a function call and associated calldata to the implementation contract. +. The `__default__` method is a fallback method that redirects a function call and associated calldata to the implementation contract. +. The `__l1_default__` method is also a fallback method; +however, it redirects the function call and associated calldata to a layer one contract. +In order to invoke `__l1_default__`, the original function call must include the library function `send_message_to_l1`. +See Cairo's https://www.cairo-lang.org/docs/hello_starknet/l1l2.html[Interacting with L1 contracts] for more information. -2. The `__l1_default__` method is also a fallback method; however, it redirects the function call and associated calldata to a layer one contract. In order to invoke `__l1_default__`, the original function call must include the library function `send_message_to_l1`. See Cairo's [Interacting with L1 contracts](https://www.cairo-lang.org/docs/hello_starknet/l1l2.html) for more information. +Since this proxy is designed to work both as an https://eips.ethereum.org/EIPS/eip-1822[UUPS-flavored upgrade proxy] as well as a non-upgradeable proxy, it does not know how to handle its own state. +Therefore it requires the implementation contract class to be declared beforehand, so its class hash can be passed to the Proxy on construction time. -Since this proxy is designed to work both as an [UUPS-flavored upgrade proxy](https://eips.ethereum.org/EIPS/eip-1822) as well as a non-upgradeable proxy, it does not know how to handle its own state. Therefore it requires the implementation contract class to be declared beforehand, so its class hash can be passed to the Proxy on construction time. +When interacting with the contract, function calls should be sent by the user to the proxy. +The proxy's fallback function redirects the function call to the implementation contract to execute. -When interacting with the contract, function calls should be sent by the user to the proxy. The proxy's fallback function redirects the function call to the implementation contract to execute. +=== Implementation contract -### Implementation contract - -The implementation contract, also known as the logic contract, receives the redirected function calls from the proxy contract. The implementation contract should follow the [Extensibility pattern](../docs/Extensibility.md#the-pattern) and import directly from the [Proxy library](../src/openzeppelin/upgrades/library.cairo). +The implementation contract, also known as the logic contract, receives the redirected function calls from the proxy contract. +The implementation contract should follow the link:../docs/Extensibility.md#the-pattern[Extensibility pattern] and import directly from the link:../src/openzeppelin/upgrades/library.cairo[Proxy library]. The implementation contract should: @@ -98,25 +125,33 @@ The implementation contract should: If the implementation is upgradeable, it should: -* include a method to upgrade the implementation (i.e. `upgrade`) +* include a method to upgrade the implementation (i.e. +`upgrade`) * use access control to protect the contract's upgradeability. The implementation contract should NOT: -* be deployed like a regular contract. Instead, the implementation contract should be declared (which creates a `DeclaredClass` containing its hash and abi) -* set its initial state with a traditional constructor (decorated with `@constructor`). Instead, use an initializer method that invokes the Proxy `constructor`. +* be deployed like a regular contract. +Instead, the implementation contract should be declared (which creates a `DeclaredClass` containing its hash and abi) +* set its initial state with a traditional constructor (decorated with `@constructor`). +Instead, use an initializer method that invokes the Proxy `constructor`. -> Note that the Proxy `constructor` includes a check the ensures the initializer can only be called once; however, `_set_implementation` does not include this check. It's up to the developers to protect their implementation contract's upgradeability with access controls such as [`assert_only_admin`](#assert_only_admin). +____ +Note that the Proxy `constructor` includes a check the ensures the initializer can only be called once; +however, `_set_implementation` does not include this check. +It's up to the developers to protect their implementation contract's upgradeability with access controls such as <>. +____ For a full implementation contract example, please see: -* [Proxiable implementation](../tests/mocks/proxiable_implementation.cairo) +* link:../tests/mocks/proxiable_implementation.cairo[Proxiable implementation] -## Upgrades library API +== Upgrades library API -### Methods +=== Methods -```cairo +[,cairo] +---- func initializer(proxy_admin: felt): end @@ -134,23 +169,24 @@ end func _set_implementation_hash(new_implementation: felt): end -``` +---- -#### `initializer` +==== `initializer` Initializes the proxy contract with an initial implementation. Parameters: -```cairo +[,cairo] +---- proxy_admin: felt -``` +---- Returns: None. -#### `assert_only_admin` +==== `assert_only_admin` Reverts if called by any account other than the admin. @@ -162,7 +198,7 @@ Returns: None. -#### `get_implementation` +==== `get_implementation` Returns the current implementation hash. @@ -172,11 +208,12 @@ None. Returns: -```cairo +[,cairo] +---- implementation: felt -``` +---- -#### `get_admin` +==== `get_admin` Returns the current admin. @@ -186,76 +223,84 @@ None. Returns: -```cairo +[,cairo] +---- admin: felt -``` +---- -#### `_set_admin` +==== `_set_admin` Sets `new_admin` as the admin of the proxy contract. Parameters: -```cairo +[,cairo] +---- new_admin: felt -``` +---- Returns: None. -#### `_set_implementation_hash` +==== `_set_implementation_hash` -Sets `new_implementation` as the implementation's contract class. This method is included in the proxy contract's constructor and can be used to upgrade contracts. +Sets `new_implementation` as the implementation's contract class. +This method is included in the proxy contract's constructor and can be used to upgrade contracts. Parameters: -```cairo +[,cairo] +---- new_implementation: felt -``` +---- Returns: None. -### Events +=== Events -```cairo +[,cairo] +---- func Upgraded(implementation: felt): end func AdminChanged(previousAdmin: felt, newAdmin: felt): end -``` +---- -#### `Upgraded` +==== `Upgraded` Emitted when a proxy contract sets a new implementation class hash. Parameters: -```cairo +[,cairo] +---- implementation: felt -``` +---- -#### `AdminChanged` +==== `AdminChanged` Emitted when the `admin` changes from `previousAdmin` to `newAdmin`. Parameters: -```cairo +[,cairo] +---- previousAdmin: felt newAdmin: felt -``` +---- -## Using proxies +== Using proxies -### Contract upgrades +=== Contract upgrades To upgrade a contract, the implementation contract should include an `upgrade` method that, when called, changes the reference to a new deployed contract like this: -```python +[,python] +---- # declare first implementation IMPLEMENTATION = await starknet.declare( "path/to/implementation.cairo", @@ -280,22 +325,31 @@ To upgrade a contract, the implementation contract should include an `upgrade` m IMPLEMENTATION_V2.class_hash ] ) -``` +---- For a full deployment and upgrade implementation, please see: -* [Upgrades V1](../tests/mocks/upgrades_v1_mock.cairo) -* [Upgrades V2](../tests/mocks/upgrades_v2_mock.cairo) +* link:../tests/mocks/upgrades_v1_mock.cairo[Upgrades V1] +* link:../tests/mocks/upgrades_v2_mock.cairo[Upgrades V2] -### Declaring contracts +=== Declaring contracts -StarkNet contracts come in two forms: contract classes and contract instances. Contract classes represent the uninstantiated, stateless code; whereas, contract instances are instantiated and include the state. Since the Proxy contract references the implementation contract by its class hash, declaring an implementation contract proves sufficient (as opposed to a full deployment). For more information on declaring classes, see [StarkNet's documentation](https://starknet.io/docs/hello_starknet/intro.html#declare-contract). +StarkNet contracts come in two forms: contract classes and contract instances. +Contract classes represent the uninstantiated, stateless code; +whereas, contract instances are instantiated and include the state. +Since the Proxy contract references the implementation contract by its class hash, declaring an implementation contract proves sufficient (as opposed to a full deployment). +For more information on declaring classes, see https://starknet.io/docs/hello_starknet/intro.html#declare-contract[StarkNet's documentation]. -### Handling method calls +=== Handling method calls -As with most StarkNet contracts, interacting with a proxy contract requires an [account abstraction](../docs/Account.md#quickstart). One notable difference with proxy contracts versus other contract implementations is that calling `@view` methods also requires an account abstraction. As of now, direct calls to default entrypoints are only supported by StarkNet's `syscalls` from other contracts i.e. account contracts. The differences in getter methods written in Python, for example, are as follows: +As with most StarkNet contracts, interacting with a proxy contract requires an link:../docs/Account.md#quickstart[account abstraction]. +One notable difference with proxy contracts versus other contract implementations is that calling `@view` methods also requires an account abstraction. +As of now, direct calls to default entrypoints are only supported by StarkNet's `syscalls` from other contracts i.e. +account contracts. +The differences in getter methods written in Python, for example, are as follows: -```python +[,python] +---- # standard ERC20 call result = await erc20.totalSupply().call() @@ -303,13 +357,16 @@ result = await erc20.totalSupply().call() result = await signer.send_transaction( account, PROXY.contract_address, 'totalSupply', [] ) -``` +---- -## Presets +== Presets -Presets are pre-written contracts that extend from our library of contracts. They can be deployed as-is or used as templates for customization. +Presets are pre-written contracts that extend from our library of contracts. +They can be deployed as-is or used as templates for customization. Some presets include: -* [ERC20_Upgradeable](../src/openzeppelin/token/erc20/ERC20_Upgradeable.cairo) -* more to come! have an idea? [open an issue](https://github.com/OpenZeppelin/cairo-contracts/issues/new/choose)! +* link:../src/openzeppelin/token/erc20/ERC20_Upgradeable.cairo[ERC20_Upgradeable] +* more to come! +have an idea? +https://github.com/OpenZeppelin/cairo-contracts/issues/new/choose[open an issue]! diff --git a/docs/modules/ROOT/pages/security.adoc b/docs/modules/ROOT/pages/security.adoc index 5a372d6cf..29ae2a571 100644 --- a/docs/modules/ROOT/pages/security.adoc +++ b/docs/modules/ROOT/pages/security.adoc @@ -1,29 +1,33 @@ -# Security += Security - The following documentation provides context, reasoning, and examples of methods and constants found in `openzeppelin/security/`. +The following documentation provides context, reasoning, and examples of methods and constants found in `openzeppelin/security/`. - > Expect this module to evolve. +____ +Expect this module to evolve. +____ -## Table of Contents +== Table of Contents -* [Initializable](#initializable) -* [Pausable](#pausable) -* [Reentrancy Guard](#Reentrancy-Guard) -* [SafeMath](#safemath) - * [SafeUint256](#safeuint256) +* <> +* <> +* <> +* <> + ** <> -## Initializable +== Initializable -The Initializable library provides a simple mechanism that mimics the functionality of a constructor. More specifically, it enables logic to be performed once and only once which is useful to setup a contract's initial state when a constructor cannot be used. +The Initializable library provides a simple mechanism that mimics the functionality of a constructor. +More specifically, it enables logic to be performed once and only once which is useful to setup a contract's initial state when a constructor cannot be used. The recommended pattern with Initializable is to include a check that the Initializable state is `False` and invoke `initialize` in the target function like this: -```cairo +[,cairo] +---- from openzeppelin.security.initializable import Initializable @external func foo{ - syscall_ptr : felt*, + syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr }(): @@ -33,17 +37,24 @@ func foo{ Initializable.initialize() return () end -``` +---- -> Please note that this Initializable pattern should only be used on one function. +____ +Please note that this Initializable pattern should only be used on one function. +____ -## Pausable +== Pausable -The Pausable library allows contracts to implement an emergency stop mechanism. This can be useful for scenarios such as preventing trades until the end of an evaluation period or having an emergency switch to freeze all transactions in the event of a large bug. +The Pausable library allows contracts to implement an emergency stop mechanism. +This can be useful for scenarios such as preventing trades until the end of an evaluation period or having an emergency switch to freeze all transactions in the event of a large bug. -To use the Pausable library, the contract should include `pause` and `unpause` functions (which should be protected). For methods that should be available only when not paused, insert `assert_not_paused`. For methods that should be available only when paused, insert `assert_paused`. For example: +To use the Pausable library, the contract should include `pause` and `unpause` functions (which should be protected). +For methods that should be available only when not paused, insert `assert_not_paused`. +For methods that should be available only when paused, insert `assert_paused`. +For example: -```cairo +[,cairo] +---- from openzeppelin.security.pausable import Pausable @external @@ -69,22 +80,27 @@ func whenPaused{ # function body return () end -``` +---- -> Note that `pause` and `unpause` already include these assertions. In other words, `pause` cannot be invoked when already paused and vice versa. +____ +Note that `pause` and `unpause` already include these assertions. +In other words, `pause` cannot be invoked when already paused and vice versa. +____ For a list of full implementations utilizing the Pausable library, see: -* [ERC20_Pausable](../src/openzeppelin/token/erc20/ERC20_Pausable.cairo) -* [ERC721_Mintable_Pausable](../src/openzeppelin/token/erc721/ERC721_Mintable_Pausable.cairo) +* link:../src/openzeppelin/token/erc20/ERC20_Pausable.cairo[ERC20_Pausable] +* link:../src/openzeppelin/token/erc721/ERC721_Mintable_Pausable.cairo[ERC721_Mintable_Pausable] -## Reentrancy Guard +== Reentrancy Guard -A [reentrancy attack](https://gus-tavo-guim.medium.com/reentrancy-attack-on-smart-contracts-how-to-identify-the-exploitable-and-an-example-of-an-attack-4470a2d8dfe4) occurs when the caller is able to obtain more resources than allowed by recursively calling a target’s function. +A https://gus-tavo-guim.medium.com/reentrancy-attack-on-smart-contracts-how-to-identify-the-exploitable-and-an-example-of-an-attack-4470a2d8dfe4[reentrancy attack] occurs when the caller is able to obtain more resources than allowed by recursively calling a target's function. -Since Cairo does not support modifiers like Solidity, the [`reentrancyguard`](../src/openzeppelin/security/reentrancyguard.cairo) library exposes two methods `_start` and `_end` to protect functions against reentrancy attacks. The protected function must call `ReentrancyGuard._start` before the first function statement, and `ReentrancyGuard._end` before the return statement, as shown below: +Since Cairo does not support modifiers like Solidity, the link:../src/openzeppelin/security/reentrancyguard.cairo[`reentrancyguard`] library exposes two methods `_start` and `_end` to protect functions against reentrancy attacks. +The protected function must call `ReentrancyGuard._start` before the first function statement, and `ReentrancyGuard._end` before the return statement, as shown below: -```cairo +[,cairo] +---- from openzeppelin.security.reentrancyguard import ReentrancyGuard func test_function{ @@ -97,17 +113,22 @@ func test_function{ ReentrancyGuard._end() return () end -``` +---- -## SafeMath +== SafeMath -### SafeUint256 +=== SafeUint256 -The SafeUint256 namespace in the [SafeMath library](../src/openzeppelin/security/safemath.cairo) offers arithmetic for unsigned 256-bit integers (uint256) by leveraging Cairo's Uint256 library and integrating overflow checks. Some of Cairo's Uint256 functions do not revert upon overflows. For instance, `uint256_add` will return a bit carry when the sum exceeds 256 bits. This library includes an additional assertion ensuring values do not overflow. +The SafeUint256 namespace in the link:../src/openzeppelin/security/safemath.cairo[SafeMath library] offers arithmetic for unsigned 256-bit integers (uint256) by leveraging Cairo's Uint256 library and integrating overflow checks. +Some of Cairo's Uint256 functions do not revert upon overflows. +For instance, `uint256_add` will return a bit carry when the sum exceeds 256 bits. +This library includes an additional assertion ensuring values do not overflow. -Using SafeUint256 methods is rather straightforward. Simply import SafeUint256 and insert the arithmetic method like this: +Using SafeUint256 methods is rather straightforward. +Simply import SafeUint256 and insert the arithmetic method like this: -```cairo +[,cairo] +---- from openzeppelin.security.safemath import SafeUint256 func add_two_uints{ @@ -118,4 +139,4 @@ func add_two_uints{ let (c: Uint256) = SafeUint256.add(a, b) return (c) end -``` +---- diff --git a/docs/modules/ROOT/pages/utilities.adoc b/docs/modules/ROOT/pages/utilities.adoc index 78f3ade88..d7ed69962 100644 --- a/docs/modules/ROOT/pages/utilities.adoc +++ b/docs/modules/ROOT/pages/utilities.adoc @@ -1,153 +1,177 @@ -# Utilities += Utilities -The following documentation provides context, reasoning, and examples for methods and constants found in `tests/utils.py`. +The following documentation provides context, reasoning, and examples for methods and constants found in `tests/utils.py`. -> Expect this module to evolve (as it has already done). +____ +Expect this module to evolve (as it has already done). +____ -## Table of Contents +== Table of Contents -* [Constants](#constants) -* [Strings](#strings) - * [`str_to_felt`](#str_to_felt) - * [`felt_to_str`](#felt_to_str) -* [Uint256](#uint256) - * [`uint`](#uint) - * [`to_uint`](#to_uint) - * [`from_uint`](#from_uint) - * [`add_uint`](#add_uint) - * [`sub_uint`](#sub_uint) -* [Assertions](#assertions) - * [`assert_revert`](#assert_revert) - * [`assert_revert_entry_point`](#assert_revert_entry_point) - * [`assert_events_emitted`](#assert_event_emitted) -* [Memoization](#memoization) - * [`get_contract_class`](#get_contract_class) - * [`cached_contract`](#cached_contract) -* [MockSigner](#mocksigner) +* <> +* <> + ** <> + ** <> +* <> + ** <> + ** <> + ** <> + ** <> + ** <> +* <> + ** <> + ** <> + ** <> +* <> + ** <> + ** <> +* <> -## Constants +== Constants -To ease the readability of Cairo contracts, this project includes reusable [constants variables](../src/openzeppelin/utils/constants.cairo) like `UINT8_MAX`, or EIP165 interface IDs such as `IERC165_ID` or `IERC721_ID`. For more information on how interface ids are calculated, see the [ERC165 documentation](../docs/Introspection.md#interface-calculations). +To ease the readability of Cairo contracts, this project includes reusable link:../src/openzeppelin/utils/constants.cairo[constants variables] like `UINT8_MAX`, or EIP165 interface IDs such as `IERC165_ID` or `IERC721_ID`. +For more information on how interface ids are calculated, see the link:../docs/Introspection.md#interface-calculations[ERC165 documentation]. -## Strings +== Strings -Cairo currently only provides support for short string literals (less than 32 characters). Note that short strings aren't really strings, rather, they're representations of Cairo field elements. The following methods provide a simple conversion to/from field elements. +Cairo currently only provides support for short string literals (less than 32 characters). +Note that short strings aren't really strings, rather, they're representations of Cairo field elements. +The following methods provide a simple conversion to/from field elements. -### `str_to_felt` +=== `str_to_felt` Takes an ASCII string and converts it to a field element via big endian representation. -### `felt_to_str` +=== `felt_to_str` Takes an integer and converts it to an ASCII string by trimming the null bytes and decoding the remaining bits. -## Uint256 +== Uint256 -Cairo's native data type is a field element (felt). Felts equate to 252 bits which poses a problem regarding 256-bit integer integration. To resolve the bit discrepancy, Cairo represents 256-bit integers as a struct of two 128-bit integers. Further, the low bits precede the high bits e.g. +Cairo's native data type is a field element (felt). +Felts equate to 252 bits which poses a problem regarding 256-bit integer integration. +To resolve the bit discrepancy, Cairo represents 256-bit integers as a struct of two 128-bit integers. +Further, the low bits precede the high bits e.g. -```python +[,python] +---- 1 = (1, 0) 1 << 128 = (0, 1) (1 << 128) - 1 = (340282366920938463463374607431768211455, 0) -``` +---- -### `uint` +=== `uint` Converts a simple integer into a uint256-ish tuple. -> Note `to_uint` should be used in favor of `uint`, as `uint` only returns the low bits of the tuple. +____ +Note `to_uint` should be used in favor of `uint`, as `uint` only returns the low bits of the tuple. +____ -### `to_uint` +=== `to_uint` Converts an integer into a uint256-ish tuple. -```python +[,python] +---- x = to_uint(340282366920938463463374607431768211456) print(x) # prints (0, 1) -``` +---- -### `from_uint` +=== `from_uint` Converts a uin256-ish tuple into an integer. -```python +[,python] +---- x = (0, 1) y = from_uint(x) print(y) # prints 340282366920938463463374607431768211456 -``` +---- -### `add_uint` +=== `add_uint` Performs addition between two uint256-ish tuples and returns the sum as a uint256-ish tuple. -```python +[,python] +---- x = (0, 1) y = (1, 0) z = add_uint(x, y) print(z) # prints (1, 1) -``` +---- -### `sub_uint` +=== `sub_uint` Performs subtraction between two uint256-ish tuples and returns the difference as a uint256-ish tuple. -```python +[,python] +---- x = (0, 1) y = (1, 0) z = sub_uint(x, y) print(z) # prints (340282366920938463463374607431768211455, 0) -``` +---- -### `mul_uint` +=== `mul_uint` Performs multiplication between two uint256-ish tuples and returns the product as a uint256-ish tuple. -```python +[,python] +---- x = (0, 10) y = (2, 0) z = mul_uint(x, y) print(z) # prints (0, 20) -``` +---- -### `div_rem_uint` +=== `div_rem_uint` Performs division between two uint256-ish tuples and returns both the quotient and remainder as uint256-ish tuples respectively. -```python +[,python] +---- x = (1, 100) y = (0, 25) z = div_rem_uint(x, y) print(z) -# prints ((4, 0), (1, 0)) -``` +# prints ((4, 0), (1, 0)) +---- -## Assertions +== Assertions In order to abstract away some of the verbosity regarding test assertions on StarkNet transactions, this project includes the following helper methods: -### `assert_revert` +=== `assert_revert` -An asynchronous wrapper method that executes a try-except pattern for transactions that should fail. Note that this wrapper does not check for a StarkNet error code. This allows for more flexibility in checking that a transaction simply failed. If you wanted to check for an exact error code, you could use StarkNet's [error_codes module](https://github.com/starkware-libs/cairo-lang/blob/ed6cf8d6cec50a6ad95fa36d1eb4a7f48538019e/src/starkware/starknet/definitions/error_codes.py) and implement additional logic to the `assert_revert` method. +An asynchronous wrapper method that executes a try-except pattern for transactions that should fail. +Note that this wrapper does not check for a StarkNet error code. +This allows for more flexibility in checking that a transaction simply failed. +If you wanted to check for an exact error code, you could use StarkNet's https://github.com/starkware-libs/cairo-lang/blob/ed6cf8d6cec50a6ad95fa36d1eb4a7f48538019e/src/starkware/starknet/definitions/error_codes.py[error_codes module] and implement additional logic to the `assert_revert` method. - To successfully use this wrapper, the transaction method should be wrapped with `assert_revert`; however, `await` should precede the wrapper itself like this: +To successfully use this wrapper, the transaction method should be wrapped with `assert_revert`; +however, `await` should precede the wrapper itself like this: -```python +[,python] +---- await assert_revert(signer.send_transaction( account, contract.contract_address, 'foo', [ recipient, *token ]) ) -``` +---- -This wrapper also includes the option to check that an error message was included in the reversion. To check that the reversion sends the correct error message, add the `reverted_with` keyword argument outside of the actual transaction (but still inside the wrapper) like this: +This wrapper also includes the option to check that an error message was included in the reversion. +To check that the reversion sends the correct error message, add the `reverted_with` keyword argument outside of the actual transaction (but still inside the wrapper) like this: -```python +[,python] +---- await assert_revert(signer.send_transaction( account, contract.contract_address, 'foo', [ recipient, @@ -155,26 +179,31 @@ await assert_revert(signer.send_transaction( ]), reverted_with="insert error message here" ) -``` +---- -### `assert_revert_entry_point` +=== `assert_revert_entry_point` -An extension of `assert_revert` that asserts an entry point error occurs with the given `invalid_selector` parameter. This assertion is especially useful in checking proxy/implementation contracts. To use `assert_revert_entry_point`: +An extension of `assert_revert` that asserts an entry point error occurs with the given `invalid_selector` parameter. +This assertion is especially useful in checking proxy/implementation contracts. +To use `assert_revert_entry_point`: -```python +[,python] +---- await assert_revert_entry_point( signer.send_transaction( account, contract.contract_address, 'nonexistent_selector', [] ), invalid_selector='nonexistent_selector' ) -``` +---- -### `assert_event_emitted` +=== `assert_event_emitted` -A helper method that checks a transaction receipt for the contract emitting the event (`from_address`), the emitted event itself (`name`), and the arguments emitted (`data`). To use `assert_event_emitted`: +A helper method that checks a transaction receipt for the contract emitting the event (`from_address`), the emitted event itself (`name`), and the arguments emitted (`data`). +To use `assert_event_emitted`: -```python +[,python] +---- # capture the tx receipt tx_exec_info = await signer.send_transaction( account, contract.contract_address, 'foo', [ @@ -193,31 +222,39 @@ assert_event_emitted( *token ] ) -``` +---- -## Memoization +== Memoization Memoizing functions allow for quicker and computationally cheaper calculations which is immensely beneficial while testing smart contracts. -### `get_contract_class` +=== `get_contract_class` -A helper method that returns the contract class from the contract's name. To capture the contract class, simply add the contract's name as an argument like this: +A helper method that returns the contract class from the contract's name. +To capture the contract class, simply add the contract's name as an argument like this: -```python +[,python] +---- contract_class = get_contract_class('ContractName') -``` +---- -If multiple contracts exist with the same name, then the contract's path must be passed along with the `is_path` flag instead of the name. To pass the contract's path: +If multiple contracts exist with the same name, then the contract's path must be passed along with the `is_path` flag instead of the name. +To pass the contract's path: -```python +[,python] +---- contract_class = get_contract_class('path/to/Contract.cairo', is_path=True) -``` +---- -### `cached_contract` +=== `cached_contract` -A helper method that returns the cached state of a given contract. It's recommended to first deploy all the relevant contracts before caching the state. The requisite contracts in the testing module should each be instantiated with `cached_contract` in a fixture after the state has been copied. The memoization pattern with `cached_contract` should look something like this: +A helper method that returns the cached state of a given contract. +It's recommended to first deploy all the relevant contracts before caching the state. +The requisite contracts in the testing module should each be instantiated with `cached_contract` in a fixture after the state has been copied. +The memoization pattern with `cached_contract` should look something like this: -```python +[,python] +---- # get contract classes @pytest.fixture(scope='module') def contract_classes(): @@ -243,8 +280,10 @@ def foo_factory(contract_classes, foo_init): _state = state.copy() # copy the state cached_foo = cached_contract(_state, foo_cls, foo) # cache contracts return cached_foo # return cached contracts -``` +---- -## MockSigner +== MockSigner -`MockSigner` is used to perform transactions with an instance of [Nile's Signer](https://github.com/OpenZeppelin/nile/blob/main/src/nile/signer.py) on a given Account, crafting the transaction and managing nonces. The `Signer` instance manages signatures and is leveraged by `MockSigner` to operate with the Account contract's `__execute__` method. See [MockSigner utility](../docs/Account.md#mocksigner-utility) for more information. +`MockSigner` is used to perform transactions with an instance of https://github.com/OpenZeppelin/nile/blob/main/src/nile/signer.py[Nile's Signer] on a given Account, crafting the transaction and managing nonces. +The `Signer` instance manages signatures and is leveraged by `MockSigner` to operate with the Account contract's `__execute__` method. +See link:../docs/Account.md#mocksigner-utility[MockSigner utility] for more information. From fb6c98e24ca89f040d549e67639d65e6cbe317a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Thu, 14 Jul 2022 11:51:09 -0300 Subject: [PATCH 04/39] update antora name --- docs/antora.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/antora.yml b/docs/antora.yml index 575842e54..bd09b8d1e 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -1,4 +1,4 @@ -name: cairo-contracts +name: contracts-cairo title: Contracts for Cairo version: 0.2 nav: From 0e7aaa0b1f34e24587b2c42a5256f3d1ff264f47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Thu, 14 Jul 2022 12:05:46 -0300 Subject: [PATCH 05/39] fix index adoc format --- docs/modules/ROOT/pages/index.adoc | 87 ++++++++++++++++++------------ 1 file changed, 54 insertions(+), 33 deletions(-) diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index 22cb64219..e0a2ced18 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -1,81 +1,102 @@ = Contracts for Cairo -**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 https://starkware.co/product/starknet/[StarkNet], a decentralized ZK Rollup. -## Security Advisory ⚠️ +== Security Advisory ⚠️ -- A critical [vulnerability](https://github.com/OpenZeppelin/cairo-contracts/issues/344) was found in an **unreleased** version of the Account contract. It was [introduced in March 25th](https://github.com/OpenZeppelin/cairo-contracts/pull/233) and has been [patched as of June 1st](https://github.com/OpenZeppelin/cairo-contracts/pull/347). If you copied the Account contract code into your project during that period, please update to the patched version. Note that 0.1.0 users are not affected. +* A critical https://github.com/OpenZeppelin/cairo-contracts/issues/344[vulnerability] was found in an *unreleased* version of the Account contract. +It was https://github.com/OpenZeppelin/cairo-contracts/pull/233[introduced in March 25th] and has been https://github.com/OpenZeppelin/cairo-contracts/pull/347[patched as of June 1st]. +If you copied the Account contract code into your project during that period, please update to the patched version. +Note that 0.1.0 users are not affected. -## Usage +== Usage -> ## ⚠️ WARNING! ⚠️ -> -> This repo contains highly experimental code. -> Expect rapid iteration. -> **Use at your own risk.** +____ +== ⚠️ WARNING! ⚠️ -### First time? +This repo contains highly experimental code. +Expect rapid iteration. +*Use at your own risk.* +____ + +=== First time? Before installing Cairo on your machine, you need to install `gmp`: -```bash +[,bash] +---- sudo apt install -y libgmp3-dev # linux brew install gmp # mac -``` +---- -> 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). +____ +If you have any troubles installing gmp on your Apple M1 computer, https://github.com/OpenZeppelin/nile/issues/22[here's a list of potential solutions]. +____ -### Set up your project +=== Set up your project Create a directory for your project, then `cd` into it and create a Python virtual environment. -```bash +[,bash] +---- mkdir my-project cd my-project python3 -m venv env source env/bin/activate -``` +---- -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/). +Install the https://github.com/OpenZeppelin/nile[Nile] development environment and then run `init` to kickstart a new project. +Nile will create the project directory structure and install https://www.cairo-lang.org/docs/quickstart.html[the Cairo language], a https://github.com/Shard-Labs/starknet-devnet/[local network], and a https://docs.pytest.org/en/6.2.x/[testing framework]. -```bash +[,bash] +---- pip install cairo-nile nile init -``` +---- -### Install the library +=== Install the library -```bash +[,bash] +---- pip install openzeppelin-cairo-contracts -``` +---- -### Use a basic preset +=== Use a basic preset -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](docs/Extensibility.md#presets). +Presets are ready-to-use contracts that you can deploy right away. +They also serve as examples of how to use library modules. +link:docs/Extensibility.md#presets[Read more about presets]. -```cairo +[,cairo] +---- # contracts/MyToken.cairo %lang starknet from openzeppelin.token.erc20.ERC20 import constructor -``` +---- Compile and deploy it right away: -```bash +[,bash] +---- nile compile nile deploy MyToken --alias my_token -``` +---- -> Note that `` is expected to be two integers i.e. `1` `0`. See [Uint256](docs/Utilities.md#Uint256) for more information. +____ +Note that `` is expected to be two integers i.e. +`1` `0`. +See link:docs/Utilities.md#Uint256[Uint256] for more information. +____ -### Write a custom contract using library modules +=== Write a custom contract using library modules -[Read more about libraries](docs/Extensibility.md#libraries). +link:docs/Extensibility.md#libraries[Read more about libraries]. -```cairo +[,cairo] +---- %lang starknet from starkware.cairo.common.cairo_builtins import HashBuiltin @@ -95,4 +116,4 @@ func transfer{ ERC20.transfer(recipient, amount) return (TRUE) end -``` +---- From caae43220b3ab2e624ff878cba9a22c202b430d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Fri, 15 Jul 2022 18:32:52 +0200 Subject: [PATCH 06/39] Apply suggestions from code review Co-authored-by: Francisco --- RELEASING.md | 20 ++++++-------------- docs/modules/ROOT/nav.adoc | 2 +- docs/modules/ROOT/pages/access.adoc | 2 +- docs/modules/ROOT/pages/index.adoc | 8 +------- 4 files changed, 9 insertions(+), 23 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 78d3a2ec7..5cb320e5d 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -20,20 +20,12 @@ to (3) Update documentation version in `docs/antora.yml` -```yml -name: cairo-contracts -title: Contracts for Cairo -version: 0.1 -(...) -``` - -to - -```yml -name: cairo-contracts -title: Contracts for Cairo -version: 0.2 -(...) +```diff + name: cairo-contracts + title: Contracts for Cairo +-version: 0.1 ++version: 0.2 + (...) ``` (4) Create a release branch and add a tag to it. This branch can be useful if we need to push a hot fix on top of an existing release in the case of a bug. diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index d7fc52392..c34a6e4d7 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -1,5 +1,5 @@ * xref:index.adoc[Overview] -// * xref:wizard.adoc[Wizard] +* https://wizard.openzeppelin.com/cairo[Wizard] * xref:extensibility.adoc[Extensibility] * xref:proxies.adoc[Proxies and Upgrades] diff --git a/docs/modules/ROOT/pages/access.adoc b/docs/modules/ROOT/pages/access.adoc index 7117e9a86..ca82e7b2b 100644 --- a/docs/modules/ROOT/pages/access.adoc +++ b/docs/modules/ROOT/pages/access.adoc @@ -67,7 +67,7 @@ end === Ownable library API -[,cairo] +[.hljs-theme-light.nopadding,cairo] ---- func initializer(owner: felt): end diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index e0a2ced18..a61d8ff72 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -11,13 +11,7 @@ Note that 0.1.0 users are not affected. == Usage -____ -== ⚠️ WARNING! ⚠️ - -This repo contains highly experimental code. -Expect rapid iteration. -*Use at your own risk.* -____ +WARNING: This repo contains highly experimental code. Expect rapid iteration. *Use at your own risk.* === First time? From 377e09b63313d59b2e2dd97fefa8e0e605322fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Fri, 15 Jul 2022 13:33:59 -0300 Subject: [PATCH 07/39] add crosslink to solidity, remove advisory --- docs/modules/ROOT/nav.adoc | 1 + docs/modules/ROOT/pages/index.adoc | 7 ------- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index c34a6e4d7..18080a795 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -1,3 +1,4 @@ +* xref:contracts::index.adoc[Contracts for Solidity] * xref:index.adoc[Overview] * https://wizard.openzeppelin.com/cairo[Wizard] * xref:extensibility.adoc[Extensibility] diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index a61d8ff72..18fe360f6 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -2,13 +2,6 @@ *A library for secure smart contract development* written in Cairo for https://starkware.co/product/starknet/[StarkNet], a decentralized ZK Rollup. -== Security Advisory ⚠️ - -* A critical https://github.com/OpenZeppelin/cairo-contracts/issues/344[vulnerability] was found in an *unreleased* version of the Account contract. -It was https://github.com/OpenZeppelin/cairo-contracts/pull/233[introduced in March 25th] and has been https://github.com/OpenZeppelin/cairo-contracts/pull/347[patched as of June 1st]. -If you copied the Account contract code into your project during that period, please update to the patched version. -Note that 0.1.0 users are not affected. - == Usage WARNING: This repo contains highly experimental code. Expect rapid iteration. *Use at your own risk.* From 96dac3d6033f85205d7e78f7dfed827e4bee65ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Fri, 15 Jul 2022 13:35:22 -0300 Subject: [PATCH 08/39] fix extensibility --- docs/modules/ROOT/pages/extensibility.adoc | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/modules/ROOT/pages/extensibility.adoc b/docs/modules/ROOT/pages/extensibility.adoc index 147fad22c..938c895b6 100644 --- a/docs/modules/ROOT/pages/extensibility.adoc +++ b/docs/modules/ROOT/pages/extensibility.adoc @@ -1,8 +1,6 @@ = Extensibility -____ -Expect this pattern to evolve (as it has already done) or even disappear if https://community.starknet.io/t/contract-extensibility-pattern/210/11?u=martriay[proper extensibility features] are implemented into Cairo. -____ +NOTE: Expect this pattern to evolve (as it has already done) or even disappear if https://community.starknet.io/t/contract-extensibility-pattern/210/11?u=martriay[proper extensibility features] are implemented into Cairo. * <> * <> @@ -107,7 +105,7 @@ The main reason being that Cairo does not support overriding functions. This is what a hook looks like in Solidity: -[,js] +[,solidity] ---- abstract contract ERC20Pausable is ERC20, Pausable { function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override { @@ -121,7 +119,7 @@ abstract contract ERC20Pausable is ERC20, Pausable { Instead, the extensibility pattern allows us to simply extend the library implementation of a function (namely `transfer`) by adding lines before or after calling it. This way, we can get away with: -[,python] +[,cairo] ---- @external func transfer{ From 131435e29c21c81c5655803b9f585b9551c51d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Fri, 15 Jul 2022 13:53:04 -0300 Subject: [PATCH 09/39] add admonitions and scape underscores --- docs/modules/ROOT/pages/access.adoc | 4 +--- docs/modules/ROOT/pages/accounts.adoc | 22 +++++++++++----------- docs/modules/ROOT/pages/erc20.adoc | 2 +- docs/modules/ROOT/pages/erc721.adoc | 6 ++---- docs/modules/ROOT/pages/index.adoc | 8 ++------ docs/modules/ROOT/pages/introspection.adoc | 8 ++------ docs/modules/ROOT/pages/proxies.adoc | 14 +++++--------- docs/modules/ROOT/pages/security.adoc | 12 +++--------- docs/modules/ROOT/pages/utilities.adoc | 6 ++---- 9 files changed, 29 insertions(+), 53 deletions(-) diff --git a/docs/modules/ROOT/pages/access.adoc b/docs/modules/ROOT/pages/access.adoc index ca82e7b2b..d858c645a 100644 --- a/docs/modules/ROOT/pages/access.adoc +++ b/docs/modules/ROOT/pages/access.adoc @@ -1,8 +1,6 @@ = Access -____ -Expect these modules to evolve. -____ +CAUTION: Expect these modules to evolve. Access control--that is, "who is allowed to do this thing"--is incredibly important in the world of smart contracts. The access control of your contract may govern who can mint tokens, vote on proposals, freeze transfers, and many other things. diff --git a/docs/modules/ROOT/pages/accounts.adoc b/docs/modules/ROOT/pages/accounts.adoc index 52c41fdac..96cad3156 100644 --- a/docs/modules/ROOT/pages/accounts.adoc +++ b/docs/modules/ROOT/pages/accounts.adoc @@ -25,7 +25,7 @@ A more detailed writeup on the topic can be found on https://perama-v.github.io/ ** <> ** <> ** <> - ** <<__execute__,`__execute__`>> + ** <<\\__execute__,`\\__execute__`>> ** <> ** <> ** <<_unsafe_execute,`_unsafe_execute`>> @@ -144,7 +144,7 @@ The hexadecimal conversion is necessary because Nile's `Signer` converts the add Note that directly converting `to` to a string will ultimately result in an integer exceeding Cairo's `FIELD_PRIME`. The values included in the transaction are passed to the `sign_transaction` method of Nile's `Signer` which creates and returns a signature. -Finally, the `MockSigner` instance invokes the account contract's `__execute__` with the transaction data. +Finally, the `MockSigner` instance invokes the account contract's `\\__execute__` with the transaction data. Users only need to interact with the following exposed methods to perform a transaction: @@ -191,7 +191,7 @@ It differs from the `MockSigner` implementation by: == Account entrypoint -`__execute__` acts as a single entrypoint for all user interaction with any contract, including managing the account contract itself. +`\\__execute__` acts as a single entrypoint for all user interaction with any contract, including managing the account contract itself. That's why if you want to change the public key controlling the Account, you would send a transaction targeting the very Account contract: [,python] @@ -209,7 +209,7 @@ await signer.send_transaction(account, registry.contract_address, 'set_L1_addres You can read more about how messages are structured and hashed in the https://github.com/OpenZeppelin/cairo-contracts/discussions/24[Account message scheme discussion]. For more information on the design choices and implementation of multicall, you can read the https://github.com/OpenZeppelin/cairo-contracts/discussions/27[How should Account multicall work discussion]. -The `__execute__` method has the following interface: +The `\\__execute__` method has the following interface: [,cairo] ---- @@ -233,15 +233,15 @@ Where: Current implementation requires nonces to be incremental ____ -Note that the scheme of building multicall transactions within the `__execute__` method will change once StarkNet allows for pointers in struct arrays. -In which case, multiple transactions can be passed to (as opposed to built within) `__execute__`. +Note that the scheme of building multicall transactions within the `\\__execute__` method will change once StarkNet allows for pointers in struct arrays. +In which case, multiple transactions can be passed to (as opposed to built within) `\\__execute__`. ____ == `Call` and `AccountCallArray` format The idea is for all user intent to be encoded into a `Call` representing a smart contract call. Users can also pack multiple messages into a single transaction (creating a multicall transaction). -Cairo currently does not support arrays of structs with pointers which means the `__execute__` function cannot properly iterate through mutiple ``Call``s. +Cairo currently does not support arrays of structs with pointers which means the `\\__execute__` function cannot properly iterate through mutiple ``Call``s. Instead, this implementation utilizes a workaround with the `AccountCallArray` struct. See <>. @@ -290,7 +290,7 @@ Where: == Multicall transactions A multicall transaction packs the `to`, `selector`, `calldata_offset`, and `calldata_len` of each call into the `AccountCallArray` struct and keeps the cumulative calldata for every call in a separate array. -The `__execute__` function rebuilds each message by combining the `AccountCallArray` with its calldata (demarcated by the offset and calldata length specified for that particular call). +The `\\__execute__` function rebuilds each message by combining the `AccountCallArray` with its calldata (demarcated by the offset and calldata length specified for that particular call). The rebuilding logic is set in the internal `_from_call_array_to_call`. This is the basic flow: @@ -309,9 +309,9 @@ This is the basic flow: + The `_from_call_to_call_array` method in link:../tests/utils.py[utils.py] converts each call into the `AccountCallArray` format and cumulatively stores the calldata of every call into a single array. Next, both arrays (as well as the `sender`, `nonce`, and `max_fee`) are used to create the transaction hash. -The Signer then invokes `__execute__` with the signature and passes `AccountCallArray`, calldata, and nonce as arguments. +The Signer then invokes `\\__execute__` with the signature and passes `AccountCallArray`, calldata, and nonce as arguments. -. The `__execute__` method takes the `AccountCallArray` and calldata and builds an array of ``Call``s (MultiCall). +. The `\\__execute__` method takes the `AccountCallArray` and calldata and builds an array of ``Call``s (MultiCall). ____ It should be noted that every transaction utilizes `AccountCallArray`. @@ -422,7 +422,7 @@ Otherwise, reverts. In the future it will return `FALSE` if a given signature is invalid (for more info please check https://github.com/OpenZeppelin/cairo-contracts/issues/327[this issue]). ____ -=== `__execute__` +=== `\\__execute__` This is the only external entrypoint to interact with the Account contract. It: diff --git a/docs/modules/ROOT/pages/erc20.adoc b/docs/modules/ROOT/pages/erc20.adoc index 3ab7593fe..194f88f45 100644 --- a/docs/modules/ROOT/pages/erc20.adoc +++ b/docs/modules/ROOT/pages/erc20.adoc @@ -258,7 +258,7 @@ symbol: felt ==== `decimals` Returns the number of decimals the token uses - e.g. -8 means to divide the token amount by 100000000 to get its user representation. +`8` means to divide the token amount by `100000000` to get its user representation. Parameters: None. diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index a9c41a86e..5a06b62fb 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -192,18 +192,16 @@ The `safeTransferFrom` method incorporates the following conditional logic: . if the calling address is not an account contract, the safe function will check that the contract supports ERC721 tokens The current implementation of `safeTansferFrom` checks for `onERC721Received` and requires that the recipient contract supports ERC165 and exposes the `supportsInterface` method. -See <> +See <>. === Interpreting ERC721 URIs Token URIs in Cairo are stored as single field elements. Each field element equates to 252-bits (or 31.5 bytes) which means that a token's URI can be no longer than 31 characters. -____ -Note that storing the URI as an array of felts was considered to accommodate larger strings. +NOTE: Storing the URI as an array of felts was considered to accommodate larger strings. While this approach is more flexible regarding URIs, a returned array further deviates from the standard set in https://eips.ethereum.org/EIPS/eip-721[EIP721]. Therefore, this library's ERC721 implementation sets URIs as a single field element. -____ The `utils.py` module includes utility methods for converting to/from Cairo field elements. To properly interpret a URI from ERC721, simply trim the null bytes and decode the remaining bits as an ASCII string. diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index 18fe360f6..50ee045db 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -16,9 +16,7 @@ sudo apt install -y libgmp3-dev # linux brew install gmp # mac ---- -____ -If you have any troubles installing gmp on your Apple M1 computer, https://github.com/OpenZeppelin/nile/issues/22[here's a list of potential solutions]. -____ +TIP: If you have any troubles installing gmp on your Apple M1 computer, https://github.com/OpenZeppelin/nile/issues/22[here's a list of potential solutions]. === Set up your project @@ -72,11 +70,9 @@ nile compile nile deploy MyToken --alias my_token ---- -____ -Note that `` is expected to be two integers i.e. +NOTE: `` is expected to be two integers i.e. `1` `0`. See link:docs/Utilities.md#Uint256[Uint256] for more information. -____ === Write a custom contract using library modules diff --git a/docs/modules/ROOT/pages/introspection.adoc b/docs/modules/ROOT/pages/introspection.adoc index ec1bb0b64..35d0a483c 100644 --- a/docs/modules/ROOT/pages/introspection.adoc +++ b/docs/modules/ROOT/pages/introspection.adoc @@ -1,8 +1,6 @@ = Introspection -____ -Expect this module to evolve. -____ +CAUTION: Expect this module to evolve. == Table of Contents @@ -85,11 +83,9 @@ func check_support{ end ---- -____ -Please note that `supportsInterface` is camelCased because it is an exposed contract method as part of ERC165's interface. +NOTE: `supportsInterface` is camelCased because it is an exposed contract method as part of ERC165's interface. This differs from library methods (such as `supports_interface` from the link:../src/openzeppelin/introspection/ERC165.cairo[ERC165 library]) which are snake_cased and not exposed. See the link:../docs/Extensibility.md#function-names-and-coding-style[Function names and coding style] for more details. -____ === IERC165 diff --git a/docs/modules/ROOT/pages/proxies.adoc b/docs/modules/ROOT/pages/proxies.adoc index 628691865..1e8704a2b 100644 --- a/docs/modules/ROOT/pages/proxies.adoc +++ b/docs/modules/ROOT/pages/proxies.adoc @@ -1,8 +1,6 @@ = Proxies -____ -Expect rapid iteration as this pattern matures and more patterns potentially emerge. -____ +NOTE: Expect rapid iteration as this pattern matures and more patterns potentially emerge. == Table of Contents @@ -101,10 +99,10 @@ This allows developers to add features, update logic, and fix bugs without touch The link:../src/openzeppelin/upgrades/Proxy.cairo[Proxy contract] includes two core methods: -. The `__default__` method is a fallback method that redirects a function call and associated calldata to the implementation contract. -. The `__l1_default__` method is also a fallback method; +. The `\\__default__` method is a fallback method that redirects a function call and associated calldata to the implementation contract. +. The `\\__l1_default__` method is also a fallback method; however, it redirects the function call and associated calldata to a layer one contract. -In order to invoke `__l1_default__`, the original function call must include the library function `send_message_to_l1`. +In order to invoke `\\__l1_default__`, the original function call must include the library function `send_message_to_l1`. See Cairo's https://www.cairo-lang.org/docs/hello_starknet/l1l2.html[Interacting with L1 contracts] for more information. Since this proxy is designed to work both as an https://eips.ethereum.org/EIPS/eip-1822[UUPS-flavored upgrade proxy] as well as a non-upgradeable proxy, it does not know how to handle its own state. @@ -136,11 +134,9 @@ Instead, the implementation contract should be declared (which creates a `Declar * set its initial state with a traditional constructor (decorated with `@constructor`). Instead, use an initializer method that invokes the Proxy `constructor`. -____ -Note that the Proxy `constructor` includes a check the ensures the initializer can only be called once; +NOTE: The Proxy `constructor` includes a check the ensures the initializer can only be called once; however, `_set_implementation` does not include this check. It's up to the developers to protect their implementation contract's upgradeability with access controls such as <>. -____ For a full implementation contract example, please see: diff --git a/docs/modules/ROOT/pages/security.adoc b/docs/modules/ROOT/pages/security.adoc index 29ae2a571..1aad22e53 100644 --- a/docs/modules/ROOT/pages/security.adoc +++ b/docs/modules/ROOT/pages/security.adoc @@ -2,9 +2,7 @@ The following documentation provides context, reasoning, and examples of methods and constants found in `openzeppelin/security/`. -____ -Expect this module to evolve. -____ +CAUTION: Expect this module to evolve. == Table of Contents @@ -39,9 +37,7 @@ func foo{ end ---- -____ -Please note that this Initializable pattern should only be used on one function. -____ +CAUTION: This Initializable pattern should only be used on one function. == Pausable @@ -82,10 +78,8 @@ func whenPaused{ end ---- -____ -Note that `pause` and `unpause` already include these assertions. +NOTE: `pause` and `unpause` already include these assertions. In other words, `pause` cannot be invoked when already paused and vice versa. -____ For a list of full implementations utilizing the Pausable library, see: diff --git a/docs/modules/ROOT/pages/utilities.adoc b/docs/modules/ROOT/pages/utilities.adoc index d7ed69962..e059df82b 100644 --- a/docs/modules/ROOT/pages/utilities.adoc +++ b/docs/modules/ROOT/pages/utilities.adoc @@ -2,9 +2,7 @@ The following documentation provides context, reasoning, and examples for methods and constants found in `tests/utils.py`. -____ -Expect this module to evolve (as it has already done). -____ +CAUTION: Expect this module to evolve (as it has already done). == Table of Contents @@ -285,5 +283,5 @@ def foo_factory(contract_classes, foo_init): == MockSigner `MockSigner` is used to perform transactions with an instance of https://github.com/OpenZeppelin/nile/blob/main/src/nile/signer.py[Nile's Signer] on a given Account, crafting the transaction and managing nonces. -The `Signer` instance manages signatures and is leveraged by `MockSigner` to operate with the Account contract's `__execute__` method. +The `Signer` instance manages signatures and is leveraged by `MockSigner` to operate with the Account contract's `\\__execute__` method. See link:../docs/Account.md#mocksigner-utility[MockSigner utility] for more information. From 15374dfaba16b9e7279ab692ecbccfb8fe117f47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Fri, 15 Jul 2022 14:02:30 -0300 Subject: [PATCH 10/39] update navbar --- docs/modules/ROOT/nav.adoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 18080a795..4d716291a 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -1,6 +1,4 @@ -* xref:contracts::index.adoc[Contracts for Solidity] * xref:index.adoc[Overview] -* https://wizard.openzeppelin.com/cairo[Wizard] * xref:extensibility.adoc[Extensibility] * xref:proxies.adoc[Proxies and Upgrades] @@ -14,3 +12,6 @@ * xref:security.adoc[Security] * xref:introspection.adoc[Introspection] * xref:utilities.adoc[Utilities] + +* xref:contracts::index.adoc[Contracts for Solidity] +* https://wizard.openzeppelin.com/cairo[Wizard] \ No newline at end of file From c03729bab36fcbdf62c9b616c29725c69ce88844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Fri, 15 Jul 2022 14:18:10 -0300 Subject: [PATCH 11/39] merge access control docs --- docs/modules/ROOT/pages/access.adoc | 565 +++++++++++++++++- src/openzeppelin/access/accesscontrol.cairo | 2 +- src/openzeppelin/access/ownable.cairo | 2 +- src/openzeppelin/account/Account.cairo | 2 +- .../account/AddressRegistry.cairo | 2 +- src/openzeppelin/account/EthAccount.cairo | 2 +- src/openzeppelin/account/IAccount.cairo | 2 +- src/openzeppelin/account/library.cairo | 2 +- src/openzeppelin/introspection/ERC165.cairo | 2 +- src/openzeppelin/introspection/IERC165.cairo | 2 +- src/openzeppelin/security/initializable.cairo | 2 +- src/openzeppelin/security/pausable.cairo | 2 +- .../security/reentrancyguard.cairo | 2 +- src/openzeppelin/security/safemath.cairo | 2 +- src/openzeppelin/token/erc20/ERC20.cairo | 2 +- .../token/erc20/ERC20_Mintable.cairo | 2 +- .../token/erc20/ERC20_Pausable.cairo | 2 +- .../token/erc20/ERC20_Upgradeable.cairo | 2 +- .../token/erc20/interfaces/IERC20.cairo | 2 +- src/openzeppelin/token/erc20/library.cairo | 2 +- .../erc721/ERC721_Mintable_Burnable.cairo | 2 +- .../erc721/ERC721_Mintable_Pausable.cairo | 2 +- .../token/erc721/interfaces/IERC721.cairo | 2 +- .../erc721/interfaces/IERC721_Metadata.cairo | 2 +- .../erc721/interfaces/IERC721_Receiver.cairo | 2 +- src/openzeppelin/token/erc721/library.cairo | 2 +- .../token/erc721/utils/ERC721_Holder.cairo | 2 +- .../ERC721_Enumerable_Mintable_Burnable.cairo | 2 +- .../interfaces/IERC721_Enumerable.cairo | 2 +- .../token/erc721_enumerable/library.cairo | 2 +- src/openzeppelin/upgrades/Proxy.cairo | 2 +- src/openzeppelin/upgrades/library.cairo | 2 +- src/openzeppelin/utils/constants.cairo | 2 +- 33 files changed, 595 insertions(+), 34 deletions(-) diff --git a/docs/modules/ROOT/pages/access.adoc b/docs/modules/ROOT/pages/access.adoc index d858c645a..380668791 100644 --- a/docs/modules/ROOT/pages/access.adoc +++ b/docs/modules/ROOT/pages/access.adoc @@ -19,6 +19,25 @@ It is therefore critical to understand how you implement it, lest someone else h *** <> ** <> *** <> +* <> + ** <> + ** <> + ** <> + ** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + ** <> + *** <> + *** <> + *** <> == Ownable @@ -65,7 +84,7 @@ end === Ownable library API -[.hljs-theme-light.nopadding,cairo] +[.hljs-theme-light.nopaddingq,cairo] ---- func initializer(owner: felt): end @@ -169,7 +188,7 @@ None. ==== `_transfer_ownership` Transfers ownership of the contract to a new account (`new_owner`). -Unprotected method without access restriction. +link:./Extensibility.md#the-pattern[`internal`] function without access restriction. Emits a <> event. @@ -203,3 +222,545 @@ Parameters: previousOwner: felt newOwner: felt ---- + +== AccessControl + +While the simplicity of ownership can be useful for simple systems or quick prototyping, different levels of authorization are often needed. +You may want for an account to have permission to ban users from a system, but not create new tokens. +https://en.wikipedia.org/wiki/Role-based_access_control[Role-Based Access Control (RBAC)] offers flexibility in this regard. + +In essence, we will be defining multiple roles, each allowed to perform different sets of actions. +An account may have, for example, 'moderator', 'minter' or 'admin' roles, which you will then check for instead of simply using <>. +This check can be enforced through <>. +Separately, you will be able to define rules for how accounts can be granted a role, have it revoked, and more. + +Most software uses access control systems that are role-based: some users are regular users, some may be supervisors or managers, and a few will often have administrative privileges. + +=== Usage + +For each role that you want to define, you will create a new _role identifier_ that is used to grant, revoke, and check if an account has that role (see <> for information on creating identifiers). + +Here's a simple example of implementing `AccessControl` on a portion of an link:../src/openzeppelin/token/erc20/ERC20.cairo[ERC20 token contract] which defines and sets the 'minter' role: + +[,cairo] +---- +from openzeppelin.token.erc20.library import ERC20 + +from openzeppelin.access.accesscontrol import AccessControl + + +const MINTER_ROLE = 0x4f96f87f6963bb246f2c30526628466840c642dc5c50d5a67777c6cc0e44ab5 + +@constructor +func constructor{ + syscall_ptr: felt*, + pedersen_ptr: HashBuiltin*, + range_check_ptr + }( + name: felt, + symbol: felt, + decimals: felt, + minter: felt + ): + ERC20.initializer(name, symbol, decimals) + AccessControl.initializer() + AccessControl._grant_role(MINTER_ROLE, minter) + return () +end + +@external +func mint{ + syscall_ptr: felt*, + pedersen_ptr: HashBuiltin*, + range_check_ptr + }(to: felt, amount: Uint256): + AccessControl.assert_only_role(MINTER_ROLE) + ERC20._mint(to, amount) + return () +end +---- + +CAUTION: Make sure you fully understand how <> works before using it on your system, or copy-pasting the examples from this guide. + +While clear and explicit, this isn't anything we wouldn't have been able to achieve with <>. +Indeed, where `AccessControl` shines is in scenarios where granular permissions are required, which can be implemented by defining _multiple_ roles. + +Let's augment our ERC20 token example by also defining a 'burner' role, which lets accounts destroy tokens, and by using `assert_only_role`: + +[,cairo] +---- +from openzeppelin.token.erc20.library import ERC20 + +from openzeppelin.access.accesscontrol import AccessControl + + +const MINTER_ROLE = 0x4f96f87f6963bb246f2c30526628466840c642dc5c50d5a67777c6cc0e44ab5 +const BURNER_ROLE = 0x7823a2d975ffa03bed39c38809ec681dc0ae931ebe0048c321d4a8440aed509 + +@constructor +func constructor{ + syscall_ptr: felt*, + pedersen_ptr: HashBuiltin*, + range_check_ptr + }( + name: felt, + symbol: felt, + decimals: felt, + minter: felt, + burner: felt + ): + ERC20.initializer(name, symbol, decimals) + AccessControl.initializer() + AccessControl._grant_role(MINTER_ROLE, minter) + AccessControl._grant_role(BURNER_ROLE, burner) + return () +end + +@external +func mint{ + syscall_ptr: felt*, + pedersen_ptr: HashBuiltin*, + range_check_ptr + }(to: felt, amount: Uint256): + AccessControl.assert_only_role(MINTER_ROLE) + ERC20._mint(to, amount) + return () +end + +@external +func burn{ + syscall_ptr: felt*, + pedersen_ptr: HashBuiltin*, + range_check_ptr + }(from_: felt, amount: Uint256): + AccessControl.assert_only_role(BURNER_ROLE) + ERC20._burn(from_, amount) + return () +end +---- + +So clean! +By splitting concerns this way, more granular levels of permission may be implemented than were possible with the simpler ownership approach to access control. +Limiting what each component of a system is able to do is known as the https://en.wikipedia.org/wiki/Principle_of_least_privilege[principle of least privilege], and is a good security practice. +Note that each account may still have more than one role, if so desired. + +=== Granting and revoking roles + +The ERC20 token example above uses `_grant_role`, an link:./Extensibility.md#the-pattern[`internal`] function that is useful when programmatically assigning roles (such as during construction). +But what if we later want to grant the 'minter' role to additional accounts? + +By default, *accounts with a role cannot grant it or revoke it from other accounts*: all having a role does is making the `assert_only_role` check pass. +To grant and revoke roles dynamically, you will need help from the role's _admin_. + +Every role has an associated admin role, which grants permission to call the `grant_role` and `revoke_role` functions. +A role can be granted or revoked by using these if the calling account has the corresponding admin role. +Multiple roles may have the same admin role to make management easier. +A role's admin can even be the same role itself, which would cause accounts with that role to be able to also grant and revoke it. + +This mechanism can be used to create complex permissioning structures resembling organizational charts, but it also provides an easy way to manage simpler applications. +`AccessControl` includes a special role with the role identifier of `0`, called `DEFAULT_ADMIN_ROLE`, which acts as the *default admin role for all roles*. +An account with this role will be able to manage any other role, unless `_set_role_admin` is used to select a new admin role. + +Let's take a look at the ERC20 token example, this time taking advantage of the default admin role: + +[,cairo] +---- +from openzeppelin.token.erc20.library import ERC20 + +from openzeppelin.access.accesscontrol import AccessControl + +from openzeppelin.utils.constants import DEFAULT_ADMIN_ROLE + + +const MINTER_ROLE = 0x4f96f87f6963bb246f2c30526628466840c642dc5c50d5a67777c6cc0e44ab5 +const BURNER_ROLE = 0x7823a2d975ffa03bed39c38809ec681dc0ae931ebe0048c321d4a8440aed509 + +@constructor +func constructor{ + syscall_ptr: felt*, + pedersen_ptr: HashBuiltin*, + range_check_ptr + }( + name: felt, + symbol: felt, + decimals: felt, + admin: felt, + ): + ERC20.initializer(name, symbol, decimals) + AccessControl.initializer() + + AccessControl._grant_role(DEFAULT_ADMIN_ROLE, admin) + return () +end + +@external +func mint{ + syscall_ptr: felt*, + pedersen_ptr: HashBuiltin*, + range_check_ptr + }(to: felt, amount: Uint256): + AccessControl.assert_only_role(MINTER_ROLE) + ERC20._mint(to, amount) + return () +end + +@external +func burn{ + syscall_ptr: felt*, + pedersen_ptr: HashBuiltin*, + range_check_ptr + }(from_: felt, amount: Uint256): + AccessControl.assert_only_role(BURNER_ROLE) + ERC20._burn(from_, amount) + return () +end +---- + +Note that, unlike the previous examples, no accounts are granted the 'minter' or 'burner' roles. +However, because those roles' admin role is the default admin role, and that role was granted to the 'admin', that same account can call `grant_role` to give minting or burning permission, and `revoke_role` to remove it. + +Dynamic role allocation is often a desirable property, for example in systems where trust in a participant may vary over time. +It can also be used to support use cases such as https://en.wikipedia.org/wiki/Know_your_customer[KYC], where the list of role-bearers may not be known up-front, or may be prohibitively expensive to include in a single transaction. + +The following example uses the link:../tests/mocks/AccessControl.cairo[AccessControl mock contract] which exposes the role management functions. +To grant and revoke roles in Python, for example: + +[,python] +---- +MINTER_ROLE = 0x4f96f87f6963bb246f2c30526628466840c642dc5c50d5a67777c6cc0e44ab5 +BURNER_ROLE = 0x7823a2d975ffa03bed39c38809ec681dc0ae931ebe0048c321d4a8440aed509 + +# grants MINTER_ROLE and BURNER_ROLE to account1 and account2 respectively +await signer.send_transactions( + admin, [ + (accesscontrol.contract_address, 'grantRole', [MINTER_ROLE, account1.contract_address]), + (accesscontrol.contract_address, 'grantRole', [BURNER_ROLE, account2.contract_address]) + ] +) + +# revokes MINTER_ROLE from account1 +await signer.send_transaction( + admin, + accesscontrol.contract_address, + 'revokeRole', + [MINTER_ROLE, account1.contract_address] +) +---- + +=== Creating role identifiers + +In the Solidity implementation of AccessControl, contracts generally refer to the https://docs.soliditylang.org/en/latest/units-and-global-variables.html?highlight=keccak256#mathematical-and-cryptographic-functions[keccak256 hash] of a role as the role identifier. +For example: + +[,solidity] +---- +bytes32 public constant SOME_ROLE = keccak256("SOME_ROLE") +---- + +These identifiers take up 32 bytes (256 bits). + +Cairo field elements store a maximum of 252 bits. +Even further, a declared _constant_ field element in a StarkNet contract stores even less (see https://github.com/starkware-libs/cairo-lang/blob/167b28bcd940fd25ea3816204fa882a0b0a49603/src/starkware/cairo/lang/cairo_constants.py#L1[cairo_constants]). +With this discrepancy, this library maintains an agnostic stance on how contracts should create identifiers. +Some ideas to consider: + +* using the first or last 251 bits of keccak256 hash digests +* using Cairo's https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/cairo/common/hash.cairo[hash2] + +=== AccessControl library API + +[,cairo] +---- +func initializer(): +end + +func assert_only_role(role: felt): +end + +func has_role(role: felt, user: felt) -> (has_role: felt): +end + +func get_role_admin(role: felt) -> (admin: felt): +end + +func grant_role(role: felt, user: felt): +end + +func revoke_role(role: felt, user: felt): +end + +func renounce_role(role: felt, user: felt): +end + +func _grant_role(role: felt, user: felt): +end + +func _revoke_role(role: felt, user: felt): +end + +func _set_role_admin(role: felt, admin_role: felt): +end +---- + +[#initializer-accesscontrol] +==== `initializer` + +Initializes AccessControl and should be called in the implementing contract's constructor. + +This must only be called once. + +Parameters: + +None. + +Returns: + +None. + +==== `assert_only_role` + +Checks that an account has a specific role. +Reverts with a message including the required role. + +Parameters: + +[,cairo] +---- +role: felt +---- + +Returns: + +None. + +==== has_role + +Returns `TRUE` if `user` has been granted `role`, `FALSE` otherwise. + +Parameters: + +[,cairo] +---- +role: felt +user: felt +---- + +Returns: + +[,cairo] +---- +has_role: felt +---- + +==== `get_role_admin` + +Returns the admin role that controls `role`. +See <> and <>. + +To change a role's admin, use <<_set_role_admin,_set_role_admin>>. + +Parameters: + +[,cairo] +---- +role: felt +---- + +Returns: + +[,cairo] +---- +admin: felt +---- + +==== `grant_role` + +Grants `role` to `user`. + +If `user` had not been already granted `role`, emits a <> event. + +Requirements: + +* the caller must have ``role``'s admin role. + +Parameters: + +[,cairo] +---- +role: felt +user: felt +---- + +Returns: + +None. + +==== `revoke_role` + +Revokes `role` from `user`. + +If `user` had been granted `role`, emits a <> event. + +Requirements: + +* the caller must have ``role``'s admin role. + +Parameters: + +[,cairo] +---- +role: felt +user: felt +---- + +Returns: + +None. + +==== `renounce_role` + +Revokes `role` from the calling `user`. + +Roles are often managed via <> and <>: this function's purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). + +If the calling `user` had been revoked `role`, emits a <> event. + +Requirements: + +* the caller must be `user`. + +Parameters: + +[,cairo] +---- +role: felt +user: felt +---- + +Returns: + +None. + +[#grantrole-internal] +==== `_grant_role` + +Grants `role` to `user`. + +link:./Extensibility.md#the-pattern[`internal`] function without access restriction. + +Emits a <> event. + +Parameters: + +[,cairo] +---- +role: felt +user: felt +---- + +Returns: + +None. + +[#revokerole-internal] +==== `_revoke_role` + +Revokes `role` from `user`. + +link:./Extensibility.md#the-pattern[`internal`] function without access restriction. + +Emits a <> event. + +Parameters: + +[,cairo] +---- +role: felt +user: felt +---- + +Returns: + +None. + +[#setroleadmin] +==== `_set_role_admin` + +link:./Extensibility.md#the-pattern[`internal`] function that sets `admin_role` as ``role``'s admin role. + +Emits a <> event. + +Parameters: + +[,cairo] +---- +role: felt +admin_role: felt +---- + +Returns: + +None. + +=== AccessControl events + +[,cairo] +---- +func RoleGranted(role: felt, account: felt, sender: felt): +end + +func RoleRevoked(role: felt, account: felt, sender: felt): +end + +func RoleAdminChanged( + role: felt, + previousAdminRole: felt, + newAdminRole: felt + ): +end +---- + +==== `RoleGranted` + +Emitted when `account` is granted `role`. + +`sender` is the account that originated the contract call, an admin role bearer. + +Parameters: + +[,cairo] +---- +role: felt +account: felt +sender: felt +---- + +==== `RoleRevoked` + +Emitted when account is revoked role. + +`sender` is the account that originated the contract call: + +* if using <>, it is the admin role bearer +* if using <>, it is the role bearer (i.e. +`account`). + +[,cairo] +---- +role: felt +account: felt +sender: felt +---- + +==== `RoleAdminChanged` + +Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` + +`DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite `RoleAdminChanged` not being emitted signaling this. + +[,cairo] +---- +role: felt +previousAdminRole: felt +newAdminRole: felt +---- diff --git a/src/openzeppelin/access/accesscontrol.cairo b/src/openzeppelin/access/accesscontrol.cairo index b71c51c9a..7ddce3b0a 100644 --- a/src/openzeppelin/access/accesscontrol.cairo +++ b/src/openzeppelin/access/accesscontrol.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Cairo Contracts v0.2.0 (access/accesscontrol.cairo) +# OpenZeppelin Cairo Contracts v0.2.1 (access/accesscontrol.cairo) %lang starknet diff --git a/src/openzeppelin/access/ownable.cairo b/src/openzeppelin/access/ownable.cairo index ee32f3d8a..8b142f67b 100644 --- a/src/openzeppelin/access/ownable.cairo +++ b/src/openzeppelin/access/ownable.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (access/ownable.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (access/ownable.cairo) %lang starknet diff --git a/src/openzeppelin/account/Account.cairo b/src/openzeppelin/account/Account.cairo index 3b08dca92..af78fbbd1 100644 --- a/src/openzeppelin/account/Account.cairo +++ b/src/openzeppelin/account/Account.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (account/Account.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (account/Account.cairo) %lang starknet diff --git a/src/openzeppelin/account/AddressRegistry.cairo b/src/openzeppelin/account/AddressRegistry.cairo index 5ec8c5d0f..c001af637 100644 --- a/src/openzeppelin/account/AddressRegistry.cairo +++ b/src/openzeppelin/account/AddressRegistry.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (account/AddressRegistry.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (account/AddressRegistry.cairo) %lang starknet diff --git a/src/openzeppelin/account/EthAccount.cairo b/src/openzeppelin/account/EthAccount.cairo index 235f5b491..26e1300d9 100644 --- a/src/openzeppelin/account/EthAccount.cairo +++ b/src/openzeppelin/account/EthAccount.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (account/EthAccount.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (account/EthAccount.cairo) %lang starknet from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin, BitwiseBuiltin diff --git a/src/openzeppelin/account/IAccount.cairo b/src/openzeppelin/account/IAccount.cairo index 94a7f1027..d403819bd 100644 --- a/src/openzeppelin/account/IAccount.cairo +++ b/src/openzeppelin/account/IAccount.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (account/IAccount.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (account/IAccount.cairo) %lang starknet diff --git a/src/openzeppelin/account/library.cairo b/src/openzeppelin/account/library.cairo index 06f95b872..149b86eae 100644 --- a/src/openzeppelin/account/library.cairo +++ b/src/openzeppelin/account/library.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (account/library.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (account/library.cairo) %lang starknet diff --git a/src/openzeppelin/introspection/ERC165.cairo b/src/openzeppelin/introspection/ERC165.cairo index a8178492b..f6302d54e 100644 --- a/src/openzeppelin/introspection/ERC165.cairo +++ b/src/openzeppelin/introspection/ERC165.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (introspection/ERC165.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (introspection/ERC165.cairo) %lang starknet diff --git a/src/openzeppelin/introspection/IERC165.cairo b/src/openzeppelin/introspection/IERC165.cairo index f68d76a5e..17673b474 100644 --- a/src/openzeppelin/introspection/IERC165.cairo +++ b/src/openzeppelin/introspection/IERC165.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (introspection/IERC165.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (introspection/IERC165.cairo) %lang starknet diff --git a/src/openzeppelin/security/initializable.cairo b/src/openzeppelin/security/initializable.cairo index 083610ad2..c6504602f 100644 --- a/src/openzeppelin/security/initializable.cairo +++ b/src/openzeppelin/security/initializable.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (security/initializable.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (security/initializable.cairo) %lang starknet diff --git a/src/openzeppelin/security/pausable.cairo b/src/openzeppelin/security/pausable.cairo index 154e1e4fc..2cebcab73 100644 --- a/src/openzeppelin/security/pausable.cairo +++ b/src/openzeppelin/security/pausable.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (security/pausable.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (security/pausable.cairo) %lang starknet diff --git a/src/openzeppelin/security/reentrancyguard.cairo b/src/openzeppelin/security/reentrancyguard.cairo index 76be091a0..02d0b938a 100644 --- a/src/openzeppelin/security/reentrancyguard.cairo +++ b/src/openzeppelin/security/reentrancyguard.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (security/reentrancyguard.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (security/reentrancyguard.cairo) %lang starknet diff --git a/src/openzeppelin/security/safemath.cairo b/src/openzeppelin/security/safemath.cairo index f17fba642..a07525373 100644 --- a/src/openzeppelin/security/safemath.cairo +++ b/src/openzeppelin/security/safemath.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (security/safemath.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (security/safemath.cairo) %lang starknet diff --git a/src/openzeppelin/token/erc20/ERC20.cairo b/src/openzeppelin/token/erc20/ERC20.cairo index ebfda8991..35735138d 100644 --- a/src/openzeppelin/token/erc20/ERC20.cairo +++ b/src/openzeppelin/token/erc20/ERC20.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (token/erc20/ERC20.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (token/erc20/ERC20.cairo) %lang starknet diff --git a/src/openzeppelin/token/erc20/ERC20_Mintable.cairo b/src/openzeppelin/token/erc20/ERC20_Mintable.cairo index 874761fba..ead9170f8 100644 --- a/src/openzeppelin/token/erc20/ERC20_Mintable.cairo +++ b/src/openzeppelin/token/erc20/ERC20_Mintable.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (token/erc20/ERC20_Mintable.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (token/erc20/ERC20_Mintable.cairo) %lang starknet diff --git a/src/openzeppelin/token/erc20/ERC20_Pausable.cairo b/src/openzeppelin/token/erc20/ERC20_Pausable.cairo index b9617d8bb..573e52582 100644 --- a/src/openzeppelin/token/erc20/ERC20_Pausable.cairo +++ b/src/openzeppelin/token/erc20/ERC20_Pausable.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (token/erc20/ERC20_Pausable.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (token/erc20/ERC20_Pausable.cairo) %lang starknet diff --git a/src/openzeppelin/token/erc20/ERC20_Upgradeable.cairo b/src/openzeppelin/token/erc20/ERC20_Upgradeable.cairo index bcd5ad1b5..24940e212 100644 --- a/src/openzeppelin/token/erc20/ERC20_Upgradeable.cairo +++ b/src/openzeppelin/token/erc20/ERC20_Upgradeable.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (token/erc20/ERC20_Upgradeable.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (token/erc20/ERC20_Upgradeable.cairo) %lang starknet %builtins pedersen range_check diff --git a/src/openzeppelin/token/erc20/interfaces/IERC20.cairo b/src/openzeppelin/token/erc20/interfaces/IERC20.cairo index c2c7bb9b9..3eb543aed 100644 --- a/src/openzeppelin/token/erc20/interfaces/IERC20.cairo +++ b/src/openzeppelin/token/erc20/interfaces/IERC20.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (token/erc20/interfaces/IERC20.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (token/erc20/interfaces/IERC20.cairo) %lang starknet diff --git a/src/openzeppelin/token/erc20/library.cairo b/src/openzeppelin/token/erc20/library.cairo index b5a0fdf2a..29383204c 100644 --- a/src/openzeppelin/token/erc20/library.cairo +++ b/src/openzeppelin/token/erc20/library.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (token/erc20/library.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (token/erc20/library.cairo) %lang starknet diff --git a/src/openzeppelin/token/erc721/ERC721_Mintable_Burnable.cairo b/src/openzeppelin/token/erc721/ERC721_Mintable_Burnable.cairo index 4dfb50569..49fea3a8a 100644 --- a/src/openzeppelin/token/erc721/ERC721_Mintable_Burnable.cairo +++ b/src/openzeppelin/token/erc721/ERC721_Mintable_Burnable.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (token/erc721/ERC721_Mintable_Burnable.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (token/erc721/ERC721_Mintable_Burnable.cairo) %lang starknet diff --git a/src/openzeppelin/token/erc721/ERC721_Mintable_Pausable.cairo b/src/openzeppelin/token/erc721/ERC721_Mintable_Pausable.cairo index e3066c2d8..170695de1 100644 --- a/src/openzeppelin/token/erc721/ERC721_Mintable_Pausable.cairo +++ b/src/openzeppelin/token/erc721/ERC721_Mintable_Pausable.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (token/erc721/ERC721_Mintable_Pausable.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (token/erc721/ERC721_Mintable_Pausable.cairo) %lang starknet diff --git a/src/openzeppelin/token/erc721/interfaces/IERC721.cairo b/src/openzeppelin/token/erc721/interfaces/IERC721.cairo index beb8d8088..5cfa80998 100644 --- a/src/openzeppelin/token/erc721/interfaces/IERC721.cairo +++ b/src/openzeppelin/token/erc721/interfaces/IERC721.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (token/erc721/interfaces/IERC721.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (token/erc721/interfaces/IERC721.cairo) %lang starknet diff --git a/src/openzeppelin/token/erc721/interfaces/IERC721_Metadata.cairo b/src/openzeppelin/token/erc721/interfaces/IERC721_Metadata.cairo index 8e57a976b..ef4b2bc52 100644 --- a/src/openzeppelin/token/erc721/interfaces/IERC721_Metadata.cairo +++ b/src/openzeppelin/token/erc721/interfaces/IERC721_Metadata.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (token/erc721/interfaces/IERC721_Metadata.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (token/erc721/interfaces/IERC721_Metadata.cairo) %lang starknet diff --git a/src/openzeppelin/token/erc721/interfaces/IERC721_Receiver.cairo b/src/openzeppelin/token/erc721/interfaces/IERC721_Receiver.cairo index f73f51dcb..0e7178983 100644 --- a/src/openzeppelin/token/erc721/interfaces/IERC721_Receiver.cairo +++ b/src/openzeppelin/token/erc721/interfaces/IERC721_Receiver.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (token/erc721/interfaces/IERC721_Receiver.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (token/erc721/interfaces/IERC721_Receiver.cairo) %lang starknet diff --git a/src/openzeppelin/token/erc721/library.cairo b/src/openzeppelin/token/erc721/library.cairo index 7874245d8..1dc352779 100644 --- a/src/openzeppelin/token/erc721/library.cairo +++ b/src/openzeppelin/token/erc721/library.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (token/erc721/library.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (token/erc721/library.cairo) %lang starknet diff --git a/src/openzeppelin/token/erc721/utils/ERC721_Holder.cairo b/src/openzeppelin/token/erc721/utils/ERC721_Holder.cairo index deae9bef7..9524f7fb3 100644 --- a/src/openzeppelin/token/erc721/utils/ERC721_Holder.cairo +++ b/src/openzeppelin/token/erc721/utils/ERC721_Holder.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (token/erc721/utils/ERC721_Holder.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (token/erc721/utils/ERC721_Holder.cairo) %lang starknet diff --git a/src/openzeppelin/token/erc721_enumerable/ERC721_Enumerable_Mintable_Burnable.cairo b/src/openzeppelin/token/erc721_enumerable/ERC721_Enumerable_Mintable_Burnable.cairo index b3769006b..9218003cb 100644 --- a/src/openzeppelin/token/erc721_enumerable/ERC721_Enumerable_Mintable_Burnable.cairo +++ b/src/openzeppelin/token/erc721_enumerable/ERC721_Enumerable_Mintable_Burnable.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (token/erc721_enumerable/ERC721_Enumerable_Mintable_Burnable.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (token/erc721_enumerable/ERC721_Enumerable_Mintable_Burnable.cairo) %lang starknet diff --git a/src/openzeppelin/token/erc721_enumerable/interfaces/IERC721_Enumerable.cairo b/src/openzeppelin/token/erc721_enumerable/interfaces/IERC721_Enumerable.cairo index f4607e01b..0bf95b6a7 100644 --- a/src/openzeppelin/token/erc721_enumerable/interfaces/IERC721_Enumerable.cairo +++ b/src/openzeppelin/token/erc721_enumerable/interfaces/IERC721_Enumerable.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (token/erc721_enumerable/interfaces/IERC721_Enumerable.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (token/erc721_enumerable/interfaces/IERC721_Enumerable.cairo) %lang starknet diff --git a/src/openzeppelin/token/erc721_enumerable/library.cairo b/src/openzeppelin/token/erc721_enumerable/library.cairo index c315181e7..fda447356 100644 --- a/src/openzeppelin/token/erc721_enumerable/library.cairo +++ b/src/openzeppelin/token/erc721_enumerable/library.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (token/erc721_enumerable/library.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (token/erc721_enumerable/library.cairo) %lang starknet diff --git a/src/openzeppelin/upgrades/Proxy.cairo b/src/openzeppelin/upgrades/Proxy.cairo index 026e792f9..e38d858b9 100644 --- a/src/openzeppelin/upgrades/Proxy.cairo +++ b/src/openzeppelin/upgrades/Proxy.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (upgrades/Proxy.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (upgrades/Proxy.cairo) %lang starknet diff --git a/src/openzeppelin/upgrades/library.cairo b/src/openzeppelin/upgrades/library.cairo index ac4ccf03d..04755031d 100644 --- a/src/openzeppelin/upgrades/library.cairo +++ b/src/openzeppelin/upgrades/library.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (upgrades/library.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (upgrades/library.cairo) %lang starknet diff --git a/src/openzeppelin/utils/constants.cairo b/src/openzeppelin/utils/constants.cairo index d9fed7d45..e1e2fde1c 100644 --- a/src/openzeppelin/utils/constants.cairo +++ b/src/openzeppelin/utils/constants.cairo @@ -1,5 +1,5 @@ # SPDX-License-Identifier: MIT -# OpenZeppelin Contracts for Cairo v0.2.0 (utils/constants.cairo) +# OpenZeppelin Contracts for Cairo v0.2.1 (utils/constants.cairo) %lang starknet From 54ccec50deb43bf9f5a77ee09158809c4cd18dfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Fri, 22 Jul 2022 20:19:46 +0200 Subject: [PATCH 12/39] Apply suggestions from code review Co-authored-by: Andrew Fleming --- docs/modules/ROOT/pages/erc20.adoc | 2 +- docs/modules/ROOT/pages/utilities.adoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/erc20.adoc b/docs/modules/ROOT/pages/erc20.adoc index 194f88f45..c670341e7 100644 --- a/docs/modules/ROOT/pages/erc20.adoc +++ b/docs/modules/ROOT/pages/erc20.adoc @@ -149,7 +149,7 @@ func transfer{ pedersen_ptr : HashBuiltin*, range_check_ptr }(recipient: felt, amount: Uint256) -> (success: felt): - Pausable_when_not_paused() # imported extended logic + Pausable.assert_not_paused() # imported extended logic ERC20_transfer(recipient, amount) # imported library method return (TRUE) end diff --git a/docs/modules/ROOT/pages/utilities.adoc b/docs/modules/ROOT/pages/utilities.adoc index e059df82b..1b79b9782 100644 --- a/docs/modules/ROOT/pages/utilities.adoc +++ b/docs/modules/ROOT/pages/utilities.adoc @@ -79,7 +79,7 @@ print(x) === `from_uint` -Converts a uin256-ish tuple into an integer. +Converts a uint256-ish tuple into an integer. [,python] ---- From 797d2b3d23c38da2f5f208e8871193ff913573f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Fri, 22 Jul 2022 16:10:51 -0300 Subject: [PATCH 13/39] remove unused style. try toc clause --- docs/modules/ROOT/pages/access.adoc | 35 ++--------------------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/docs/modules/ROOT/pages/access.adoc b/docs/modules/ROOT/pages/access.adoc index a6ba0f3df..b13e2b1dc 100644 --- a/docs/modules/ROOT/pages/access.adoc +++ b/docs/modules/ROOT/pages/access.adoc @@ -6,38 +6,7 @@ Access control--that is, "who is allowed to do this thing"--is incredibly import The access control of your contract may govern who can mint tokens, vote on proposals, freeze transfers, and many other things. It is therefore critical to understand how you implement it, lest someone else https://blog.openzeppelin.com/on-the-parity-wallet-multisig-hack-405a8c12e8f7/[steals your whole system]. -== Table of Contents - -* <> - ** <> - ** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - ** <> - *** <> -* <> - ** <> - ** <> - ** <> - ** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - ** <> - *** <> - *** <> - *** <> +:toc: == Ownable @@ -84,7 +53,7 @@ end === Ownable library API -[.hljs-theme-light.nopaddingq,cairo] +[,cairo] ---- func initializer(owner: felt): end From 62efa2d0a9e9841ddeb579ac7f2cc893ecdea456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Fri, 22 Jul 2022 16:25:54 -0300 Subject: [PATCH 14/39] return toc. fix links for access --- docs/modules/ROOT/pages/access.adoc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/access.adoc b/docs/modules/ROOT/pages/access.adoc index b13e2b1dc..471f377fa 100644 --- a/docs/modules/ROOT/pages/access.adoc +++ b/docs/modules/ROOT/pages/access.adoc @@ -6,7 +6,18 @@ Access control--that is, "who is allowed to do this thing"--is incredibly import The access control of your contract may govern who can mint tokens, vote on proposals, freeze transfers, and many other things. It is therefore critical to understand how you implement it, lest someone else https://blog.openzeppelin.com/on-the-parity-wallet-multisig-hack-405a8c12e8f7/[steals your whole system]. -:toc: +== Table of Contents + +* <> + ** <> + ** <> + ** <> +* <> + ** <> + ** <> + ** <> + ** <> + ** <> == Ownable From c3998d9933ce9b3eb47e3468eade3f84cc9365a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Sat, 23 Jul 2022 14:21:20 -0300 Subject: [PATCH 15/39] fix access links --- docs/modules/ROOT/pages/access.adoc | 83 +++++++++++++++-------------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/docs/modules/ROOT/pages/access.adoc b/docs/modules/ROOT/pages/access.adoc index 471f377fa..4c8e5759f 100644 --- a/docs/modules/ROOT/pages/access.adoc +++ b/docs/modules/ROOT/pages/access.adoc @@ -1,3 +1,7 @@ +:ownable-cairo: link:https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/access/ownable.cairo[Ownable] + +:extensibility-pattern: xref:extensibility.adoc#the-pattern + = Access CAUTION: Expect these modules to evolve. @@ -8,28 +12,28 @@ It is therefore critical to understand how you implement it, lest someone else h == Table of Contents -* <> - ** <> - ** <> - ** <> -* <> - ** <> - ** <> - ** <> - ** <> - ** <> +* <<_ownable,Ownable>> + ** <<_quickstart,Quickstart>> + ** <<_ownable_library_api,Ownable library API>> + ** <<_ownable_events,Ownable events>> +* <<_accesscontrol,AccessControl>> + ** <<_usage,Usage>> + ** <<_granting_and_revoking_roles,Granting and revoking roles>> + ** <<_creating_role_identifiers,Creating role identifiers>> + ** <<_accesscontrol_library_api,AccessControl library API>> + ** <<_accesscontrol_events,AccessControl events>> == Ownable The most common and basic form of access control is the concept of ownership: there's an account that is the `owner` of a contract and can do administrative tasks on it. This approach is perfectly reasonable for contracts that have a single administrative user. -OpenZeppelin Contracts for Cairo provides link:../src/openzeppelin/access/ownable.cairo[Ownable] for implementing ownership in your contracts. +OpenZeppelin Contracts for Cairo provides {ownable-cairo} for implementing ownership in your contracts. === Quickstart -Integrating link:../src/openzeppelin/access/ownable.cairo[Ownable] into a contract first requires assigning an owner. -The implementing contract's constructor should set the initial owner by passing the owner's address to Ownable's <> like this: +Integrating {ownable-cairo} into a contract first requires assigning an owner. +The implementing contract's constructor should set the initial owner by passing the owner's address to Ownable's <<_initializer,initializer>> like this: [,cairo] ---- @@ -136,7 +140,7 @@ owner: felt Transfers ownership of the contract to a new account (`new_owner`). Can only be called by the current owner. -Emits a <> event. +Emits a <<_ownershiptransferred,`OwnershipTransferred`>> event. Parameters: @@ -155,7 +159,7 @@ Leaves the contract without owner. It will not be possible to call functions with `assert_only_owner` anymore. Can only be called by the current owner. -Emits a <> event. +Emits a <<_ownershiptransferred,`OwnershipTransferred`>> event. Parameters: @@ -168,10 +172,9 @@ None. [#transfer-ownership-internal] ==== `_transfer_ownership` -Transfers ownership of the contract to a new account (`new_owner`). -link:./Extensibility.md#the-pattern[`internal`] function without access restriction. +Transfers ownership of the contract to a new account (`new_owner`). {extensibility-pattern}[`internal`] function without access restriction. -Emits a <> event. +Emits a <<_ownershiptransferred,`OwnershipTransferred`>> event. Parameters: @@ -211,17 +214,17 @@ You may want for an account to have permission to ban users from a system, but n https://en.wikipedia.org/wiki/Role-based_access_control[Role-Based Access Control (RBAC)] offers flexibility in this regard. In essence, we will be defining multiple roles, each allowed to perform different sets of actions. -An account may have, for example, 'moderator', 'minter' or 'admin' roles, which you will then check for instead of simply using <>. -This check can be enforced through <>. +An account may have, for example, 'moderator', 'minter' or 'admin' roles, which you will then check for instead of simply using <<_assert_only_owner,assert_only_owner>>. +This check can be enforced through <<_assert_only_role,assert_only_role>>. Separately, you will be able to define rules for how accounts can be granted a role, have it revoked, and more. Most software uses access control systems that are role-based: some users are regular users, some may be supervisors or managers, and a few will often have administrative privileges. === Usage -For each role that you want to define, you will create a new _role identifier_ that is used to grant, revoke, and check if an account has that role (see <> for information on creating identifiers). +For each role that you want to define, you will create a new _role identifier_ that is used to grant, revoke, and check if an account has that role (see <<_creating_role_identifiers,Creating role identifiers>> for information on creating identifiers). -Here's a simple example of implementing `AccessControl` on a portion of an link:../src/openzeppelin/token/erc20/ERC20.cairo[ERC20 token contract] which defines and sets the 'minter' role: +Here's a simple example of implementing `AccessControl` on a portion of an link:https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20.cairo[ERC20 token contract] which defines and sets the 'minter' role: [,cairo] ---- @@ -261,9 +264,9 @@ func mint{ end ---- -CAUTION: Make sure you fully understand how <> works before using it on your system, or copy-pasting the examples from this guide. +CAUTION: Make sure you fully understand how <<_accesscontrol,AccessControl>> works before using it on your system, or copy-pasting the examples from this guide. -While clear and explicit, this isn't anything we wouldn't have been able to achieve with <>. +While clear and explicit, this isn't anything we wouldn't have been able to achieve with <<_ownable,Ownable>>. Indeed, where `AccessControl` shines is in scenarios where granular permissions are required, which can be implemented by defining _multiple_ roles. Let's augment our ERC20 token example by also defining a 'burner' role, which lets accounts destroy tokens, and by using `assert_only_role`: @@ -327,7 +330,7 @@ Note that each account may still have more than one role, if so desired. === Granting and revoking roles -The ERC20 token example above uses `_grant_role`, an link:./Extensibility.md#the-pattern[`internal`] function that is useful when programmatically assigning roles (such as during construction). +The ERC20 token example above uses `_grant_role`, an {extensibility-pattern}[`internal`] function that is useful when programmatically assigning roles (such as during construction). But what if we later want to grant the 'minter' role to additional accounts? By default, *accounts with a role cannot grant it or revoke it from other accounts*: all having a role does is making the `assert_only_role` check pass. @@ -403,7 +406,7 @@ However, because those roles' admin role is the default admin role, and that rol Dynamic role allocation is often a desirable property, for example in systems where trust in a participant may vary over time. It can also be used to support use cases such as https://en.wikipedia.org/wiki/Know_your_customer[KYC], where the list of role-bearers may not be known up-front, or may be prohibitively expensive to include in a single transaction. -The following example uses the link:../tests/mocks/AccessControl.cairo[AccessControl mock contract] which exposes the role management functions. +The following example uses the link:https://github.com/OpenZeppelin/cairo-contracts/blob/main/tests/mocks/AccessControl.cairo[AccessControl mock contract] which exposes the role management functions. To grant and revoke roles in Python, for example: [,python] @@ -536,9 +539,9 @@ has_role: felt ==== `get_role_admin` Returns the admin role that controls `role`. -See <> and <>. +See <<_grant_role,grant_role>> and <<_revoke_role,revoke_role>>. -To change a role's admin, use <<_set_role_admin,_set_role_admin>>. +To change a role's admin, use <<_set_role_admin,`_set_role_admin`>>. Parameters: @@ -558,7 +561,7 @@ admin: felt Grants `role` to `user`. -If `user` had not been already granted `role`, emits a <> event. +If `user` had not been already granted `role`, emits a <<_rolegranted,RoleGranted>> event. Requirements: @@ -580,7 +583,7 @@ None. Revokes `role` from `user`. -If `user` had been granted `role`, emits a <> event. +If `user` had been granted `role`, emits a <<_rolerevoked,RoleRevoked>> event. Requirements: @@ -602,9 +605,9 @@ None. Revokes `role` from the calling `user`. -Roles are often managed via <> and <>: this function's purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). +Roles are often managed via <<_grant_role,grant_role>> and <<_revoke_role,revoke_role>>: this function's purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). -If the calling `user` had been revoked `role`, emits a <> event. +If the calling `user` had been revoked `role`, emits a <<_rolerevoked,RoleRevoked>> event. Requirements: @@ -627,9 +630,9 @@ None. Grants `role` to `user`. -link:./Extensibility.md#the-pattern[`internal`] function without access restriction. +{extensibility-pattern}[`internal`] function without access restriction. -Emits a <> event. +Emits a <<_rolegranted,RoleGranted>> event. Parameters: @@ -648,9 +651,9 @@ None. Revokes `role` from `user`. -link:./Extensibility.md#the-pattern[`internal`] function without access restriction. +{extensibility-pattern}[`internal`] function without access restriction. -Emits a <> event. +Emits a <<_rolerevoked,RoleRevoked>> event. Parameters: @@ -667,9 +670,9 @@ None. [#setroleadmin] ==== `_set_role_admin` -link:./Extensibility.md#the-pattern[`internal`] function that sets `admin_role` as ``role``'s admin role. +{extensibility-pattern}[`internal`] function that sets `admin_role` as ``role``'s admin role. -Emits a <> event. +Emits a <<_roleadminchanged,RoleAdminChanged>> event. Parameters: @@ -722,8 +725,8 @@ Emitted when account is revoked role. `sender` is the account that originated the contract call: -* if using <>, it is the admin role bearer -* if using <>, it is the role bearer (i.e. +* if using <<_revoke_role,revoke_role>>, it is the admin role bearer +* if using <<_renounce_role,renounce_role>>, it is the role bearer (i.e. `account`). [,cairo] From a6da3ffe4b07435769002860bfe9e0d49c931e43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Sat, 23 Jul 2022 14:42:09 -0300 Subject: [PATCH 16/39] fix account doc links --- docs/modules/ROOT/pages/accounts.adoc | 106 +++++++++++--------------- 1 file changed, 45 insertions(+), 61 deletions(-) diff --git a/docs/modules/ROOT/pages/accounts.adoc b/docs/modules/ROOT/pages/accounts.adoc index 96cad3156..0086c48f0 100644 --- a/docs/modules/ROOT/pages/accounts.adoc +++ b/docs/modules/ROOT/pages/accounts.adoc @@ -1,3 +1,5 @@ +:test-utils: https://github.com/OpenZeppelin/cairo-contracts/blob/main/tests/utils.py + = Accounts Unlike Ethereum where accounts are directly derived from a private key, there's no native account concept on StarkNet. @@ -9,33 +11,33 @@ A more detailed writeup on the topic can be found on https://perama-v.github.io/ == Table of Contents -* <> -* <> -* <> - ** <> - ** <> - ** <> -* <> -* <> - ** <> - ** <> -* <> -* <> - ** <> - ** <> - ** <> - ** <> +* <<_quickstart,Quickstart>> +* <<_standard_interface,Standard Interface>> +* <<_keys_signatures_and_signers,Keys, signatures and signers>> + ** <<_signer,Signer>> + ** <<_mocksigner_utility,MockSigner utility>> + ** <<_mockethsigner_utility,MockEthSigner utility>> +* <<_account_entrypoint,Account entrypoint>> +* <<_call_and_accountcallarray_format,Call and AccountCallArray format>> + ** <<_call,Call>> + ** <<_accountcallarray,AccountCallArray>> +* <<_multicall_transactions,Multicall transactions>> +* <<_api_specification,API Specification>> + ** <<_get_public_key,`get_public_key`>> + ** <<_get_nonce,`get_nonce`>> + ** <<_set_public_key,`set_public_key`>> + ** <<_is_valid_signature,`is_valid_signature`>> ** <<\\__execute__,`\\__execute__`>> - ** <> - ** <> - ** <<_unsafe_execute,`_unsafe_execute`>> -* <> - ** <> - ** <> -* <> -* <> -* <> -* <> + ** <<_is_valid_eth_signature,`is_valid_eth_signature`>> + ** <<_eth_execute,`eth_execute`>> + ** <<__unsafe_execute,`_unsafe_execute`>> +* <<_presets,Presets>> + ** <<_account,Account>> + ** <<_eth_account,Eth Account>> +* <<_account_differentiation_with_erc165,Account differentiation with ERC165>> +* <<_extending_the_account_contract,Extending the Account contract>> +* <<_l1_escape_hatch_mechanism,L1 escape hatch mechanism>> +* <<_paying_for_gas,Paying for gas>> == Quickstart @@ -64,7 +66,7 @@ await signer.send_transaction(account, some_contract_address, 'some_function', [ == Standard Interface -The link:../src/openzeppelin/account/IAccount.cairo[`IAccount.cairo`] contract interface contains the standard account interface proposed in https://github.com/OpenZeppelin/cairo-contracts/discussions/41[#41] and adopted by OpenZeppelin and Argent. +The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/account/IAccount.cairo[`IAccount.cairo`] contract interface contains the standard account interface proposed in https://github.com/OpenZeppelin/cairo-contracts/discussions/41[#41] and adopted by OpenZeppelin and Argent. It implements https://eips.ethereum.org/EIPS/eip-1271[EIP-1271] and it is agnostic of signature validation and nonce management strategies. [,cairo] @@ -137,7 +139,7 @@ To simplify Account management, most of this is abstracted away with `MockSigner === MockSigner utility -The `MockSigner` class in link:../tests/utils.py[utils.py] is used to perform transactions on a given Account, crafting the transaction and managing nonces. +The `MockSigner` class in {test-utils}[utils.py] is used to perform transactions on a given Account, crafting the transaction and managing nonces. The flow of a transaction starts with checking the nonce and converting the `to` contract address of each call to hexadecimal format. The hexadecimal conversion is necessary because Nile's `Signer` converts the address to a base-16 integer (which requires a string argument). @@ -183,7 +185,7 @@ If utilizing multicall, send multiple transactions with the `send_transactions` === MockEthSigner utility -The `MockEthSigner` class in link:../tests/utils.py[utils.py] is used to perform transactions on a given Account with a secp256k1 curve key pair, crafting the transaction and managing nonces. +The `MockEthSigner` class in {test-utils}[utils.py] is used to perform transactions on a given Account with a secp256k1 curve key pair, crafting the transaction and managing nonces. It differs from the `MockSigner` implementation by: * not using the public key but its derived address instead (the last 20 bytes of the keccak256 hash of the public key and adding `0x` to the beginning) @@ -232,10 +234,8 @@ Where: * `nonce` is an unique identifier of this message to prevent transaction replays. Current implementation requires nonces to be incremental -____ -Note that the scheme of building multicall transactions within the `\\__execute__` method will change once StarkNet allows for pointers in struct arrays. +NOTE: The scheme of building multicall transactions within the `\\__execute__` method will change once StarkNet allows for pointers in struct arrays. In which case, multiple transactions can be passed to (as opposed to built within) `\\__execute__`. -____ == `Call` and `AccountCallArray` format @@ -243,7 +243,7 @@ The idea is for all user intent to be encoded into a `Call` representing a smart Users can also pack multiple messages into a single transaction (creating a multicall transaction). Cairo currently does not support arrays of structs with pointers which means the `\\__execute__` function cannot properly iterate through mutiple ``Call``s. Instead, this implementation utilizes a workaround with the `AccountCallArray` struct. -See <>. +See <<_multicall-transactions,Multicall transactions>>. === `Call` @@ -307,16 +307,14 @@ This is the basic flow: ) ---- + -The `_from_call_to_call_array` method in link:../tests/utils.py[utils.py] converts each call into the `AccountCallArray` format and cumulatively stores the calldata of every call into a single array. +The `_from_call_to_call_array` method in {test-utils}[utils.py] converts each call into the `AccountCallArray` format and cumulatively stores the calldata of every call into a single array. Next, both arrays (as well as the `sender`, `nonce`, and `max_fee`) are used to create the transaction hash. The Signer then invokes `\\__execute__` with the signature and passes `AccountCallArray`, calldata, and nonce as arguments. . The `\\__execute__` method takes the `AccountCallArray` and calldata and builds an array of ``Call``s (MultiCall). -____ -It should be noted that every transaction utilizes `AccountCallArray`. +NOTE: Every transaction utilizes `AccountCallArray`. A single `Call` is treated as a bundle with one message. -____ == API Specification @@ -416,11 +414,7 @@ Returns: is_valid: felt ---- -____ -returns `TRUE` if a given signature is valid. -Otherwise, reverts. -In the future it will return `FALSE` if a given signature is invalid (for more info please check https://github.com/OpenZeppelin/cairo-contracts/issues/327[this issue]). -____ +NOTE: It may return `FALSE` in the future if a given signature is invalid (follow the discussion on https://github.com/OpenZeppelin/cairo-contracts/issues/327[this issue]). === `\\__execute__` @@ -443,9 +437,7 @@ calldata: felt* nonce: felt ---- -____ -Note that the current signature scheme expects a 2-element array like `[sig_r, sig_s]`. -____ +NOTE: The current signature scheme expects a 2-element array like `[sig_r, sig_s]`. Returns: @@ -475,11 +467,7 @@ Returns: is_valid: felt ---- -____ -returns `TRUE` if a given signature is valid. -Otherwise, reverts. -In the future it will return `FALSE` if a given signature is invalid (for more info please check https://github.com/OpenZeppelin/cairo-contracts/issues/327[this issue]). -____ +NOTE: It may return `FALSE` in the future if a given signature is invalid (follow the discussion on https://github.com/OpenZeppelin/cairo-contracts/issues/327[this issue]). === `eth_execute` @@ -496,10 +484,6 @@ calldata: felt* nonce: felt ---- -____ -Note that the current signature scheme expects a 7-element array like `[sig_v, uint256_sig_r_low, uint256_sig_r_high, uint256_sig_s_low, uint256_sig_s_high, uint256_hash_low, uint256_hash_high]` given that the parameters of the verification are bigger than a felt. -____ - Returns: [,cairo] @@ -508,13 +492,15 @@ response_len: felt response: felt* ---- +NOTE: The current signature scheme expects a 7-element array like `[sig_v, uint256_sig_r_low, uint256_sig_r_high, uint256_sig_s_low, uint256_sig_s_high, uint256_hash_low, uint256_hash_high]` given that the parameters of the verification are bigger than a felt. + === `_unsafe_execute` It's an internal method that performs the following tasks: . Increments the nonce. . Takes the input and builds a `Call` for each iterated message. -See <> for more information. +See <<_multicall-transactions,Multicall transactions>> for more information. . Calls the target contract with the intended function selector and calldata parameters . Forwards the contract call response data as return value @@ -525,11 +511,11 @@ Each preset differs on the signature type being used by the Account. === Account -The link:../src/openzeppelin/account/Account.cairo[`Account`] preset uses StarkNet keys to validate transactions. +The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/account/Account.cairo[`Account`] preset uses StarkNet keys to validate transactions. === Eth Account -The link:../src/openzeppelin/account/EthAccount.cairo[`EthAccount`] preset supports Ethereum addresses, validating transactions with secp256k1 keys. +The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/account/EthAccount.cairo[`EthAccount`] preset supports Ethereum addresses, validating transactions with secp256k1 keys. == Account differentiation with ERC165 @@ -543,7 +529,7 @@ In the case of account contracts, querying `supportsInterface` of an account's a == Extending the Account contract -Account contracts can be extended by following the link:../docs/Extensibility.md#the-pattern[extensibility pattern]. +Account contracts can be extended by following the xref:extensibility.adoc#the-pattern[extensibility pattern]. To implement custom account contracts, a pair of `validate` and `execute` functions should be exposed. This is why the Account library comes with different flavors of such pairs, like the vanilla `is_valid_signature` and `execute`, or the Ethereum flavored `is_valid_eth_signature` and `eth_execute` pair. @@ -553,12 +539,10 @@ Account contract developers are encouraged to implement the https://github.com/O To implement alternative `execute` functions, make sure to check their corresponding `validate` function before calling the `_unsafe_execute` building block, as each of the current presets is doing. Do not expose `_unsafe_execute` directly. -____ -Please note that the `ecdsa_ptr` implicit argument should be included in new methods that invoke `_unsafe_execute` (even if the `ecdsa_ptr` is not being used). +IMPORTANT: The `ecdsa_ptr` implicit argument should be included in new methods that invoke `_unsafe_execute` (even if the `ecdsa_ptr` is not being used). Otherwise, it's possible that an account's functionalty can work in both the testing and local devnet environments; however, it could fail on public networks on account of the https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/cairo/lang/builtins/signature/signature_builtin_runner.py[SignatureBuiltinRunner]. See https://github.com/OpenZeppelin/cairo-contracts/issues/386[issue #386] for more information. -____ Some other validation schemes to look out for in the future: From 930748a1efc1651c22775c1d2e8205f3f7770288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Sat, 23 Jul 2022 14:49:12 -0300 Subject: [PATCH 17/39] fix erc20 doc links --- docs/modules/ROOT/pages/erc20.adoc | 70 +++++++++++++++--------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/docs/modules/ROOT/pages/erc20.adoc b/docs/modules/ROOT/pages/erc20.adoc index c670341e7..86abfdaa4 100644 --- a/docs/modules/ROOT/pages/erc20.adoc +++ b/docs/modules/ROOT/pages/erc20.adoc @@ -5,29 +5,29 @@ The `ERC20.cairo` contract implements an approximation of https://eips.ethereum. == Table of Contents -* <> - ** <> -* <> -* <> -* <> - ** <> - ** <> - ** <> - ** <> -* <> - ** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - ** <> - *** <> - *** <> +* <<_interface,Interface>> + ** <<_erc20_compatibility,ERC20 compatibility>> +* <<_usage,Usage>> +* <<_extensibility,Extensibility>> +* <<_presets,Presets>> + ** <<_erc20_basic,ERC20 (basic)>> + ** <<_erc20_mintable,ERC20_Mintable>> + ** <<_erc20_pausable,ERC20_Pausable>> + ** <<_erc20_upgradeable,ERC20_Upgradeable>> +* <<_api_specification,API Specification>> + ** <<_methods,Methods>> + *** <<_name,`name`>> + *** <<_symbol,`symbol`>> + *** <<_decimals,`decimals`>> + *** <<_totalsupply,`totalSupply`>> + *** <<_balanceof,`balanceOf`>> + *** <<_allowance,`allowance`>> + *** <<_transfer,`transfer`>> + *** <<_transferfrom,`transferFrom`>> + *** <<_approve,`approve`>> + ** <<_events,Events>> + *** <<_transfer_event,`Transfer (event)`>> + *** <<_approval_event,`Approval (event)`>> == Interface @@ -134,10 +134,10 @@ await signer.send_transaction(account, erc20.contract_address, 'transfer', [reci == Extensibility -ERC20 contracts can be extended by following the link:../docs/Extensibility.md#the-pattern[extensibility pattern]. +ERC20 contracts can be extended by following the xref:extensibility.adoc#the-pattern[extensibility pattern]. The basic idea behind integrating the pattern is to import the requisite ERC20 methods from the ERC20 library and incorporate the extended logic thereafter. For example, let's say you wanted to implement a pausing mechanism. -The contract should first import the ERC20 methods and the extended logic from the link:../src/openzeppelin/security/pausable.cairo[pausable library] i.e. +The contract should first import the ERC20 methods and the extended logic from the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/security/pausable.cairo[pausable library] i.e. `Pausable_pause`, `Pausable_unpause`. Next, the contract should expose the methods with the extended logic therein like this: @@ -164,7 +164,7 @@ Some other ways to extend ERC20 contracts may include: * Creating a timelock * Adding roles such as owner or minter -For full examples of the extensibility pattern being used in ERC20 contracts, see <>. +For full examples of the extensibility pattern being used in ERC20 contracts, see <<_presets,Presets>>. == Presets @@ -173,23 +173,23 @@ Each preset mints an initial supply which is especially necessary for presets th === ERC20 (basic) -The link:../src/openzeppelin/token/erc20/ERC20.cairo[`ERC20`] preset offers a quick and easy setup for deploying a basic ERC20 token. +The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20.cairo[`ERC20`] preset offers a quick and easy setup for deploying a basic ERC20 token. === ERC20_Mintable -The link:../src/openzeppelin/token/erc20/ERC20_Mintable.cairo[`ERC20_Mintable`] preset allows the contract owner to mint new tokens. +The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20_Mintable.cairo[`ERC20_Mintable`] preset allows the contract owner to mint new tokens. === ERC20_Pausable -The link:../src/openzeppelin/token/erc20/ERC20_Pausable.cairo[`ERC20_Pausable`] preset allows the contract owner to pause/unpause all state-modifying methods i.e. +The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20_Pausable.cairo[`ERC20_Pausable`] preset allows the contract owner to pause/unpause all state-modifying methods i.e. `transfer`, `approve`, etc. This preset proves useful for scenarios such as preventing trades until the end of an evaluation period and having an emergency switch for freezing all token transfers in the event of a large bug. === ERC20_Upgradeable -The link:../src/openzeppelin/token/erc20/ERC20_Upgradeable.cairo[`ERC20_Upgradeable`] preset allows the contract owner to upgrade a contract by deploying a new ERC20 implementation contract while also maintaing the contract's state. +The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20_Upgradeable.cairo[`ERC20_Upgradeable`] preset allows the contract owner to upgrade a contract by deploying a new ERC20 implementation contract while also maintaing the contract's state. This preset proves useful for scenarios such as eliminating bugs and adding new features. -For more on upgradeability, see link:Proxies.md#contract-upgrades[Contract upgrades]. +For more on upgradeability, see xref:proxies.adoc#contract-upgrades[Contract upgrades]. == API Specification @@ -327,7 +327,7 @@ remaining: Uint256 Moves `amount` tokens from the caller's account to `recipient`. It returns `1` representing a bool if it succeeds. -Emits a <> event. +Emits a <<_transfer-event,Transfer>> event. Parameters: @@ -350,7 +350,7 @@ Moves `amount` tokens from `sender` to `recipient` using the allowance mechanism `amount` is then deducted from the caller's allowance. It returns `1` representing a bool if it succeeds. -Emits a <> event. +Emits a <<_transfer-event,Transfer>> event. Parameters: @@ -373,7 +373,7 @@ success: felt Sets `amount` as the allowance of `spender` over the caller's tokens. It returns `1` representing a bool if it succeeds. -Emits an <> event. +Emits an <<_approval-event,Approval>> event. Parameters: @@ -418,7 +418,7 @@ value: Uint256 ==== `Approval (event)` -Emitted when the allowance of a `spender` for an `owner` is set by a call to <>. +Emitted when the allowance of a `spender` for an `owner` is set by a call to <<_approve,approve>>. `value` is the new allowance. Parameters: From 14d8aa093d2ee5392cc4e028ac657b41eeb3849a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Sat, 23 Jul 2022 14:54:21 -0300 Subject: [PATCH 18/39] fix erc721 doc links --- docs/modules/ROOT/pages/accounts.adoc | 4 +- docs/modules/ROOT/pages/erc20.adoc | 6 +- docs/modules/ROOT/pages/erc721.adoc | 116 +++++++++++++------------- 3 files changed, 63 insertions(+), 63 deletions(-) diff --git a/docs/modules/ROOT/pages/accounts.adoc b/docs/modules/ROOT/pages/accounts.adoc index 0086c48f0..91f8f3b5f 100644 --- a/docs/modules/ROOT/pages/accounts.adoc +++ b/docs/modules/ROOT/pages/accounts.adoc @@ -243,7 +243,7 @@ The idea is for all user intent to be encoded into a `Call` representing a smart Users can also pack multiple messages into a single transaction (creating a multicall transaction). Cairo currently does not support arrays of structs with pointers which means the `\\__execute__` function cannot properly iterate through mutiple ``Call``s. Instead, this implementation utilizes a workaround with the `AccountCallArray` struct. -See <<_multicall-transactions,Multicall transactions>>. +See <<_multicall_transactions,Multicall transactions>>. === `Call` @@ -500,7 +500,7 @@ It's an internal method that performs the following tasks: . Increments the nonce. . Takes the input and builds a `Call` for each iterated message. -See <<_multicall-transactions,Multicall transactions>> for more information. +See <<_multicall_transactions,Multicall transactions>> for more information. . Calls the target contract with the intended function selector and calldata parameters . Forwards the contract call response data as return value diff --git a/docs/modules/ROOT/pages/erc20.adoc b/docs/modules/ROOT/pages/erc20.adoc index 86abfdaa4..f5ca904d4 100644 --- a/docs/modules/ROOT/pages/erc20.adoc +++ b/docs/modules/ROOT/pages/erc20.adoc @@ -327,7 +327,7 @@ remaining: Uint256 Moves `amount` tokens from the caller's account to `recipient`. It returns `1` representing a bool if it succeeds. -Emits a <<_transfer-event,Transfer>> event. +Emits a <<_transfer_event,Transfer>> event. Parameters: @@ -350,7 +350,7 @@ Moves `amount` tokens from `sender` to `recipient` using the allowance mechanism `amount` is then deducted from the caller's allowance. It returns `1` representing a bool if it succeeds. -Emits a <<_transfer-event,Transfer>> event. +Emits a <<_transfer_event,Transfer>> event. Parameters: @@ -373,7 +373,7 @@ success: felt Sets `amount` as the allowance of `spender` over the caller's tokens. It returns `1` representing a bool if it succeeds. -Emits an <<_approval-event,Approval>> event. +Emits an <<_approval_event,Approval>> event. Parameters: diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 5a06b62fb..35dcf0704 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -5,49 +5,49 @@ The `ERC721.cairo` contract implements an approximation of https://eips.ethereum == Table of Contents -* <> -* <> -* <> - ** <> - ** <> - ** <> - *** <> - ** <> - ** <> -* <> -* <> - ** <> - ** <> - ** <> - *** <> - ** <> - *** <> -* <> - ** <> -* <> - ** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - *** <> - ** <> - *** <> - *** <> - *** <> - ** <> - *** <> - *** <> - *** <> - ** <> - *** <> - *** <> - *** <> - ** <> - *** <> +* <<_ierc721,IERC721>> +* <<_erc721_compatibility,ERC721 Compatibility>> +* <<_usage,Usage>> + ** <<_token_transfers,Token Transfers>> + ** <<_interpreting_erc721_uris,Interpreting ERC721 URIs>> + ** <<_erc721received,ERC721Received>> + *** <<_ierc721_receiver,IERC721_Receiver>> + ** <<_supporting_interfaces,Supporting Interfaces>> + ** <<_ready_to_use_presets,Ready_to_Use Presets>> +* <<_extensibility,Extensibility>> +* <<_presets,Presets>> + ** <<_erc721_mintable_burnable,ERC721_Mintable_Burnable>> + ** <<_erc721_mintable_pausable,ERC721_Mintable_Pausable>> + ** <<_erc721_enumerable_mintable_burnable,ERC721_Enumerable_Mintable_Burnable>> + *** <<_ierc721_enumerable,IERC721_Enumerable>> + ** <<_erc721_metadata,ERC721_Metadata>> + *** <<_ierc721_metadata,IERC721_Metadata>> +* <<_utilities,Utilities>> + ** <<_erc721_holder,ERC721_Holder>> +* <<_api_specification,API Specification>> + ** <<_ierc721_api,`IERC721`>> + *** <<_balanceof,`balanceOf`>> + *** <<_ownerof,`ownerOf`>> + *** <<_safetransferfrom,`safeTransferFrom`>> + *** <<_transferfrom,`transferFrom`>> + *** <<_approve,`approve`>> + *** <<_setapprovalforall,`setApprovalForAll`>> + *** <<_getapproved,`getApproved`>> + *** <<_isapprovedforall,`isApprovedForAll`>> + ** <<_events,Events>> + *** <<_approval_event,`Approval (event)`>> + *** <<_approvalforall_event,`ApprovalForAll (event)`>> + *** <<_transfer_event,`Transfer (event)`>> + ** <<_ierc721_metadata,`IERC721_Metadata`>> + *** <<_name,`name`>> + *** <<_symbol,`symbol`>> + *** <<_tokenuri,`tokenURI`>> + ** <<_ierc721_enumerable,`IERC721_Enumerable`>> + *** <<_totalsupply,`totalSupply`>> + *** <<_tokenbyindex,`tokenByIndex`>> + *** <<_tokenofownerbyindex,`tokenOfOwnerByIndex`>> + ** <<_ierc721_receiver,`IERC721_Receiver`>> + *** <<_onerc721received,`onERC721Received`>> == IERC721 @@ -105,10 +105,10 @@ But some differences can still be found, such as: The EIP721 standard, however, states that the return value should be of type string. If a token's URI is not set, the returned value is `0`. Note that URIs cannot exceed 31 characters. -See <> +See <<_interpreting_erc721_uris,Interpreting ERC721 URIs>> * ``interface_id``s are hardcoded and initialized by the constructor. The hardcoded values derive from Solidity's selector calculations. -See <> +See <<_supporting_interfaces,Supporting Interfaces>> * `safeTransferFrom` can only be expressed as a single function in Cairo as opposed to the two functions declared in EIP721. The difference between both functions consists of accepting `data` as an argument. Because function overloading is currently not possible in Cairo, `safeTransferFrom` by default accepts the `data` argument. @@ -192,7 +192,7 @@ The `safeTransferFrom` method incorporates the following conditional logic: . if the calling address is not an account contract, the safe function will check that the contract supports ERC721 tokens The current implementation of `safeTansferFrom` checks for `onERC721Received` and requires that the recipient contract supports ERC165 and exposes the `supportsInterface` method. -See <>. +See <<_erc721received,ERC721Received>>. === Interpreting ERC721 URIs @@ -236,7 +236,7 @@ In order to be sure a contract can safely accept ERC721 tokens, said contract mu Methods such as `safeTransferFrom` and `safeMint` call the recipient contract's `onERC721Received` method. If the contract fails to return the correct magic value, the transaction fails. -StarkNet contracts that support safe transfers, however, must also support link:./Introspection.md#erc165[ERC165] and include `supportsInterface` as proposed in https://github.com/OpenZeppelin/cairo-contracts/discussions/100[#100]. +StarkNet contracts that support safe transfers, however, must also support xref:introspection.adoc#erc165[ERC165] and include `supportsInterface` as proposed in https://github.com/OpenZeppelin/cairo-contracts/discussions/100[#100]. `safeTransferFrom` requires a means of differentiating between account and non-account contracts. Currently, StarkNet does not support error handling from the contract level; therefore, the current ERC721 implementation requires that all contracts that support safe ERC721 transfers (both accounts and non-accounts) include the `supportsInterface` method. @@ -280,16 +280,16 @@ For example: * `ERC721_Mintable_Burnable` includes `mint` and `burn` * `ERC721_Mintable_Pausable` includes `mint`, `pause`, and `unpause` -* `ERC721_Enumerable_Mintable_Burnable` includes `mint`, `burn`, and <> methods +* `ERC721_Enumerable_Mintable_Burnable` includes `mint`, `burn`, and <<_ierc721_enumerable,IERC721_Enumerable>> methods Ready-to-use presets are a great option for testing and prototyping. -See <>. +See <<_presets,Presets>>. == Extensibility Following the xref:Extensibility.adoc[contracts extensibility pattern], this implementation is set up to include all ERC721 related storage and business logic under a namespace. Developers should be mindful of manually exposing the required methods from the namespace to comply with the standard interface. -This is already done in the <>; +This is already done in the <<_presets,preset contracts>>; however, additional functionality can be added. For instance, you could: @@ -401,7 +401,7 @@ Accepts all token transfers. Make sure the contract is able to use its token with `IERC721.safeTransferFrom`, `IERC721.approve` or `IERC721.setApprovalForAll`. Also utilizes the ERC165 method `supportsInterface` to determine if the contract is an account. -See <> +See <<_erc721received,ERC721Received>> == API Specification @@ -479,9 +479,9 @@ owner: felt ==== `safeTransferFrom` Safely transfers `tokenId` token from `from_` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. -For information regarding how contracts communicate their awareness of the ERC721 protocol, see <>. +For information regarding how contracts communicate their awareness of the ERC721 protocol, see <<_erc721received,ERC721Received>>. -Emits a <> event. +Emits a <<_transfer_event,Transfer>> event. Parameters: @@ -503,7 +503,7 @@ None. Transfers `tokenId` token from `from_` to `to`. *The caller is responsible to confirm that `to` is capable of receiving NFTs or else they may be permanently lost*. -Emits a <> event. +Emits a <<_transfer_event,Transfer>> event. Parameters: @@ -523,7 +523,7 @@ None. Gives permission to `to` to transfer `tokenId` token to another account. The approval is cleared when the token is transferred. -Emits an <> event. +Emits an <<_approval_event,Approval>> event. Parameters: @@ -560,7 +560,7 @@ operator: felt Approve or remove `operator` as an operator for the caller. Operators can call `transferFrom` or `safeTransferFrom` for any token owned by the caller. -Emits an <> event. +Emits an <<_approvalforall_event,ApprovalForAll>> event. Parameters: @@ -731,7 +731,7 @@ totalSupply: Uint256 ==== `tokenByIndex` Returns a token ID owned by `owner` at a given `index` of its token list. -Use along with <> to enumerate all of ``owner``'s tokens. +Use along with <<_balanceof,balanceOf>> to enumerate all of ``owner``'s tokens. Parameters: @@ -750,7 +750,7 @@ tokenId: Uint256 ==== `tokenOfOwnerByIndex` Returns a token ID at a given `index` of all the tokens stored by the contract. -Use along with <> to enumerate all tokens. +Use along with <<_totalsupply,totalSupply>> to enumerate all tokens. Parameters: From 9c7b0cbf64f5c1976060c642fdcfeb2f108e8f0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Sat, 23 Jul 2022 15:06:02 -0300 Subject: [PATCH 19/39] fix extensibility doc links --- docs/modules/ROOT/pages/extensibility.adoc | 32 +++++++++++----------- docs/modules/ROOT/pages/index.adoc | 6 ++-- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/modules/ROOT/pages/extensibility.adoc b/docs/modules/ROOT/pages/extensibility.adoc index 938c895b6..726a02a14 100644 --- a/docs/modules/ROOT/pages/extensibility.adoc +++ b/docs/modules/ROOT/pages/extensibility.adoc @@ -2,13 +2,13 @@ NOTE: Expect this pattern to evolve (as it has already done) or even disappear if https://community.starknet.io/t/contract-extensibility-pattern/210/11?u=martriay[proper extensibility features] are implemented into Cairo. -* <> -* <> - ** <> - ** <> -* <> -* <> -* <> +* <<_the_extensibility_problem,The extensibility problem>> +* <<_the_pattern,The pattern ™️>> + ** <<_libraries,Libraries>> + ** <<_contracts,Contracts>> +* <<_presets,Presets>> +* <<_function_names_and_coding_style,Function names and coding style>> +* <<_emulating_hooks,Emulating hooks>> == The extensibility problem @@ -79,15 +79,15 @@ They can be deployed as-is or used as templates for customization. Some presets are: -* link:../src/openzeppelin/account/Account.cairo[Account] -* link:../tests/mocks/ERC165.cairo[ERC165] -* link:../src/openzeppelin/token/erc20/ERC20_Mintable.cairo[ERC20_Mintable] -* link:../src/openzeppelin/token/erc20/ERC20_Pausable.cairo[ERC20_Pausable] -* link:../src/openzeppelin/token/erc20/ERC20_Upgradeable.cairo[ERC20_Upgradeable] -* link:../src/openzeppelin/token/erc20/ERC20.cairo[ERC20] -* link:../src/openzeppelin/token/erc721/ERC721_Mintable_Burnable.cairo[ERC721_Mintable_Burnable] -* link:../src/openzeppelin/token/erc721/ERC721_Mintable_Pausable.cairo[ERC721_Mintable_Pausable] -* link:../src/openzeppelin/token/erc721_enumerable/ERC721_Enumerable_Mintable_Burnable.cairo[ERC721_Enumerable_Mintable_Burnable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/account/Account.cairo[Account] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/tests/mocks/ERC165.cairo[ERC165] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20_Mintable.cairo[ERC20_Mintable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20_Pausable.cairo[ERC20_Pausable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20_Upgradeable.cairo[ERC20_Upgradeable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20.cairo[ERC20] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc721/ERC721_Mintable_Burnable.cairo[ERC721_Mintable_Burnable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc721/ERC721_Mintable_Pausable.cairo[ERC721_Mintable_Pausable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc721_enumerable/ERC721_Enumerable_Mintable_Burnable.cairo[ERC721_Enumerable_Mintable_Burnable] == Function names and coding style diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index 50ee045db..f5270110e 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -50,7 +50,7 @@ pip install openzeppelin-cairo-contracts Presets are ready-to-use contracts that you can deploy right away. They also serve as examples of how to use library modules. -link:docs/Extensibility.md#presets[Read more about presets]. +xref:extensibility.adoc#presets[Read more about presets]. [,cairo] ---- @@ -72,11 +72,11 @@ nile deploy MyToken --al NOTE: `` is expected to be two integers i.e. `1` `0`. -See link:docs/Utilities.md#Uint256[Uint256] for more information. +See xref:utilities.adoc#Uint256[Uint256] for more information. === Write a custom contract using library modules -link:docs/Extensibility.md#libraries[Read more about libraries]. +xref:extensibility.adoc#libraries[Read more about libraries]. [,cairo] ---- From 22b6ffbb4d924ede40a37ec927559ec4abbc68d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Sat, 23 Jul 2022 15:26:38 -0300 Subject: [PATCH 20/39] fix introspection doc links --- docs/modules/ROOT/pages/introspection.adoc | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/modules/ROOT/pages/introspection.adoc b/docs/modules/ROOT/pages/introspection.adoc index 35d0a483c..170295155 100644 --- a/docs/modules/ROOT/pages/introspection.adoc +++ b/docs/modules/ROOT/pages/introspection.adoc @@ -4,16 +4,16 @@ CAUTION: Expect this module to evolve. == Table of Contents -* <> - ** <> - ** <> - ** <> - ** <> - ** <> - *** <> - ** <> - *** <> - *** <> +* <<_erc165,ERC165>> + ** <<_interface_calculations,Interface calculations>> + ** <<_registering_interfaces,Registering interfaces>> + ** <<_querying_interfaces,Querying interfaces>> + ** <<_ierc165,IERC165>> + ** <<_ierc165_api_specification,IERC165 API Specification>> + *** <<_supportsinterface,`supportsInterface`>> + ** <<_erc165_library_functions,ERC165 Library Functions>> + *** <<_supportsinterface2,`supports_interface`>> + *** <<_register_interface,`register_interface`>> == ERC165 @@ -27,7 +27,7 @@ There may even not be any direct calls to them! ERC20 tokens may be sent to a contract that lacks a way to transfer them out of it, locking them forever). In these cases, a contract declaring its interface can be very helpful in preventing errors. -It should be noted that the link:../src/openzeppelin/utils/constants.cairo[constants library] includes constant variables referencing all of the interface ids used in these contracts. +It should be noted that the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/utils/constants.cairo[constants library] includes constant variables referencing all of the interface ids used in these contracts. This allows for more legible code i.e. using `IERC165_ID` instead of `0x01ffc9a7`. @@ -84,8 +84,8 @@ end ---- NOTE: `supportsInterface` is camelCased because it is an exposed contract method as part of ERC165's interface. -This differs from library methods (such as `supports_interface` from the link:../src/openzeppelin/introspection/ERC165.cairo[ERC165 library]) which are snake_cased and not exposed. -See the link:../docs/Extensibility.md#function-names-and-coding-style[Function names and coding style] for more details. +This differs from library methods (such as `supports_interface` from the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/introspection/ERC165.cairo[ERC165 library]) which are snake_cased and not exposed. +See the xref:extensibility.adoc#function_names_and_coding_style[Function names and coding style] for more details. === IERC165 From ff2ed8697e8dfb983f9fd5909081e9c778cbddbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Sat, 23 Jul 2022 15:29:25 -0300 Subject: [PATCH 21/39] fix proxies doc links --- docs/modules/ROOT/pages/proxies.adoc | 46 ++++++++++++++-------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/docs/modules/ROOT/pages/proxies.adoc b/docs/modules/ROOT/pages/proxies.adoc index 1e8704a2b..abca1b76b 100644 --- a/docs/modules/ROOT/pages/proxies.adoc +++ b/docs/modules/ROOT/pages/proxies.adoc @@ -4,21 +4,21 @@ NOTE: Expect rapid iteration as this pattern matures and more patterns potential == Table of Contents -* <> -* <> - ** <> - ** <> -* <> - ** <> - ** <> -* <> - ** <> - ** <> -* <> - ** <> - ** <> - ** <> -* <> +* <<_quickstart,Quickstart>> +* <<_solidity\/cairo_upgrades_comparison,Solidity/Cairo upgrades comparison>> + ** <<_constructors,Constructors>> + ** <<_storage,Storage>> +* <<_proxies2,Proxies>> + ** <<_proxy_contract,Proxy contract>> + ** <<_implementation_contract,Implementation contract>> +* <<_upgrades_library_api,Upgrades library API>> + ** <<_methods,Methods>> + ** <<_events,Events>> +* <<_using_proxies,Using proxies>> + ** <<_contract_upgrades,Contract upgrades>> + ** <<_declaring_contracts,Declaring contracts>> + ** <<_handling_method_calls,Handling method calls>> +* <<_presets,Presets>> == Quickstart @@ -97,7 +97,7 @@ This allows developers to add features, update logic, and fix bugs without touch === Proxy contract -The link:../src/openzeppelin/upgrades/Proxy.cairo[Proxy contract] includes two core methods: +The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/upgrades/Proxy.cairo[Proxy contract] includes two core methods: . The `\\__default__` method is a fallback method that redirects a function call and associated calldata to the implementation contract. . The `\\__l1_default__` method is also a fallback method; @@ -114,7 +114,7 @@ The proxy's fallback function redirects the function call to the implementation === Implementation contract The implementation contract, also known as the logic contract, receives the redirected function calls from the proxy contract. -The implementation contract should follow the link:../docs/Extensibility.md#the-pattern[Extensibility pattern] and import directly from the link:../src/openzeppelin/upgrades/library.cairo[Proxy library]. +The implementation contract should follow the xref:extensibility.md#the-pattern[Extensibility pattern] and import directly from the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/upgrades/library.cairo[Proxy library]. The implementation contract should: @@ -136,11 +136,11 @@ Instead, use an initializer method that invokes the Proxy `constructor`. NOTE: The Proxy `constructor` includes a check the ensures the initializer can only be called once; however, `_set_implementation` does not include this check. -It's up to the developers to protect their implementation contract's upgradeability with access controls such as <>. +It's up to the developers to protect their implementation contract's upgradeability with access controls such as <<_assert_only_admin,`assert_only_admin`>>. For a full implementation contract example, please see: -* link:../tests/mocks/proxiable_implementation.cairo[Proxiable implementation] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/tests/mocks/proxiable_implementation.cairo[Proxiable implementation] == Upgrades library API @@ -325,8 +325,8 @@ To upgrade a contract, the implementation contract should include an `upgrade` m For a full deployment and upgrade implementation, please see: -* link:../tests/mocks/upgrades_v1_mock.cairo[Upgrades V1] -* link:../tests/mocks/upgrades_v2_mock.cairo[Upgrades V2] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/tests/mocks/upgrades_v1_mock.cairo[Upgrades V1] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/tests/mocks/upgrades_v2_mock.cairo[Upgrades V2] === Declaring contracts @@ -338,7 +338,7 @@ For more information on declaring classes, see https://starknet.io/docs/hello_st === Handling method calls -As with most StarkNet contracts, interacting with a proxy contract requires an link:../docs/Account.md#quickstart[account abstraction]. +As with most StarkNet contracts, interacting with a proxy contract requires an xref:eccount.md#quickstart[account abstraction]. One notable difference with proxy contracts versus other contract implementations is that calling `@view` methods also requires an account abstraction. As of now, direct calls to default entrypoints are only supported by StarkNet's `syscalls` from other contracts i.e. account contracts. @@ -362,7 +362,7 @@ They can be deployed as-is or used as templates for customization. Some presets include: -* link:../src/openzeppelin/token/erc20/ERC20_Upgradeable.cairo[ERC20_Upgradeable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20_Upgradeable.cairo[ERC20_Upgradeable] * more to come! have an idea? https://github.com/OpenZeppelin/cairo-contracts/issues/new/choose[open an issue]! From f88c899549b9989f3a40a090d8cd2b0a86dc90ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Sat, 23 Jul 2022 15:30:30 -0300 Subject: [PATCH 22/39] fix security doc links --- docs/modules/ROOT/pages/security.adoc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/modules/ROOT/pages/security.adoc b/docs/modules/ROOT/pages/security.adoc index 1aad22e53..66b3f1736 100644 --- a/docs/modules/ROOT/pages/security.adoc +++ b/docs/modules/ROOT/pages/security.adoc @@ -6,11 +6,11 @@ CAUTION: Expect this module to evolve. == Table of Contents -* <> -* <> -* <> -* <> - ** <> +* <<_initializable,Initializable>> +* <<_pausable,Pausable>> +* <<_reentrancy_guard,Reentrancy Guard>> +* <<_safemath,SafeMath>> + ** <<_safeuint256,SafeUint256>> == Initializable @@ -83,14 +83,14 @@ In other words, `pause` cannot be invoked when already paused and vice versa. For a list of full implementations utilizing the Pausable library, see: -* link:../src/openzeppelin/token/erc20/ERC20_Pausable.cairo[ERC20_Pausable] -* link:../src/openzeppelin/token/erc721/ERC721_Mintable_Pausable.cairo[ERC721_Mintable_Pausable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20_Pausable.cairo[ERC20_Pausable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc721/ERC721_Mintable_Pausable.cairo[ERC721_Mintable_Pausable] == Reentrancy Guard A https://gus-tavo-guim.medium.com/reentrancy-attack-on-smart-contracts-how-to-identify-the-exploitable-and-an-example-of-an-attack-4470a2d8dfe4[reentrancy attack] occurs when the caller is able to obtain more resources than allowed by recursively calling a target's function. -Since Cairo does not support modifiers like Solidity, the link:../src/openzeppelin/security/reentrancyguard.cairo[`reentrancyguard`] library exposes two methods `_start` and `_end` to protect functions against reentrancy attacks. +Since Cairo does not support modifiers like Solidity, the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/security/reentrancyguard.cairo[`reentrancyguard`] library exposes two methods `_start` and `_end` to protect functions against reentrancy attacks. The protected function must call `ReentrancyGuard._start` before the first function statement, and `ReentrancyGuard._end` before the return statement, as shown below: [,cairo] @@ -113,7 +113,7 @@ end === SafeUint256 -The SafeUint256 namespace in the link:../src/openzeppelin/security/safemath.cairo[SafeMath library] offers arithmetic for unsigned 256-bit integers (uint256) by leveraging Cairo's Uint256 library and integrating overflow checks. +The SafeUint256 namespace in the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/security/safemath.cairo[SafeMath library] offers arithmetic for unsigned 256-bit integers (uint256) by leveraging Cairo's Uint256 library and integrating overflow checks. Some of Cairo's Uint256 functions do not revert upon overflows. For instance, `uint256_add` will return a bit carry when the sum exceeds 256 bits. This library includes an additional assertion ensuring values do not overflow. From 351c6a4212bf39c0e0f4e43cffefc002060d188d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Sat, 23 Jul 2022 15:32:37 -0300 Subject: [PATCH 23/39] fix utils doc links --- docs/modules/ROOT/pages/utilities.adoc | 42 +++++++++++++------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/modules/ROOT/pages/utilities.adoc b/docs/modules/ROOT/pages/utilities.adoc index 1b79b9782..addd6cffb 100644 --- a/docs/modules/ROOT/pages/utilities.adoc +++ b/docs/modules/ROOT/pages/utilities.adoc @@ -6,29 +6,29 @@ CAUTION: Expect this module to evolve (as it has already done). == Table of Contents -* <> -* <> - ** <> - ** <> -* <> - ** <> - ** <> - ** <> - ** <> - ** <> -* <> - ** <> - ** <> - ** <> -* <> - ** <> - ** <> -* <> +* <<_constants,Constants>> +* <<_strings,Strings>> + ** <<_str_to_felt,`str_to_felt`>> + ** <<_felt_to_str,`felt_to_str`>> +* <<_uint256,Uint256>> + ** <<_uint,`uint`>> + ** <<_to_uint,`to_uint`>> + ** <<_from_uint,`from_uint`>> + ** <<_add_uint,`add_uint`>> + ** <<_sub_uint,`sub_uint`>> +* <<_assertions,Assertions>> + ** <<_assert_revert,`assert_revert`>> + ** <<_assert_revert_entry_point,`assert_revert_entry_point`>> + ** <<_assert_event_emitted,`assert_events_emitted`>> +* <<_memoization,Memoization>> + ** <<_get_contract_class,`get_contract_class`>> + ** <<_cached_contract,`cached_contract`>> +* <<_mocksigner,MockSigner>> == Constants -To ease the readability of Cairo contracts, this project includes reusable link:../src/openzeppelin/utils/constants.cairo[constants variables] like `UINT8_MAX`, or EIP165 interface IDs such as `IERC165_ID` or `IERC721_ID`. -For more information on how interface ids are calculated, see the link:../docs/Introspection.md#interface-calculations[ERC165 documentation]. +To ease the readability of Cairo contracts, this project includes reusable https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/utils/constants.cairo[constant variables] like `UINT8_MAX`, or EIP165 interface IDs such as `IERC165_ID` or `IERC721_ID`. +For more information on how interface ids are calculated, see the xref:introspection.md#interface-calculations[ERC165 documentation]. == Strings @@ -284,4 +284,4 @@ def foo_factory(contract_classes, foo_init): `MockSigner` is used to perform transactions with an instance of https://github.com/OpenZeppelin/nile/blob/main/src/nile/signer.py[Nile's Signer] on a given Account, crafting the transaction and managing nonces. The `Signer` instance manages signatures and is leveraged by `MockSigner` to operate with the Account contract's `\\__execute__` method. -See link:../docs/Account.md#mocksigner-utility[MockSigner utility] for more information. +See xref:account.md#mocksigner-utility[MockSigner utility] for more information. From c163952c911bcf5fe07074e82a17a66c8365735b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Sat, 23 Jul 2022 15:34:00 -0300 Subject: [PATCH 24/39] fix links --- docs/modules/ROOT/pages/proxies.adoc | 4 ++-- docs/modules/ROOT/pages/utilities.adoc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/modules/ROOT/pages/proxies.adoc b/docs/modules/ROOT/pages/proxies.adoc index abca1b76b..c4303d9f9 100644 --- a/docs/modules/ROOT/pages/proxies.adoc +++ b/docs/modules/ROOT/pages/proxies.adoc @@ -114,7 +114,7 @@ The proxy's fallback function redirects the function call to the implementation === Implementation contract The implementation contract, also known as the logic contract, receives the redirected function calls from the proxy contract. -The implementation contract should follow the xref:extensibility.md#the-pattern[Extensibility pattern] and import directly from the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/upgrades/library.cairo[Proxy library]. +The implementation contract should follow the xref:extensibility.adoc#the-pattern[Extensibility pattern] and import directly from the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/upgrades/library.cairo[Proxy library]. The implementation contract should: @@ -338,7 +338,7 @@ For more information on declaring classes, see https://starknet.io/docs/hello_st === Handling method calls -As with most StarkNet contracts, interacting with a proxy contract requires an xref:eccount.md#quickstart[account abstraction]. +As with most StarkNet contracts, interacting with a proxy contract requires an xref:eccount.adoc#quickstart[account abstraction]. One notable difference with proxy contracts versus other contract implementations is that calling `@view` methods also requires an account abstraction. As of now, direct calls to default entrypoints are only supported by StarkNet's `syscalls` from other contracts i.e. account contracts. diff --git a/docs/modules/ROOT/pages/utilities.adoc b/docs/modules/ROOT/pages/utilities.adoc index addd6cffb..08478dfca 100644 --- a/docs/modules/ROOT/pages/utilities.adoc +++ b/docs/modules/ROOT/pages/utilities.adoc @@ -28,7 +28,7 @@ CAUTION: Expect this module to evolve (as it has already done). == Constants To ease the readability of Cairo contracts, this project includes reusable https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/utils/constants.cairo[constant variables] like `UINT8_MAX`, or EIP165 interface IDs such as `IERC165_ID` or `IERC721_ID`. -For more information on how interface ids are calculated, see the xref:introspection.md#interface-calculations[ERC165 documentation]. +For more information on how interface ids are calculated, see the xref:introspection.adoc#interface-calculations[ERC165 documentation]. == Strings @@ -284,4 +284,4 @@ def foo_factory(contract_classes, foo_init): `MockSigner` is used to perform transactions with an instance of https://github.com/OpenZeppelin/nile/blob/main/src/nile/signer.py[Nile's Signer] on a given Account, crafting the transaction and managing nonces. The `Signer` instance manages signatures and is leveraged by `MockSigner` to operate with the Account contract's `\\__execute__` method. -See xref:account.md#mocksigner-utility[MockSigner utility] for more information. +See xref:account.adoc#mocksigner-utility[MockSigner utility] for more information. From fad466c26740ebafb65dfd67496787fefc3df893 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Sat, 23 Jul 2022 15:42:07 -0300 Subject: [PATCH 25/39] fix utils doc links --- docs/modules/ROOT/pages/proxies.adoc | 2 +- docs/modules/ROOT/pages/utilities.adoc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/modules/ROOT/pages/proxies.adoc b/docs/modules/ROOT/pages/proxies.adoc index c4303d9f9..9d19e06d3 100644 --- a/docs/modules/ROOT/pages/proxies.adoc +++ b/docs/modules/ROOT/pages/proxies.adoc @@ -70,7 +70,7 @@ Thus, OpenZeppelin offers an https://github.com/OpenZeppelin/openzeppelin-contra See OpenZeppelin's https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable[Writing Upgradeable Contracts] for more info. The Cairo programming language does not support inheritance. -Instead, Cairo contracts follow the xref:../docs/Extensibility.adoc[Extensibility Pattern] which already uses initializer methods to mimic constructors. +Instead, Cairo contracts follow the xref:extensibility.adoc[Extensibility Pattern] which already uses initializer methods to mimic constructors. Upgradeable contracts do not, therefore, require a separate library with refactored constructor logic. === Storage diff --git a/docs/modules/ROOT/pages/utilities.adoc b/docs/modules/ROOT/pages/utilities.adoc index 08478dfca..203970026 100644 --- a/docs/modules/ROOT/pages/utilities.adoc +++ b/docs/modules/ROOT/pages/utilities.adoc @@ -28,7 +28,7 @@ CAUTION: Expect this module to evolve (as it has already done). == Constants To ease the readability of Cairo contracts, this project includes reusable https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/utils/constants.cairo[constant variables] like `UINT8_MAX`, or EIP165 interface IDs such as `IERC165_ID` or `IERC721_ID`. -For more information on how interface ids are calculated, see the xref:introspection.adoc#interface-calculations[ERC165 documentation]. +For more information on how interface ids are calculated, see the xref:introspection.adoc#interface_calculations[ERC165 documentation]. == Strings @@ -284,4 +284,4 @@ def foo_factory(contract_classes, foo_init): `MockSigner` is used to perform transactions with an instance of https://github.com/OpenZeppelin/nile/blob/main/src/nile/signer.py[Nile's Signer] on a given Account, crafting the transaction and managing nonces. The `Signer` instance manages signatures and is leveraged by `MockSigner` to operate with the Account contract's `\\__execute__` method. -See xref:account.adoc#mocksigner-utility[MockSigner utility] for more information. +See xref:account.adoc#mocksigner_utility[MockSigner utility] for more information. From caefacaec5c11cde6ff803a64df793af53c956ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Sat, 23 Jul 2022 15:46:36 -0300 Subject: [PATCH 26/39] fix links and references --- docs/modules/ROOT/pages/access.adoc | 62 +++++------ docs/modules/ROOT/pages/accounts.adoc | 58 +++++----- docs/modules/ROOT/pages/erc20.adoc | 60 +++++------ docs/modules/ROOT/pages/erc721.adoc | 118 ++++++++++----------- docs/modules/ROOT/pages/extensibility.adoc | 14 +-- docs/modules/ROOT/pages/index.adoc | 2 +- docs/modules/ROOT/pages/introspection.adoc | 20 ++-- docs/modules/ROOT/pages/proxies.adoc | 34 +++--- docs/modules/ROOT/pages/security.adoc | 10 +- docs/modules/ROOT/pages/utilities.adoc | 36 +++---- 10 files changed, 207 insertions(+), 207 deletions(-) diff --git a/docs/modules/ROOT/pages/access.adoc b/docs/modules/ROOT/pages/access.adoc index 4c8e5759f..adaf3fe36 100644 --- a/docs/modules/ROOT/pages/access.adoc +++ b/docs/modules/ROOT/pages/access.adoc @@ -1,6 +1,6 @@ :ownable-cairo: link:https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/access/ownable.cairo[Ownable] -:extensibility-pattern: xref:extensibility.adoc#the-pattern +:extensibility-pattern: xref:extensibility.adoc#the_pattern = Access @@ -12,16 +12,16 @@ It is therefore critical to understand how you implement it, lest someone else h == Table of Contents -* <<_ownable,Ownable>> - ** <<_quickstart,Quickstart>> - ** <<_ownable_library_api,Ownable library API>> - ** <<_ownable_events,Ownable events>> -* <<_accesscontrol,AccessControl>> - ** <<_usage,Usage>> - ** <<_granting_and_revoking_roles,Granting and revoking roles>> - ** <<_creating_role_identifiers,Creating role identifiers>> - ** <<_accesscontrol_library_api,AccessControl library API>> - ** <<_accesscontrol_events,AccessControl events>> +* <> + ** <> + ** <> + ** <> +* <> + ** <> + ** <> + ** <> + ** <> + ** <> == Ownable @@ -33,7 +33,7 @@ OpenZeppelin Contracts for Cairo provides {ownable-cairo} for implementing owner === Quickstart Integrating {ownable-cairo} into a contract first requires assigning an owner. -The implementing contract's constructor should set the initial owner by passing the owner's address to Ownable's <<_initializer,initializer>> like this: +The implementing contract's constructor should set the initial owner by passing the owner's address to Ownable's <> like this: [,cairo] ---- @@ -140,7 +140,7 @@ owner: felt Transfers ownership of the contract to a new account (`new_owner`). Can only be called by the current owner. -Emits a <<_ownershiptransferred,`OwnershipTransferred`>> event. +Emits a <> event. Parameters: @@ -159,7 +159,7 @@ Leaves the contract without owner. It will not be possible to call functions with `assert_only_owner` anymore. Can only be called by the current owner. -Emits a <<_ownershiptransferred,`OwnershipTransferred`>> event. +Emits a <> event. Parameters: @@ -174,7 +174,7 @@ None. Transfers ownership of the contract to a new account (`new_owner`). {extensibility-pattern}[`internal`] function without access restriction. -Emits a <<_ownershiptransferred,`OwnershipTransferred`>> event. +Emits a <> event. Parameters: @@ -214,15 +214,15 @@ You may want for an account to have permission to ban users from a system, but n https://en.wikipedia.org/wiki/Role-based_access_control[Role-Based Access Control (RBAC)] offers flexibility in this regard. In essence, we will be defining multiple roles, each allowed to perform different sets of actions. -An account may have, for example, 'moderator', 'minter' or 'admin' roles, which you will then check for instead of simply using <<_assert_only_owner,assert_only_owner>>. -This check can be enforced through <<_assert_only_role,assert_only_role>>. +An account may have, for example, 'moderator', 'minter' or 'admin' roles, which you will then check for instead of simply using <>. +This check can be enforced through <>. Separately, you will be able to define rules for how accounts can be granted a role, have it revoked, and more. Most software uses access control systems that are role-based: some users are regular users, some may be supervisors or managers, and a few will often have administrative privileges. === Usage -For each role that you want to define, you will create a new _role identifier_ that is used to grant, revoke, and check if an account has that role (see <<_creating_role_identifiers,Creating role identifiers>> for information on creating identifiers). +For each role that you want to define, you will create a new _role identifier_ that is used to grant, revoke, and check if an account has that role (see <> for information on creating identifiers). Here's a simple example of implementing `AccessControl` on a portion of an link:https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20.cairo[ERC20 token contract] which defines and sets the 'minter' role: @@ -264,9 +264,9 @@ func mint{ end ---- -CAUTION: Make sure you fully understand how <<_accesscontrol,AccessControl>> works before using it on your system, or copy-pasting the examples from this guide. +CAUTION: Make sure you fully understand how <> works before using it on your system, or copy-pasting the examples from this guide. -While clear and explicit, this isn't anything we wouldn't have been able to achieve with <<_ownable,Ownable>>. +While clear and explicit, this isn't anything we wouldn't have been able to achieve with <>. Indeed, where `AccessControl` shines is in scenarios where granular permissions are required, which can be implemented by defining _multiple_ roles. Let's augment our ERC20 token example by also defining a 'burner' role, which lets accounts destroy tokens, and by using `assert_only_role`: @@ -539,9 +539,9 @@ has_role: felt ==== `get_role_admin` Returns the admin role that controls `role`. -See <<_grant_role,grant_role>> and <<_revoke_role,revoke_role>>. +See <> and <>. -To change a role's admin, use <<_set_role_admin,`_set_role_admin`>>. +To change a role's admin, use <>. Parameters: @@ -561,7 +561,7 @@ admin: felt Grants `role` to `user`. -If `user` had not been already granted `role`, emits a <<_rolegranted,RoleGranted>> event. +If `user` had not been already granted `role`, emits a <> event. Requirements: @@ -583,7 +583,7 @@ None. Revokes `role` from `user`. -If `user` had been granted `role`, emits a <<_rolerevoked,RoleRevoked>> event. +If `user` had been granted `role`, emits a <> event. Requirements: @@ -605,9 +605,9 @@ None. Revokes `role` from the calling `user`. -Roles are often managed via <<_grant_role,grant_role>> and <<_revoke_role,revoke_role>>: this function's purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). +Roles are often managed via <> and <>: this function's purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). -If the calling `user` had been revoked `role`, emits a <<_rolerevoked,RoleRevoked>> event. +If the calling `user` had been revoked `role`, emits a <> event. Requirements: @@ -632,7 +632,7 @@ Grants `role` to `user`. {extensibility-pattern}[`internal`] function without access restriction. -Emits a <<_rolegranted,RoleGranted>> event. +Emits a <> event. Parameters: @@ -653,7 +653,7 @@ Revokes `role` from `user`. {extensibility-pattern}[`internal`] function without access restriction. -Emits a <<_rolerevoked,RoleRevoked>> event. +Emits a <> event. Parameters: @@ -672,7 +672,7 @@ None. {extensibility-pattern}[`internal`] function that sets `admin_role` as ``role``'s admin role. -Emits a <<_roleadminchanged,RoleAdminChanged>> event. +Emits a <> event. Parameters: @@ -725,8 +725,8 @@ Emitted when account is revoked role. `sender` is the account that originated the contract call: -* if using <<_revoke_role,revoke_role>>, it is the admin role bearer -* if using <<_renounce_role,renounce_role>>, it is the role bearer (i.e. +* if using <>, it is the admin role bearer +* if using <>, it is the role bearer (i.e. `account`). [,cairo] diff --git a/docs/modules/ROOT/pages/accounts.adoc b/docs/modules/ROOT/pages/accounts.adoc index 91f8f3b5f..168594c5e 100644 --- a/docs/modules/ROOT/pages/accounts.adoc +++ b/docs/modules/ROOT/pages/accounts.adoc @@ -11,33 +11,33 @@ A more detailed writeup on the topic can be found on https://perama-v.github.io/ == Table of Contents -* <<_quickstart,Quickstart>> -* <<_standard_interface,Standard Interface>> -* <<_keys_signatures_and_signers,Keys, signatures and signers>> - ** <<_signer,Signer>> - ** <<_mocksigner_utility,MockSigner utility>> - ** <<_mockethsigner_utility,MockEthSigner utility>> -* <<_account_entrypoint,Account entrypoint>> -* <<_call_and_accountcallarray_format,Call and AccountCallArray format>> - ** <<_call,Call>> - ** <<_accountcallarray,AccountCallArray>> -* <<_multicall_transactions,Multicall transactions>> -* <<_api_specification,API Specification>> - ** <<_get_public_key,`get_public_key`>> - ** <<_get_nonce,`get_nonce`>> - ** <<_set_public_key,`set_public_key`>> - ** <<_is_valid_signature,`is_valid_signature`>> +* <> +* <> +* <> + ** <> + ** <> + ** <> +* <> +* <> + ** <> + ** <> +* <> +* <> + ** <> + ** <> + ** <> + ** <> ** <<\\__execute__,`\\__execute__`>> - ** <<_is_valid_eth_signature,`is_valid_eth_signature`>> - ** <<_eth_execute,`eth_execute`>> - ** <<__unsafe_execute,`_unsafe_execute`>> -* <<_presets,Presets>> - ** <<_account,Account>> - ** <<_eth_account,Eth Account>> -* <<_account_differentiation_with_erc165,Account differentiation with ERC165>> -* <<_extending_the_account_contract,Extending the Account contract>> -* <<_l1_escape_hatch_mechanism,L1 escape hatch mechanism>> -* <<_paying_for_gas,Paying for gas>> + ** <> + ** <> + ** <<_unsafe_execute,`_unsafe_execute`>> +* <> + ** <> + ** <> +* <> +* <> +* <> +* <> == Quickstart @@ -243,7 +243,7 @@ The idea is for all user intent to be encoded into a `Call` representing a smart Users can also pack multiple messages into a single transaction (creating a multicall transaction). Cairo currently does not support arrays of structs with pointers which means the `\\__execute__` function cannot properly iterate through mutiple ``Call``s. Instead, this implementation utilizes a workaround with the `AccountCallArray` struct. -See <<_multicall_transactions,Multicall transactions>>. +See <>. === `Call` @@ -500,7 +500,7 @@ It's an internal method that performs the following tasks: . Increments the nonce. . Takes the input and builds a `Call` for each iterated message. -See <<_multicall_transactions,Multicall transactions>> for more information. +See <> for more information. . Calls the target contract with the intended function selector and calldata parameters . Forwards the contract call response data as return value @@ -529,7 +529,7 @@ In the case of account contracts, querying `supportsInterface` of an account's a == Extending the Account contract -Account contracts can be extended by following the xref:extensibility.adoc#the-pattern[extensibility pattern]. +Account contracts can be extended by following the xref:extensibility.adoc#the_pattern[extensibility pattern]. To implement custom account contracts, a pair of `validate` and `execute` functions should be exposed. This is why the Account library comes with different flavors of such pairs, like the vanilla `is_valid_signature` and `execute`, or the Ethereum flavored `is_valid_eth_signature` and `eth_execute` pair. diff --git a/docs/modules/ROOT/pages/erc20.adoc b/docs/modules/ROOT/pages/erc20.adoc index f5ca904d4..781cd33d0 100644 --- a/docs/modules/ROOT/pages/erc20.adoc +++ b/docs/modules/ROOT/pages/erc20.adoc @@ -5,29 +5,29 @@ The `ERC20.cairo` contract implements an approximation of https://eips.ethereum. == Table of Contents -* <<_interface,Interface>> - ** <<_erc20_compatibility,ERC20 compatibility>> -* <<_usage,Usage>> -* <<_extensibility,Extensibility>> -* <<_presets,Presets>> - ** <<_erc20_basic,ERC20 (basic)>> - ** <<_erc20_mintable,ERC20_Mintable>> - ** <<_erc20_pausable,ERC20_Pausable>> - ** <<_erc20_upgradeable,ERC20_Upgradeable>> -* <<_api_specification,API Specification>> - ** <<_methods,Methods>> - *** <<_name,`name`>> - *** <<_symbol,`symbol`>> - *** <<_decimals,`decimals`>> - *** <<_totalsupply,`totalSupply`>> - *** <<_balanceof,`balanceOf`>> - *** <<_allowance,`allowance`>> - *** <<_transfer,`transfer`>> - *** <<_transferfrom,`transferFrom`>> - *** <<_approve,`approve`>> - ** <<_events,Events>> - *** <<_transfer_event,`Transfer (event)`>> - *** <<_approval_event,`Approval (event)`>> +* <> + ** <> +* <> +* <> +* <> + ** <> + ** <> + ** <> + ** <> +* <> + ** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + ** <> + *** <> + *** <> == Interface @@ -134,7 +134,7 @@ await signer.send_transaction(account, erc20.contract_address, 'transfer', [reci == Extensibility -ERC20 contracts can be extended by following the xref:extensibility.adoc#the-pattern[extensibility pattern]. +ERC20 contracts can be extended by following the xref:extensibility.adoc#the_pattern[extensibility pattern]. The basic idea behind integrating the pattern is to import the requisite ERC20 methods from the ERC20 library and incorporate the extended logic thereafter. For example, let's say you wanted to implement a pausing mechanism. The contract should first import the ERC20 methods and the extended logic from the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/security/pausable.cairo[pausable library] i.e. @@ -164,7 +164,7 @@ Some other ways to extend ERC20 contracts may include: * Creating a timelock * Adding roles such as owner or minter -For full examples of the extensibility pattern being used in ERC20 contracts, see <<_presets,Presets>>. +For full examples of the extensibility pattern being used in ERC20 contracts, see <>. == Presets @@ -189,7 +189,7 @@ This preset proves useful for scenarios such as preventing trades until the end The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20_Upgradeable.cairo[`ERC20_Upgradeable`] preset allows the contract owner to upgrade a contract by deploying a new ERC20 implementation contract while also maintaing the contract's state. This preset proves useful for scenarios such as eliminating bugs and adding new features. -For more on upgradeability, see xref:proxies.adoc#contract-upgrades[Contract upgrades]. +For more on upgradeability, see xref:proxies.adoc#contract_upgrades[Contract upgrades]. == API Specification @@ -327,7 +327,7 @@ remaining: Uint256 Moves `amount` tokens from the caller's account to `recipient`. It returns `1` representing a bool if it succeeds. -Emits a <<_transfer_event,Transfer>> event. +Emits a <> event. Parameters: @@ -350,7 +350,7 @@ Moves `amount` tokens from `sender` to `recipient` using the allowance mechanism `amount` is then deducted from the caller's allowance. It returns `1` representing a bool if it succeeds. -Emits a <<_transfer_event,Transfer>> event. +Emits a <> event. Parameters: @@ -373,7 +373,7 @@ success: felt Sets `amount` as the allowance of `spender` over the caller's tokens. It returns `1` representing a bool if it succeeds. -Emits an <<_approval_event,Approval>> event. +Emits an <> event. Parameters: @@ -418,7 +418,7 @@ value: Uint256 ==== `Approval (event)` -Emitted when the allowance of a `spender` for an `owner` is set by a call to <<_approve,approve>>. +Emitted when the allowance of a `spender` for an `owner` is set by a call to <>. `value` is the new allowance. Parameters: diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 35dcf0704..87e54e616 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -5,49 +5,49 @@ The `ERC721.cairo` contract implements an approximation of https://eips.ethereum == Table of Contents -* <<_ierc721,IERC721>> -* <<_erc721_compatibility,ERC721 Compatibility>> -* <<_usage,Usage>> - ** <<_token_transfers,Token Transfers>> - ** <<_interpreting_erc721_uris,Interpreting ERC721 URIs>> - ** <<_erc721received,ERC721Received>> - *** <<_ierc721_receiver,IERC721_Receiver>> - ** <<_supporting_interfaces,Supporting Interfaces>> - ** <<_ready_to_use_presets,Ready_to_Use Presets>> -* <<_extensibility,Extensibility>> -* <<_presets,Presets>> - ** <<_erc721_mintable_burnable,ERC721_Mintable_Burnable>> - ** <<_erc721_mintable_pausable,ERC721_Mintable_Pausable>> - ** <<_erc721_enumerable_mintable_burnable,ERC721_Enumerable_Mintable_Burnable>> - *** <<_ierc721_enumerable,IERC721_Enumerable>> - ** <<_erc721_metadata,ERC721_Metadata>> - *** <<_ierc721_metadata,IERC721_Metadata>> -* <<_utilities,Utilities>> - ** <<_erc721_holder,ERC721_Holder>> -* <<_api_specification,API Specification>> - ** <<_ierc721_api,`IERC721`>> - *** <<_balanceof,`balanceOf`>> - *** <<_ownerof,`ownerOf`>> - *** <<_safetransferfrom,`safeTransferFrom`>> - *** <<_transferfrom,`transferFrom`>> - *** <<_approve,`approve`>> - *** <<_setapprovalforall,`setApprovalForAll`>> - *** <<_getapproved,`getApproved`>> - *** <<_isapprovedforall,`isApprovedForAll`>> - ** <<_events,Events>> - *** <<_approval_event,`Approval (event)`>> - *** <<_approvalforall_event,`ApprovalForAll (event)`>> - *** <<_transfer_event,`Transfer (event)`>> - ** <<_ierc721_metadata,`IERC721_Metadata`>> - *** <<_name,`name`>> - *** <<_symbol,`symbol`>> - *** <<_tokenuri,`tokenURI`>> - ** <<_ierc721_enumerable,`IERC721_Enumerable`>> - *** <<_totalsupply,`totalSupply`>> - *** <<_tokenbyindex,`tokenByIndex`>> - *** <<_tokenofownerbyindex,`tokenOfOwnerByIndex`>> - ** <<_ierc721_receiver,`IERC721_Receiver`>> - *** <<_onerc721received,`onERC721Received`>> +* <> +* <> +* <> + ** <> + ** <> + ** <> + *** <> + ** <> + ** <> +* <> +* <> + ** <> + ** <> + ** <> + *** <> + ** <> + *** <> +* <> + ** <> +* <> + ** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + *** <> + ** <> + *** <> + *** <> + *** <> + ** <> + *** <> + *** <> + *** <> + ** <> + *** <> + *** <> + *** <> + ** <> + *** <> == IERC721 @@ -105,10 +105,10 @@ But some differences can still be found, such as: The EIP721 standard, however, states that the return value should be of type string. If a token's URI is not set, the returned value is `0`. Note that URIs cannot exceed 31 characters. -See <<_interpreting_erc721_uris,Interpreting ERC721 URIs>> +See <> * ``interface_id``s are hardcoded and initialized by the constructor. The hardcoded values derive from Solidity's selector calculations. -See <<_supporting_interfaces,Supporting Interfaces>> +See <> * `safeTransferFrom` can only be expressed as a single function in Cairo as opposed to the two functions declared in EIP721. The difference between both functions consists of accepting `data` as an argument. Because function overloading is currently not possible in Cairo, `safeTransferFrom` by default accepts the `data` argument. @@ -122,7 +122,7 @@ hence, the method accepts `data_len` and `data` respectively as types `felt` and This follows OpenZeppelin's https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/introspection/ERC165Storage.sol[ERC165Storage] * `IERC721_Receiver` compliant contracts (`ERC721_Holder`) return a hardcoded selector id according to EVM selectors, since selectors are calculated differently in Cairo. This is in line with the ERC165 interfaces design choice towards EVM compatibility. -See the xref:./Introspection.adoc[Introspection docs] for more info +See the xref:introspection.adoc[Introspection docs] for more info * `IERC721_Receiver` compliant contracts (`ERC721_Holder`) must support ERC165 by registering the `IERC721_Receiver` selector id in its constructor and exposing the `supportsInterface` method. In doing so, recipient contracts (both accounts and non-accounts) can be verified that they support ERC721 transfers * `ERC721_Enumerable` tracks the total number of tokens with the `all_tokens` and `all_tokens_len` storage variables mimicking the array of the Solidity implementation. @@ -192,7 +192,7 @@ The `safeTransferFrom` method incorporates the following conditional logic: . if the calling address is not an account contract, the safe function will check that the contract supports ERC721 tokens The current implementation of `safeTansferFrom` checks for `onERC721Received` and requires that the recipient contract supports ERC165 and exposes the `supportsInterface` method. -See <<_erc721received,ERC721Received>>. +See <>. === Interpreting ERC721 URIs @@ -280,16 +280,16 @@ For example: * `ERC721_Mintable_Burnable` includes `mint` and `burn` * `ERC721_Mintable_Pausable` includes `mint`, `pause`, and `unpause` -* `ERC721_Enumerable_Mintable_Burnable` includes `mint`, `burn`, and <<_ierc721_enumerable,IERC721_Enumerable>> methods +* `ERC721_Enumerable_Mintable_Burnable` includes `mint`, `burn`, and <> methods Ready-to-use presets are a great option for testing and prototyping. -See <<_presets,Presets>>. +See <>. == Extensibility -Following the xref:Extensibility.adoc[contracts extensibility pattern], this implementation is set up to include all ERC721 related storage and business logic under a namespace. +Following the xref:extensibility.adoc[contracts extensibility pattern], this implementation is set up to include all ERC721 related storage and business logic under a namespace. Developers should be mindful of manually exposing the required methods from the namespace to comply with the standard interface. -This is already done in the <<_presets,preset contracts>>; +This is already done in the <>; however, additional functionality can be added. For instance, you could: @@ -401,7 +401,7 @@ Accepts all token transfers. Make sure the contract is able to use its token with `IERC721.safeTransferFrom`, `IERC721.approve` or `IERC721.setApprovalForAll`. Also utilizes the ERC165 method `supportsInterface` to determine if the contract is an account. -See <<_erc721received,ERC721Received>> +See <> == API Specification @@ -479,9 +479,9 @@ owner: felt ==== `safeTransferFrom` Safely transfers `tokenId` token from `from_` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. -For information regarding how contracts communicate their awareness of the ERC721 protocol, see <<_erc721received,ERC721Received>>. +For information regarding how contracts communicate their awareness of the ERC721 protocol, see <>. -Emits a <<_transfer_event,Transfer>> event. +Emits a <> event. Parameters: @@ -503,7 +503,7 @@ None. Transfers `tokenId` token from `from_` to `to`. *The caller is responsible to confirm that `to` is capable of receiving NFTs or else they may be permanently lost*. -Emits a <<_transfer_event,Transfer>> event. +Emits a <> event. Parameters: @@ -523,7 +523,7 @@ None. Gives permission to `to` to transfer `tokenId` token to another account. The approval is cleared when the token is transferred. -Emits an <<_approval_event,Approval>> event. +Emits an <> event. Parameters: @@ -560,7 +560,7 @@ operator: felt Approve or remove `operator` as an operator for the caller. Operators can call `transferFrom` or `safeTransferFrom` for any token owned by the caller. -Emits an <<_approvalforall_event,ApprovalForAll>> event. +Emits an <> event. Parameters: @@ -731,7 +731,7 @@ totalSupply: Uint256 ==== `tokenByIndex` Returns a token ID owned by `owner` at a given `index` of its token list. -Use along with <<_balanceof,balanceOf>> to enumerate all of ``owner``'s tokens. +Use along with <> to enumerate all of ``owner``'s tokens. Parameters: @@ -750,7 +750,7 @@ tokenId: Uint256 ==== `tokenOfOwnerByIndex` Returns a token ID at a given `index` of all the tokens stored by the contract. -Use along with <<_totalsupply,totalSupply>> to enumerate all tokens. +Use along with <> to enumerate all tokens. Parameters: diff --git a/docs/modules/ROOT/pages/extensibility.adoc b/docs/modules/ROOT/pages/extensibility.adoc index 726a02a14..1a3155410 100644 --- a/docs/modules/ROOT/pages/extensibility.adoc +++ b/docs/modules/ROOT/pages/extensibility.adoc @@ -2,13 +2,13 @@ NOTE: Expect this pattern to evolve (as it has already done) or even disappear if https://community.starknet.io/t/contract-extensibility-pattern/210/11?u=martriay[proper extensibility features] are implemented into Cairo. -* <<_the_extensibility_problem,The extensibility problem>> -* <<_the_pattern,The pattern ™️>> - ** <<_libraries,Libraries>> - ** <<_contracts,Contracts>> -* <<_presets,Presets>> -* <<_function_names_and_coding_style,Function names and coding style>> -* <<_emulating_hooks,Emulating hooks>> +* <> +* <> + ** <> + ** <> +* <> +* <> +* <> == The extensibility problem diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index f5270110e..25efb6aac 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -72,7 +72,7 @@ nile deploy MyToken --al NOTE: `` is expected to be two integers i.e. `1` `0`. -See xref:utilities.adoc#Uint256[Uint256] for more information. +See xref:utilities.adoc#uint256[uint256] for more information. === Write a custom contract using library modules diff --git a/docs/modules/ROOT/pages/introspection.adoc b/docs/modules/ROOT/pages/introspection.adoc index 170295155..adb0fc863 100644 --- a/docs/modules/ROOT/pages/introspection.adoc +++ b/docs/modules/ROOT/pages/introspection.adoc @@ -4,16 +4,16 @@ CAUTION: Expect this module to evolve. == Table of Contents -* <<_erc165,ERC165>> - ** <<_interface_calculations,Interface calculations>> - ** <<_registering_interfaces,Registering interfaces>> - ** <<_querying_interfaces,Querying interfaces>> - ** <<_ierc165,IERC165>> - ** <<_ierc165_api_specification,IERC165 API Specification>> - *** <<_supportsinterface,`supportsInterface`>> - ** <<_erc165_library_functions,ERC165 Library Functions>> - *** <<_supportsinterface2,`supports_interface`>> - *** <<_register_interface,`register_interface`>> +* <> + ** <> + ** <> + ** <> + ** <> + ** <> + *** <> + ** <> + *** <> + *** <> == ERC165 diff --git a/docs/modules/ROOT/pages/proxies.adoc b/docs/modules/ROOT/pages/proxies.adoc index 9d19e06d3..c1309edda 100644 --- a/docs/modules/ROOT/pages/proxies.adoc +++ b/docs/modules/ROOT/pages/proxies.adoc @@ -4,21 +4,21 @@ NOTE: Expect rapid iteration as this pattern matures and more patterns potential == Table of Contents -* <<_quickstart,Quickstart>> -* <<_solidity\/cairo_upgrades_comparison,Solidity/Cairo upgrades comparison>> - ** <<_constructors,Constructors>> - ** <<_storage,Storage>> -* <<_proxies2,Proxies>> - ** <<_proxy_contract,Proxy contract>> - ** <<_implementation_contract,Implementation contract>> -* <<_upgrades_library_api,Upgrades library API>> - ** <<_methods,Methods>> - ** <<_events,Events>> -* <<_using_proxies,Using proxies>> - ** <<_contract_upgrades,Contract upgrades>> - ** <<_declaring_contracts,Declaring contracts>> - ** <<_handling_method_calls,Handling method calls>> -* <<_presets,Presets>> +* <> +* <> + ** <> + ** <> +* <> + ** <> + ** <> +* <> + ** <> + ** <> +* <> + ** <> + ** <> + ** <> +* <> == Quickstart @@ -114,7 +114,7 @@ The proxy's fallback function redirects the function call to the implementation === Implementation contract The implementation contract, also known as the logic contract, receives the redirected function calls from the proxy contract. -The implementation contract should follow the xref:extensibility.adoc#the-pattern[Extensibility pattern] and import directly from the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/upgrades/library.cairo[Proxy library]. +The implementation contract should follow the xref:extensibility.adoc#the_pattern[Extensibility pattern] and import directly from the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/upgrades/library.cairo[Proxy library]. The implementation contract should: @@ -136,7 +136,7 @@ Instead, use an initializer method that invokes the Proxy `constructor`. NOTE: The Proxy `constructor` includes a check the ensures the initializer can only be called once; however, `_set_implementation` does not include this check. -It's up to the developers to protect their implementation contract's upgradeability with access controls such as <<_assert_only_admin,`assert_only_admin`>>. +It's up to the developers to protect their implementation contract's upgradeability with access controls such as <>. For a full implementation contract example, please see: diff --git a/docs/modules/ROOT/pages/security.adoc b/docs/modules/ROOT/pages/security.adoc index 66b3f1736..991f644cc 100644 --- a/docs/modules/ROOT/pages/security.adoc +++ b/docs/modules/ROOT/pages/security.adoc @@ -6,11 +6,11 @@ CAUTION: Expect this module to evolve. == Table of Contents -* <<_initializable,Initializable>> -* <<_pausable,Pausable>> -* <<_reentrancy_guard,Reentrancy Guard>> -* <<_safemath,SafeMath>> - ** <<_safeuint256,SafeUint256>> +* <> +* <> +* <> +* <> + ** <> == Initializable diff --git a/docs/modules/ROOT/pages/utilities.adoc b/docs/modules/ROOT/pages/utilities.adoc index 203970026..7368a4dc1 100644 --- a/docs/modules/ROOT/pages/utilities.adoc +++ b/docs/modules/ROOT/pages/utilities.adoc @@ -6,24 +6,24 @@ CAUTION: Expect this module to evolve (as it has already done). == Table of Contents -* <<_constants,Constants>> -* <<_strings,Strings>> - ** <<_str_to_felt,`str_to_felt`>> - ** <<_felt_to_str,`felt_to_str`>> -* <<_uint256,Uint256>> - ** <<_uint,`uint`>> - ** <<_to_uint,`to_uint`>> - ** <<_from_uint,`from_uint`>> - ** <<_add_uint,`add_uint`>> - ** <<_sub_uint,`sub_uint`>> -* <<_assertions,Assertions>> - ** <<_assert_revert,`assert_revert`>> - ** <<_assert_revert_entry_point,`assert_revert_entry_point`>> - ** <<_assert_event_emitted,`assert_events_emitted`>> -* <<_memoization,Memoization>> - ** <<_get_contract_class,`get_contract_class`>> - ** <<_cached_contract,`cached_contract`>> -* <<_mocksigner,MockSigner>> +* <> +* <> + ** <> + ** <> +* <> + ** <> + ** <> + ** <> + ** <> + ** <> +* <> + ** <> + ** <> + ** <> +* <> + ** <> + ** <> +* <> == Constants From 15991b07cc4df37a5228a20630cff657b4a8fd14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Sat, 23 Jul 2022 15:58:15 -0300 Subject: [PATCH 27/39] fix links --- docs/modules/ROOT/pages/accounts.adoc | 16 ++++++++-------- docs/modules/ROOT/pages/proxies.adoc | 4 ++-- docs/modules/ROOT/pages/utilities.adoc | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/modules/ROOT/pages/accounts.adoc b/docs/modules/ROOT/pages/accounts.adoc index 168594c5e..88f5399cc 100644 --- a/docs/modules/ROOT/pages/accounts.adoc +++ b/docs/modules/ROOT/pages/accounts.adoc @@ -27,10 +27,10 @@ A more detailed writeup on the topic can be found on https://perama-v.github.io/ ** <> ** <> ** <> - ** <<\\__execute__,`\\__execute__`>> + ** <> ** <> ** <> - ** <<_unsafe_execute,`_unsafe_execute`>> + ** <> * <> ** <> ** <> @@ -295,8 +295,8 @@ The rebuilding logic is set in the internal `_from_call_array_to_call`. This is the basic flow: -. The user sends the messages for the transaction through a Signer instantiation which looks like this: -+ +First, the user sends the messages for the transaction through a Signer instantiation which looks like this: + [,python] ---- await signer.send_transaction( @@ -306,12 +306,12 @@ This is the basic flow: ] ) ---- -+ -The `_from_call_to_call_array` method in {test-utils}[utils.py] converts each call into the `AccountCallArray` format and cumulatively stores the calldata of every call into a single array. + +Then the `_from_call_to_call_array` method in {test-utils}[utils.py] converts each call into the `AccountCallArray` format and cumulatively stores the calldata of every call into a single array. Next, both arrays (as well as the `sender`, `nonce`, and `max_fee`) are used to create the transaction hash. The Signer then invokes `\\__execute__` with the signature and passes `AccountCallArray`, calldata, and nonce as arguments. -. The `\\__execute__` method takes the `AccountCallArray` and calldata and builds an array of ``Call``s (MultiCall). +Finally, the `\\__execute__` method takes the `AccountCallArray` and calldata and builds an array of ``Call``s (MultiCall). NOTE: Every transaction utilizes `AccountCallArray`. A single `Call` is treated as a bundle with one message. @@ -496,7 +496,7 @@ NOTE: The current signature scheme expects a 7-element array like `[sig_v, uint2 === `_unsafe_execute` -It's an internal method that performs the following tasks: +It's an xref:extensibility.adoc#the_pattern[internal] method that performs the following tasks: . Increments the nonce. . Takes the input and builds a `Call` for each iterated message. diff --git a/docs/modules/ROOT/pages/proxies.adoc b/docs/modules/ROOT/pages/proxies.adoc index c1309edda..548dbf89f 100644 --- a/docs/modules/ROOT/pages/proxies.adoc +++ b/docs/modules/ROOT/pages/proxies.adoc @@ -5,7 +5,7 @@ NOTE: Expect rapid iteration as this pattern matures and more patterns potential == Table of Contents * <> -* <> +* <> ** <> ** <> * <> @@ -338,7 +338,7 @@ For more information on declaring classes, see https://starknet.io/docs/hello_st === Handling method calls -As with most StarkNet contracts, interacting with a proxy contract requires an xref:eccount.adoc#quickstart[account abstraction]. +As with most StarkNet contracts, interacting with a proxy contract requires an xref:accounts.adoc#quickstart[account abstraction]. One notable difference with proxy contracts versus other contract implementations is that calling `@view` methods also requires an account abstraction. As of now, direct calls to default entrypoints are only supported by StarkNet's `syscalls` from other contracts i.e. account contracts. diff --git a/docs/modules/ROOT/pages/utilities.adoc b/docs/modules/ROOT/pages/utilities.adoc index 7368a4dc1..835d055fb 100644 --- a/docs/modules/ROOT/pages/utilities.adoc +++ b/docs/modules/ROOT/pages/utilities.adoc @@ -284,4 +284,4 @@ def foo_factory(contract_classes, foo_init): `MockSigner` is used to perform transactions with an instance of https://github.com/OpenZeppelin/nile/blob/main/src/nile/signer.py[Nile's Signer] on a given Account, crafting the transaction and managing nonces. The `Signer` instance manages signatures and is leveraged by `MockSigner` to operate with the Account contract's `\\__execute__` method. -See xref:account.adoc#mocksigner_utility[MockSigner utility] for more information. +See xref:accounts.adoc#mocksigner_utility[MockSigner utility] for more information. From ab3deb21727435af8627cd8b8df85973bd39bd55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Sat, 23 Jul 2022 16:00:47 -0300 Subject: [PATCH 28/39] inline None. --- docs/modules/ROOT/pages/access.adoc | 68 ++++++---------------- docs/modules/ROOT/pages/accounts.adoc | 12 +--- docs/modules/ROOT/pages/erc721.adoc | 24 ++------ docs/modules/ROOT/pages/introspection.adoc | 4 +- docs/modules/ROOT/pages/proxies.adoc | 28 +++------ 5 files changed, 34 insertions(+), 102 deletions(-) diff --git a/docs/modules/ROOT/pages/access.adoc b/docs/modules/ROOT/pages/access.adoc index adaf3fe36..c371fd37a 100644 --- a/docs/modules/ROOT/pages/access.adoc +++ b/docs/modules/ROOT/pages/access.adoc @@ -103,30 +103,22 @@ Parameters: owner: felt ---- -Returns: - -None. +Returns: None. ==== `assert_only_owner` Reverts if called by any account other than the owner. In case of renounced ownership, any call from the default zero address will also be reverted. -Parameters: +Parameters: None. -None. - -Returns: - -None. +Returns: None. ==== `owner` Returns the address of the current owner. -Parameters: - -None. +Parameters: None. Returns: @@ -149,9 +141,7 @@ Parameters: new_owner: felt ---- -Returns: - -None. +Returns: None. ==== `renounce_ownership` @@ -161,13 +151,9 @@ Can only be called by the current owner. Emits a <> event. -Parameters: - -None. - -Returns: +Parameters: None. -None. +Returns: None. [#transfer-ownership-internal] ==== `_transfer_ownership` @@ -183,9 +169,7 @@ Parameters: new_owner: felt ---- -Returns: - -None. +Returns: None. === Ownable events @@ -493,13 +477,9 @@ Initializes AccessControl and should be called in the implementing contract's co This must only be called once. -Parameters: - -None. - -Returns: +Parameters: None. -None. +Returns: None. ==== `assert_only_role` @@ -513,9 +493,7 @@ Parameters: role: felt ---- -Returns: - -None. +Returns: None. ==== has_role @@ -575,9 +553,7 @@ role: felt user: felt ---- -Returns: - -None. +Returns: None. ==== `revoke_role` @@ -597,9 +573,7 @@ role: felt user: felt ---- -Returns: - -None. +Returns: None. ==== `renounce_role` @@ -621,9 +595,7 @@ role: felt user: felt ---- -Returns: - -None. +Returns: None. [#grantrole-internal] ==== `_grant_role` @@ -642,9 +614,7 @@ role: felt user: felt ---- -Returns: - -None. +Returns: None. [#revokerole-internal] ==== `_revoke_role` @@ -663,9 +633,7 @@ role: felt user: felt ---- -Returns: - -None. +Returns: None. [#setroleadmin] ==== `_set_role_admin` @@ -682,9 +650,7 @@ role: felt admin_role: felt ---- -Returns: - -None. +Returns: None. === AccessControl events diff --git a/docs/modules/ROOT/pages/accounts.adoc b/docs/modules/ROOT/pages/accounts.adoc index 88f5399cc..30b218281 100644 --- a/docs/modules/ROOT/pages/accounts.adoc +++ b/docs/modules/ROOT/pages/accounts.adoc @@ -351,9 +351,7 @@ end Returns the public key associated with the Account contract. -Parameters: - -None. +Parameters: None. Returns: @@ -366,9 +364,7 @@ public_key: felt Returns the current transaction nonce for the Account. -Parameters: - -None. +Parameters: None. Returns: @@ -389,9 +385,7 @@ Parameters: public_key: felt ---- -Returns: - -None. +Returns: None. === `is_valid_signature` diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 87e54e616..f6c5b878c 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -494,9 +494,7 @@ data_len: felt data: felt* ---- -Returns: - -None. +Returns: None. ==== `transferFrom` @@ -514,9 +512,7 @@ to: felt tokenId: Uint256 ---- -Returns: - -None. +Returns: None. ==== `approve` @@ -533,9 +529,7 @@ to: felt tokenId: Uint256 ---- -Returns: - -None. +Returns: None. ==== `getApproved` @@ -569,9 +563,7 @@ Parameters: operator: felt ---- -Returns: - -None. +Returns: None. ==== `isApprovedForAll` @@ -653,9 +645,7 @@ end Returns the token collection name. -Parameters: - -None. +Parameters: None. Returns: @@ -668,9 +658,7 @@ name: felt Returns the token collection symbol. -Parameters: - -None. +Parameters: None. Returns: diff --git a/docs/modules/ROOT/pages/introspection.adoc b/docs/modules/ROOT/pages/introspection.adoc index adb0fc863..d9ace3312 100644 --- a/docs/modules/ROOT/pages/introspection.adoc +++ b/docs/modules/ROOT/pages/introspection.adoc @@ -165,6 +165,4 @@ Parameters: interface_id: felt ---- -Returns: - -None. +Returns: None. diff --git a/docs/modules/ROOT/pages/proxies.adoc b/docs/modules/ROOT/pages/proxies.adoc index 548dbf89f..a2cc4c901 100644 --- a/docs/modules/ROOT/pages/proxies.adoc +++ b/docs/modules/ROOT/pages/proxies.adoc @@ -178,29 +178,21 @@ Parameters: proxy_admin: felt ---- -Returns: - -None. +Returns: None. ==== `assert_only_admin` Reverts if called by any account other than the admin. -Parameters: +Parameters: None. -None. - -Returns: - -None. +Returns: None. ==== `get_implementation` Returns the current implementation hash. -Parameters: - -None. +Parameters: None. Returns: @@ -213,9 +205,7 @@ implementation: felt Returns the current admin. -Parameters: - -None. +Parameters: None. Returns: @@ -235,9 +225,7 @@ Parameters: new_admin: felt ---- -Returns: - -None. +Returns: None. ==== `_set_implementation_hash` @@ -251,9 +239,7 @@ Parameters: new_implementation: felt ---- -Returns: - -None. +Returns: None. === Events From 73c808670f21c50a6bd47f4d90fd2b9c5215b68f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Mon, 25 Jul 2022 16:53:05 -0300 Subject: [PATCH 29/39] adapt access accounts erc20 to new dir structure --- docs/modules/ROOT/pages/access.adoc | 14 +++++++------- docs/modules/ROOT/pages/accounts.adoc | 11 +++++++---- docs/modules/ROOT/pages/erc20.adoc | 27 +++++++++++++-------------- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/docs/modules/ROOT/pages/access.adoc b/docs/modules/ROOT/pages/access.adoc index c371fd37a..84c76ad8f 100644 --- a/docs/modules/ROOT/pages/access.adoc +++ b/docs/modules/ROOT/pages/access.adoc @@ -1,4 +1,4 @@ -:ownable-cairo: link:https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/access/ownable.cairo[Ownable] +:ownable-cairo: link:https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/access/ownable/library.cairo[Ownable] :extensibility-pattern: xref:extensibility.adoc#the_pattern @@ -37,7 +37,7 @@ The implementing contract's constructor should set the initial owner by passing [,cairo] ---- -from openzeppelin.access.ownable import Ownable +from openzeppelin.access.ownable.library import Ownable @constructor func constructor{ @@ -54,7 +54,7 @@ To restrict a function's access to the owner only, add in the `assert_only_owner [,cairo] ---- -from openzeppelin.access.ownable import Ownable +from openzeppelin.access.ownable.library import Ownable func protected_function{ syscall_ptr : felt*, @@ -208,13 +208,13 @@ Most software uses access control systems that are role-based: some users are re For each role that you want to define, you will create a new _role identifier_ that is used to grant, revoke, and check if an account has that role (see <> for information on creating identifiers). -Here's a simple example of implementing `AccessControl` on a portion of an link:https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20.cairo[ERC20 token contract] which defines and sets the 'minter' role: +Here's a simple example of implementing `AccessControl` on a portion of an link:https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20.cairo[ERC20 token contract] which defines and sets the 'minter' role: [,cairo] ---- from openzeppelin.token.erc20.library import ERC20 -from openzeppelin.access.accesscontrol import AccessControl +from openzeppelin.access.accesscontrol.library import AccessControl const MINTER_ROLE = 0x4f96f87f6963bb246f2c30526628466840c642dc5c50d5a67777c6cc0e44ab5 @@ -259,7 +259,7 @@ Let's augment our ERC20 token example by also defining a 'burner' role, which le ---- from openzeppelin.token.erc20.library import ERC20 -from openzeppelin.access.accesscontrol import AccessControl +from openzeppelin.access.accesscontrol.library import AccessControl const MINTER_ROLE = 0x4f96f87f6963bb246f2c30526628466840c642dc5c50d5a67777c6cc0e44ab5 @@ -335,7 +335,7 @@ Let's take a look at the ERC20 token example, this time taking advantage of the ---- from openzeppelin.token.erc20.library import ERC20 -from openzeppelin.access.accesscontrol import AccessControl +from openzeppelin.access.accesscontrol.library import AccessControl from openzeppelin.utils.constants import DEFAULT_ADMIN_ROLE diff --git a/docs/modules/ROOT/pages/accounts.adoc b/docs/modules/ROOT/pages/accounts.adoc index 30b218281..085ef281d 100644 --- a/docs/modules/ROOT/pages/accounts.adoc +++ b/docs/modules/ROOT/pages/accounts.adoc @@ -52,11 +52,14 @@ In Python, this would look as follows: ---- from starkware.starknet.testing.starknet import Starknet signer = MockSigner(123456789987654321) +from utils import get_contract_class +from signers import MockSigner + starknet = await Starknet.empty() # 1. Deploy Account account = await starknet.deploy( - "contracts/Account.cairo", + get_contract_class("Account"), constructor_calldata=[signer.public_key] ) @@ -505,11 +508,11 @@ Each preset differs on the signature type being used by the Account. === Account -The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/account/Account.cairo[`Account`] preset uses StarkNet keys to validate transactions. +The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/account/presets/Account.cairo[`Account`] preset uses StarkNet keys to validate transactions. === Eth Account -The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/account/EthAccount.cairo[`EthAccount`] preset supports Ethereum addresses, validating transactions with secp256k1 keys. +The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/account/presets/EthAccount.cairo[`EthAccount`] preset supports Ethereum addresses, validating transactions with secp256k1 keys. == Account differentiation with ERC165 @@ -534,7 +537,7 @@ To implement alternative `execute` functions, make sure to check their correspon Do not expose `_unsafe_execute` directly. IMPORTANT: The `ecdsa_ptr` implicit argument should be included in new methods that invoke `_unsafe_execute` (even if the `ecdsa_ptr` is not being used). -Otherwise, it's possible that an account's functionalty can work in both the testing and local devnet environments; +Otherwise, it's possible that an account's functionality can work in both the testing and local devnet environments; however, it could fail on public networks on account of the https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/cairo/lang/builtins/signature/signature_builtin_runner.py[SignatureBuiltinRunner]. See https://github.com/OpenZeppelin/cairo-contracts/issues/386[issue #386] for more information. diff --git a/docs/modules/ROOT/pages/erc20.adoc b/docs/modules/ROOT/pages/erc20.adoc index 781cd33d0..0734a3ac4 100644 --- a/docs/modules/ROOT/pages/erc20.adoc +++ b/docs/modules/ROOT/pages/erc20.adoc @@ -11,9 +11,9 @@ The `ERC20.cairo` contract implements an approximation of https://eips.ethereum. * <> * <> ** <> - ** <> - ** <> - ** <> + ** <> + ** <> + ** <> * <> ** <> *** <> @@ -104,7 +104,7 @@ To create a token you need to deploy it like this: [,python] ---- erc20 = await starknet.deploy( - "contracts/token/ERC20.cairo", + "openzeppelin/token/erc20/presets/ERC20.cairo", constructor_calldata=[ str_to_felt("Token"), # name str_to_felt("TKN"), # symbol @@ -137,8 +137,7 @@ await signer.send_transaction(account, erc20.contract_address, 'transfer', [reci ERC20 contracts can be extended by following the xref:extensibility.adoc#the_pattern[extensibility pattern]. The basic idea behind integrating the pattern is to import the requisite ERC20 methods from the ERC20 library and incorporate the extended logic thereafter. For example, let's say you wanted to implement a pausing mechanism. -The contract should first import the ERC20 methods and the extended logic from the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/security/pausable.cairo[pausable library] i.e. -`Pausable_pause`, `Pausable_unpause`. +The contract should first import the ERC20 methods and the extended logic from the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/security/pausable/library.cairo[Pausable library] i.e. `Pausable.pause`, `Pausable.unpause`. Next, the contract should expose the methods with the extended logic therein like this: [,python] @@ -150,7 +149,7 @@ func transfer{ range_check_ptr }(recipient: felt, amount: Uint256) -> (success: felt): Pausable.assert_not_paused() # imported extended logic - ERC20_transfer(recipient, amount) # imported library method + ERC20.transfer(recipient, amount) # imported library method return (TRUE) end ---- @@ -173,21 +172,21 @@ Each preset mints an initial supply which is especially necessary for presets th === ERC20 (basic) -The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20.cairo[`ERC20`] preset offers a quick and easy setup for deploying a basic ERC20 token. +The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20.cairo[`ERC20`] preset offers a quick and easy setup for deploying a basic ERC20 token. -=== ERC20_Mintable +=== ERC20Mintable -The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20_Mintable.cairo[`ERC20_Mintable`] preset allows the contract owner to mint new tokens. +The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20Mintable.cairo[`ERC20Mintable`] preset allows the contract owner to mint new tokens. -=== ERC20_Pausable +=== ERC20Pausable -The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20_Pausable.cairo[`ERC20_Pausable`] preset allows the contract owner to pause/unpause all state-modifying methods i.e. +The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20Pausable.cairo[`ERC20Pausable`] preset allows the contract owner to pause/unpause all state-modifying methods i.e. `transfer`, `approve`, etc. This preset proves useful for scenarios such as preventing trades until the end of an evaluation period and having an emergency switch for freezing all token transfers in the event of a large bug. -=== ERC20_Upgradeable +=== ERC20Upgradeable -The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20_Upgradeable.cairo[`ERC20_Upgradeable`] preset allows the contract owner to upgrade a contract by deploying a new ERC20 implementation contract while also maintaing the contract's state. +The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20Upgradeable.cairo[`ERC20Upgradeable`] preset allows the contract owner to upgrade a contract by deploying a new ERC20 implementation contract while also maintaing the contract's state. This preset proves useful for scenarios such as eliminating bugs and adding new features. For more on upgradeability, see xref:proxies.adoc#contract_upgrades[Contract upgrades]. From 5f0d64c5642a40dc57beeb66841feba82e0619b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Mon, 25 Jul 2022 17:07:00 -0300 Subject: [PATCH 30/39] adapt erc721 to new dir structure --- docs/modules/ROOT/pages/erc721.adoc | 94 ++++++++++++++--------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index f6c5b878c..2aaf7b015 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -11,19 +11,19 @@ The `ERC721.cairo` contract implements an approximation of https://eips.ethereum ** <> ** <> ** <> - *** <> + *** <> ** <> ** <> * <> * <> - ** <> - ** <> - ** <> - *** <> - ** <> - *** <> + ** <> + ** <> + ** <> + *** <> + ** <> + *** <> * <> - ** <> + ** <> * <> ** <> *** <> @@ -38,15 +38,15 @@ The `ERC721.cairo` contract implements an approximation of https://eips.ethereum *** <> *** <> *** <> - ** <> + ** <> *** <> *** <> *** <> - ** <> + ** <> *** <> *** <> *** <> - ** <> + ** <> *** <> == IERC721 @@ -120,18 +120,18 @@ In Cairo, arrays are expressed with the array length preceding the actual array; hence, the method accepts `data_len` and `data` respectively as types `felt` and `felt*` * `ERC165.register_interface` allows contracts to set and communicate which interfaces they support. This follows OpenZeppelin's https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/introspection/ERC165Storage.sol[ERC165Storage] -* `IERC721_Receiver` compliant contracts (`ERC721_Holder`) return a hardcoded selector id according to EVM selectors, since selectors are calculated differently in Cairo. +* `IERC721Receiver` compliant contracts (`ERC721Holder`) return a hardcoded selector id according to EVM selectors, since selectors are calculated differently in Cairo. This is in line with the ERC165 interfaces design choice towards EVM compatibility. See the xref:introspection.adoc[Introspection docs] for more info -* `IERC721_Receiver` compliant contracts (`ERC721_Holder`) must support ERC165 by registering the `IERC721_Receiver` selector id in its constructor and exposing the `supportsInterface` method. +* `IERC721Receiver` compliant contracts (`ERC721Holder`) must support ERC165 by registering the `IERC721Receiver` selector id in its constructor and exposing the `supportsInterface` method. In doing so, recipient contracts (both accounts and non-accounts) can be verified that they support ERC721 transfers -* `ERC721_Enumerable` tracks the total number of tokens with the `all_tokens` and `all_tokens_len` storage variables mimicking the array of the Solidity implementation. +* `ERC721Enumerable` tracks the total number of tokens with the `all_tokens` and `all_tokens_len` storage variables mimicking the array of the Solidity implementation. == Usage Use cases go from artwork, digital collectibles, physical property, and many more. -To show a standard use case, we'll use the `ERC721_Mintable` preset which allows for only the owner to `mint` and `burn` tokens. +To show a standard use case, we'll use the `ERC721Mintable` preset which allows for only the owner to `mint` and `burn` tokens. To create a token you need to first deploy both Account and ERC721 contracts respectively. As most StarkNet contracts, ERC721 expects to be called by another contract and it identifies it through `get_caller_address` (analogous to Solidity's `this.address`). This is why we need an Account contract to interact with it. @@ -157,7 +157,7 @@ account = await starknet.deploy( ) erc721 = await starknet.deploy( - "contracts/token/ERC721_Mintable.cairo", + "contracts/token/erc721/presets/ERC721Mintable.cairo", constructor_calldata=[ str_to_felt("Token"), # name str_to_felt("TKN"), # symbol @@ -240,18 +240,18 @@ StarkNet contracts that support safe transfers, however, must also support xref: `safeTransferFrom` requires a means of differentiating between account and non-account contracts. Currently, StarkNet does not support error handling from the contract level; therefore, the current ERC721 implementation requires that all contracts that support safe ERC721 transfers (both accounts and non-accounts) include the `supportsInterface` method. -Further, `supportsInterface` should return `TRUE` if the recipient contract supports the `IERC721_Receiver` magic value `0x150b7a02` (which invokes `onERC721Received`). +Further, `supportsInterface` should return `TRUE` if the recipient contract supports the `IERC721Receiver` magic value `0x150b7a02` (which invokes `onERC721Received`). If the recipient contract supports the `IAccount` magic value `0x50b70dcb`, `supportsInterface` should return `TRUE`. Otherwise, `safeTransferFrom` should fail. -==== IERC721_Receiver +==== IERC721Receiver Interface for any contract that wants to support safeTransfers from ERC721 asset contracts. [,cairo] ---- @contract_interface -namespace IERC721_Receiver: +namespace IERC721Receiver: func onERC721Received( operator: felt, from_: felt, @@ -278,9 +278,9 @@ ERC721 presets have been created to allow for quick deployments as-is. To be as explicit as possible, each preset includes the additional features they offer in the contract name. For example: -* `ERC721_Mintable_Burnable` includes `mint` and `burn` -* `ERC721_Mintable_Pausable` includes `mint`, `pause`, and `unpause` -* `ERC721_Enumerable_Mintable_Burnable` includes `mint`, `burn`, and <> methods +* `ERC721MintableBurnable` includes `mint` and `burn` +* `ERC721MintablePausable` includes `mint`, `pause`, and `unpause` +* `ERC721EnumerableMintableBurnable` includes `mint`, `burn`, and <> methods Ready-to-use presets are a great option for testing and prototyping. See <>. @@ -320,41 +320,41 @@ end The following contract presets are ready to deploy and can be used as-is for quick prototyping and testing. Each preset includes a contract owner, which is set in the `constructor`, to offer simple access control on sensitive methods such as `mint` and `burn`. -=== ERC721_Mintable_Burnable +=== ERC721MintableBurnable -The `ERC721_Mintable_Burnable` preset offers a quick and easy setup for creating NFTs. +The `ERC721MintableBurnable` preset offers a quick and easy setup for creating NFTs. The contract owner can create tokens with `mint`, whereas token owners can destroy their tokens with `burn`. -=== ERC721_Mintable_Pausable +=== ERC721MintablePausable -The `ERC721_Mintable_Pausable` preset creates a contract with pausable token transfers and minting capabilities. +The `ERC721MintablePausable` preset creates a contract with pausable token transfers and minting capabilities. This preset proves useful for scenarios such as preventing trades until the end of an evaluation period and having an emergency switch for freezing all token transfers in the event of a large bug. In this preset, only the contract owner can `mint`, `pause`, and `unpause`. -=== ERC721_Enumerable_Mintable_Burnable +=== ERC721EnumerableMintableBurnable -The `ERC721_Enumerable_Mintable_Burnable` preset adds enumerability of all the token ids in the contract as well as all token ids owned by each account. +The `ERC721EnumerableMintableBurnable` preset adds enumerability of all the token ids in the contract as well as all token ids owned by each account. This allows contracts to publish its full list of NFTs and make them discoverable. In regard to implementation, contracts should expose the following view methods: -* `ERC721_Enumerable.total_supply` -* `ERC721_Enumerable.token_by_index` -* `ERC721_Enumerable.token_of_owner_by_index` +* `ERC721Enumerable.total_supply` +* `ERC721Enumerable.token_by_index` +* `ERC721Enumerable.token_of_owner_by_index` In order for the tokens to be correctly indexed, the contract should also use the following methods (which supercede some of the base `ERC721` methods): -* `ERC721_Enumerable.transfer_from` -* `ERC721_Enumerable.safe_transfer_from` -* `ERC721_Enumerable._mint` -* `ERC721_Enumerable._burn` +* `ERC721Enumerable.transfer_from` +* `ERC721Enumerable.safe_transfer_from` +* `ERC721Enumerable._mint` +* `ERC721Enumerable._burn` -==== IERC721_Enumerable +==== IERC721Enumerable [,cairo] ---- @contract_interface -namespace IERC721_Enumerable: +namespace IERC721Enumerable: func totalSupply() -> (totalSupply: Uint256): end @@ -366,20 +366,20 @@ namespace IERC721_Enumerable: end ---- -=== ERC721_Metadata +=== ERC721Metadata -The `ERC721_Metadata` extension allows your smart contract to be interrogated for its name and for details about the assets which your NFTs represent. +The `ERC721Metadata` extension allows your smart contract to be interrogated for its name and for details about the assets which your NFTs represent. We follow OpenZeppelin's Solidity approach of integrating the Metadata methods `name`, `symbol`, and `tokenURI` into all ERC721 implementations. -If preferred, a contract can be created that does not import the Metadata methods from the `ERC721_base` library. -Note that the `IERC721_Metadata` interface id should be removed from the constructor as well. +If preferred, a contract can be created that does not import the Metadata methods from the `ERC721` library. +Note that the `IERC721Metadata` interface id should be removed from the constructor as well. -==== IERC721_Metadata +==== IERC721Metadata [,cairo] ---- @contract_interface -namespace IERC721_Metadata: +namespace IERC721Metadata: func name() -> (name: felt): end @@ -393,7 +393,7 @@ end == Utilities -=== ERC721_Holder +=== ERC721Holder Implementation of the `IERC721Receiver` interface. @@ -627,7 +627,7 @@ tokenId: Uint256 ''' -=== IERC721_Metadata API +=== IERC721Metadata API [,cairo] ---- @@ -688,7 +688,7 @@ tokenURI: felt ''' -=== IERC721_Enumerable API +=== IERC721Enumerable API [,cairo] ---- @@ -757,7 +757,7 @@ tokenId: Uint256 ''' -=== IERC721_Receiver API +=== IERC721Receiver API [,cairo] ---- From 581f9790a232ec57145b712452fb0ee0ef78dcb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Mon, 25 Jul 2022 17:11:33 -0300 Subject: [PATCH 31/39] adapt extensibility and introspection to new dir structure --- docs/modules/ROOT/pages/extensibility.adoc | 16 ++++++++-------- docs/modules/ROOT/pages/introspection.adoc | 12 ++++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/modules/ROOT/pages/extensibility.adoc b/docs/modules/ROOT/pages/extensibility.adoc index 1a3155410..b342a83b3 100644 --- a/docs/modules/ROOT/pages/extensibility.adoc +++ b/docs/modules/ROOT/pages/extensibility.adoc @@ -57,7 +57,7 @@ Then: * Must not prefix `external` functions with an underscore (e.g. `ERC20.transfer`) * Must prefix `storage` functions with the name of the namespace to prevent clashing with other libraries (e.g. -`ERC20_balances`) +`ERC20balances`) * Must not implement any `@external`, `@view`, or `@constructor` functions * Can implement initializers (never as `@constructor` or `@external`) * Must not call initializers on any function @@ -81,13 +81,13 @@ Some presets are: * https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/account/Account.cairo[Account] * https://github.com/OpenZeppelin/cairo-contracts/blob/main/tests/mocks/ERC165.cairo[ERC165] -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20_Mintable.cairo[ERC20_Mintable] -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20_Pausable.cairo[ERC20_Pausable] -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20_Upgradeable.cairo[ERC20_Upgradeable] -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20.cairo[ERC20] -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc721/ERC721_Mintable_Burnable.cairo[ERC721_Mintable_Burnable] -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc721/ERC721_Mintable_Pausable.cairo[ERC721_Mintable_Pausable] -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc721_enumerable/ERC721_Enumerable_Mintable_Burnable.cairo[ERC721_Enumerable_Mintable_Burnable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20Mintable.cairo[ERC20Mintable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20Pausable.cairo[ERC20Pausable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20Upgradeable.cairo[ERC20Upgradeable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20.cairo[ERC20] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc721/presets/ERC721MintableBurnable.cairo[ERC721MintableBurnable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc721/presets/ERC721MintablePausable.cairo[ERC721MintablePausable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc721/enumerable/presets/ERC721EnumerableMintableBurnable.cairo[ERC721EnumerableMintableBurnable] == Function names and coding style diff --git a/docs/modules/ROOT/pages/introspection.adoc b/docs/modules/ROOT/pages/introspection.adoc index d9ace3312..2cbd571a4 100644 --- a/docs/modules/ROOT/pages/introspection.adoc +++ b/docs/modules/ROOT/pages/introspection.adoc @@ -27,7 +27,7 @@ There may even not be any direct calls to them! ERC20 tokens may be sent to a contract that lacks a way to transfer them out of it, locking them forever). In these cases, a contract declaring its interface can be very helpful in preventing errors. -It should be noted that the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/utils/constants.cairo[constants library] includes constant variables referencing all of the interface ids used in these contracts. +It should be noted that the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/utils/constants/library.cairo[constants library] includes constant variables referencing all of the interface ids used in these contracts. This allows for more legible code i.e. using `IERC165_ID` instead of `0x01ffc9a7`. @@ -45,14 +45,14 @@ It's recommended to register interface support upon contract deployment through [,cairo] ---- -from openzeppelin.introspection.ERC165 import ERC165 +from openzeppelin.introspection.erc165.library import ERC165 INTERFACE_ID = 0x12345678 @constructor func constructor{ - syscall_ptr : felt*, - pedersen_ptr : HashBuiltin*, + syscall_ptr: felt*, + pedersen_ptr: HashBuiltin*, range_check_ptr }(): ERC165.register_interface(INTERFACE_ID) @@ -67,7 +67,7 @@ Here's an example: [,cairo] ---- -from openzeppelin.introspection.IERC165 import IERC165 +from openzeppelin.introspection.erc265.IERC165 import IERC165 INTERFACE_ID = 0x12345678 @@ -84,7 +84,7 @@ end ---- NOTE: `supportsInterface` is camelCased because it is an exposed contract method as part of ERC165's interface. -This differs from library methods (such as `supports_interface` from the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/introspection/ERC165.cairo[ERC165 library]) which are snake_cased and not exposed. +This differs from library methods (such as `supports_interface` from the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/introspection/erc165/library.cairo[ERC165 library]) which are snake_cased and not exposed. See the xref:extensibility.adoc#function_names_and_coding_style[Function names and coding style] for more details. === IERC165 From fdee3f5b43a197e75fb2016b4c878b702179ffc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Mon, 25 Jul 2022 17:34:41 -0300 Subject: [PATCH 32/39] adapt proxies security utilities to new dir structure --- docs/modules/ROOT/pages/proxies.adoc | 10 +++++----- docs/modules/ROOT/pages/security.adoc | 16 ++++++++-------- docs/modules/ROOT/pages/utilities.adoc | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/modules/ROOT/pages/proxies.adoc b/docs/modules/ROOT/pages/proxies.adoc index a2cc4c901..4ca1fdb8f 100644 --- a/docs/modules/ROOT/pages/proxies.adoc +++ b/docs/modules/ROOT/pages/proxies.adoc @@ -97,7 +97,7 @@ This allows developers to add features, update logic, and fix bugs without touch === Proxy contract -The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/upgrades/Proxy.cairo[Proxy contract] includes two core methods: +The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/upgrades/presets/Proxy.cairo[Proxy contract] includes two core methods: . The `\\__default__` method is a fallback method that redirects a function call and associated calldata to the implementation contract. . The `\\__l1_default__` method is also a fallback method; @@ -140,7 +140,7 @@ It's up to the developers to protect their implementation contract's upgradeabil For a full implementation contract example, please see: -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/tests/mocks/proxiable_implementation.cairo[Proxiable implementation] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/tests/mocks/ProxiableImplementation.cairo[Proxiable implementation] == Upgrades library API @@ -311,8 +311,8 @@ To upgrade a contract, the implementation contract should include an `upgrade` m For a full deployment and upgrade implementation, please see: -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/tests/mocks/upgrades_v1_mock.cairo[Upgrades V1] -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/tests/mocks/upgrades_v2_mock.cairo[Upgrades V2] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/tests/mocks/UpgradesMockV1.cairo[Upgrades V1] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/tests/mocks/UpgradesMockV2.cairo[Upgrades V2] === Declaring contracts @@ -348,7 +348,7 @@ They can be deployed as-is or used as templates for customization. Some presets include: -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20_Upgradeable.cairo[ERC20_Upgradeable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20Upgradeable.cairo[ERC20Upgradeable] * more to come! have an idea? https://github.com/OpenZeppelin/cairo-contracts/issues/new/choose[open an issue]! diff --git a/docs/modules/ROOT/pages/security.adoc b/docs/modules/ROOT/pages/security.adoc index 991f644cc..8a386bbe8 100644 --- a/docs/modules/ROOT/pages/security.adoc +++ b/docs/modules/ROOT/pages/security.adoc @@ -21,7 +21,7 @@ The recommended pattern with Initializable is to include a check that the Initia [,cairo] ---- -from openzeppelin.security.initializable import Initializable +from openzeppelin.security.initializable.library import Initializable @external func foo{ @@ -51,7 +51,7 @@ For example: [,cairo] ---- -from openzeppelin.security.pausable import Pausable +from openzeppelin.security.pausable.library import Pausable @external func whenNotPaused{ @@ -83,19 +83,19 @@ In other words, `pause` cannot be invoked when already paused and vice versa. For a list of full implementations utilizing the Pausable library, see: -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/ERC20_Pausable.cairo[ERC20_Pausable] -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc721/ERC721_Mintable_Pausable.cairo[ERC721_Mintable_Pausable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20Pausable.cairo[ERC20Pausable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc721/presets/ERC721MintablePausable.cairo[ERC721MintablePausable] == Reentrancy Guard A https://gus-tavo-guim.medium.com/reentrancy-attack-on-smart-contracts-how-to-identify-the-exploitable-and-an-example-of-an-attack-4470a2d8dfe4[reentrancy attack] occurs when the caller is able to obtain more resources than allowed by recursively calling a target's function. -Since Cairo does not support modifiers like Solidity, the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/security/reentrancyguard.cairo[`reentrancyguard`] library exposes two methods `_start` and `_end` to protect functions against reentrancy attacks. +Since Cairo does not support modifiers like Solidity, the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/security/reentrancyguard/library.cairo[`reentrancy guard`] library exposes two methods `_start` and `_end` to protect functions against reentrancy attacks. The protected function must call `ReentrancyGuard._start` before the first function statement, and `ReentrancyGuard._end` before the return statement, as shown below: [,cairo] ---- -from openzeppelin.security.reentrancyguard import ReentrancyGuard +from openzeppelin.security.reentrancyguard.library import ReentrancyGuard func test_function{ syscall_ptr : felt*, @@ -113,7 +113,7 @@ end === SafeUint256 -The SafeUint256 namespace in the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/security/safemath.cairo[SafeMath library] offers arithmetic for unsigned 256-bit integers (uint256) by leveraging Cairo's Uint256 library and integrating overflow checks. +The SafeUint256 namespace in the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/security/safemath/library.cairo[SafeMath library] offers arithmetic for unsigned 256-bit integers (uint256) by leveraging Cairo's Uint256 library and integrating overflow checks. Some of Cairo's Uint256 functions do not revert upon overflows. For instance, `uint256_add` will return a bit carry when the sum exceeds 256 bits. This library includes an additional assertion ensuring values do not overflow. @@ -123,7 +123,7 @@ Simply import SafeUint256 and insert the arithmetic method like this: [,cairo] ---- -from openzeppelin.security.safemath import SafeUint256 +from openzeppelin.security.safemath.library import SafeUint256 func add_two_uints{ syscall_ptr: felt*, diff --git a/docs/modules/ROOT/pages/utilities.adoc b/docs/modules/ROOT/pages/utilities.adoc index 835d055fb..1914dc809 100644 --- a/docs/modules/ROOT/pages/utilities.adoc +++ b/docs/modules/ROOT/pages/utilities.adoc @@ -27,7 +27,7 @@ CAUTION: Expect this module to evolve (as it has already done). == Constants -To ease the readability of Cairo contracts, this project includes reusable https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/utils/constants.cairo[constant variables] like `UINT8_MAX`, or EIP165 interface IDs such as `IERC165_ID` or `IERC721_ID`. +To ease the readability of Cairo contracts, this project includes reusable https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/utils/constants/library.cairo[constant variables] like `UINT8_MAX`, or EIP165 interface IDs such as `IERC165_ID` or `IERC721_ID`. For more information on how interface ids are calculated, see the xref:introspection.adoc#interface_calculations[ERC165 documentation]. == Strings From 00cf948f8f9141e4c64f74a926bab300dc8a50ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Mon, 25 Jul 2022 17:37:37 -0300 Subject: [PATCH 33/39] fix merge conflict --- docs/modules/ROOT/pages/utilities.adoc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/modules/ROOT/pages/utilities.adoc b/docs/modules/ROOT/pages/utilities.adoc index a6dcccc11..1914dc809 100644 --- a/docs/modules/ROOT/pages/utilities.adoc +++ b/docs/modules/ROOT/pages/utilities.adoc @@ -27,12 +27,8 @@ CAUTION: Expect this module to evolve (as it has already done). == Constants -<<<<<<< HEAD:docs/modules/ROOT/pages/utilities.adoc To ease the readability of Cairo contracts, this project includes reusable https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/utils/constants/library.cairo[constant variables] like `UINT8_MAX`, or EIP165 interface IDs such as `IERC165_ID` or `IERC721_ID`. For more information on how interface ids are calculated, see the xref:introspection.adoc#interface_calculations[ERC165 documentation]. -======= -To ease the readability of Cairo contracts, this project includes reusable [constants variables](../src/openzeppelin/utils/constants/library.cairo) like `UINT8_MAX`, or EIP165 interface IDs such as `IERC165_ID` or `IERC721_ID`. For more information on how interface ids are calculated, see the [ERC165 documentation](../docs/Introspection.md#interface-calculations). ->>>>>>> main:docs/Utilities.md == Strings From 6e06c79ee86cef7a5606bc04eec7ccd612b41eea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Tue, 26 Jul 2022 16:41:06 -0300 Subject: [PATCH 34/39] apply review suggestions --- RELEASING.md | 4 ++-- docs/antora.yml | 2 +- docs/modules/ROOT/pages/accounts.adoc | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 5cb320e5d..7660e8b7f 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -23,8 +23,8 @@ to ```diff name: cairo-contracts title: Contracts for Cairo --version: 0.1 -+version: 0.2 +-version: 0.1.0 ++version: 0.2.0 (...) ``` diff --git a/docs/antora.yml b/docs/antora.yml index bd09b8d1e..5c9b16126 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -1,5 +1,5 @@ name: contracts-cairo title: Contracts for Cairo -version: 0.2 +version: 0.2.1 nav: - modules/ROOT/nav.adoc diff --git a/docs/modules/ROOT/pages/accounts.adoc b/docs/modules/ROOT/pages/accounts.adoc index 085ef281d..76a8e332f 100644 --- a/docs/modules/ROOT/pages/accounts.adoc +++ b/docs/modules/ROOT/pages/accounts.adoc @@ -111,8 +111,6 @@ While the interface is agnostic of signature validation schemes, this implementa That's why the `constructor` function expects a `public_key` parameter to set it. Since there's also a `set_public_key()` method, accounts can be effectively transferred. -Note that although the current implementation works only with StarkKeys, support for Ethereum's ECDSA algorithm will be added in the future. - === Signer The signer is responsible for creating a transaction signature with the user's private key for a given transaction. From 62a854d28dfebf0a9a6c79b43e3ef613f0e39f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Tue, 26 Jul 2022 21:43:02 +0200 Subject: [PATCH 35/39] Update docs/modules/ROOT/pages/accounts.adoc Co-authored-by: Andrew Fleming --- docs/modules/ROOT/pages/accounts.adoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/accounts.adoc b/docs/modules/ROOT/pages/accounts.adoc index 76a8e332f..40108def2 100644 --- a/docs/modules/ROOT/pages/accounts.adoc +++ b/docs/modules/ROOT/pages/accounts.adoc @@ -51,10 +51,11 @@ In Python, this would look as follows: [,python] ---- from starkware.starknet.testing.starknet import Starknet -signer = MockSigner(123456789987654321) from utils import get_contract_class from signers import MockSigner +signer = MockSigner(123456789987654321) + starknet = await Starknet.empty() # 1. Deploy Account From 57a097a9293e163ff9697df3418e537e68c59f1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Fri, 29 Jul 2022 13:49:17 -0300 Subject: [PATCH 36/39] fix formatting --- docs/modules/ROOT/pages/accounts.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/accounts.adoc b/docs/modules/ROOT/pages/accounts.adoc index 40108def2..2673765a6 100644 --- a/docs/modules/ROOT/pages/accounts.adoc +++ b/docs/modules/ROOT/pages/accounts.adoc @@ -309,7 +309,7 @@ First, the user sends the messages for the transaction through a Signer instanti ) ---- -Then the `_from_call_to_call_array` method in {test-utils}[utils.py] converts each call into the `AccountCallArray` format and cumulatively stores the calldata of every call into a single array. +Then the `\_from_call_to_call_array` method in {test-utils}[utils.py] converts each call into the `AccountCallArray` format and cumulatively stores the calldata of every call into a single array. Next, both arrays (as well as the `sender`, `nonce`, and `max_fee`) are used to create the transaction hash. The Signer then invokes `\\__execute__` with the signature and passes `AccountCallArray`, calldata, and nonce as arguments. From 517058f4cabf4409f95e00402a664c95d182ca5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Fri, 29 Jul 2022 13:52:19 -0300 Subject: [PATCH 37/39] escape character --- docs/modules/ROOT/pages/accounts.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/accounts.adoc b/docs/modules/ROOT/pages/accounts.adoc index 2673765a6..02a19cc07 100644 --- a/docs/modules/ROOT/pages/accounts.adoc +++ b/docs/modules/ROOT/pages/accounts.adoc @@ -311,7 +311,7 @@ First, the user sends the messages for the transaction through a Signer instanti Then the `\_from_call_to_call_array` method in {test-utils}[utils.py] converts each call into the `AccountCallArray` format and cumulatively stores the calldata of every call into a single array. Next, both arrays (as well as the `sender`, `nonce`, and `max_fee`) are used to create the transaction hash. -The Signer then invokes `\\__execute__` with the signature and passes `AccountCallArray`, calldata, and nonce as arguments. +The Signer then invokes `\__execute__` with the signature and passes `AccountCallArray`, calldata, and nonce as arguments. Finally, the `\\__execute__` method takes the `AccountCallArray` and calldata and builds an array of ``Call``s (MultiCall). From 0cd453b1fa84a3102a8294ccd7a5af3d4baad967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Wed, 3 Aug 2022 19:13:51 +0200 Subject: [PATCH 38/39] Update docs/modules/ROOT/pages/index.adoc Co-authored-by: Francisco --- docs/modules/ROOT/pages/index.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index 25efb6aac..c228ee2fd 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -16,7 +16,7 @@ sudo apt install -y libgmp3-dev # linux brew install gmp # mac ---- -TIP: If you have any troubles installing gmp on your Apple M1 computer, https://github.com/OpenZeppelin/nile/issues/22[here's a list of potential solutions]. +TIP: If you have any trouble installing gmp on your Apple M1 computer, https://github.com/OpenZeppelin/nile/issues/22[here's a list of potential solutions]. === Set up your project From 10a0c3af38c938dfc322df904b82b89d35c65654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Wed, 3 Aug 2022 14:33:14 -0300 Subject: [PATCH 39/39] replace links with permalinks --- docs/modules/ROOT/pages/access.adoc | 4 ++-- docs/modules/ROOT/pages/accounts.adoc | 6 +++--- docs/modules/ROOT/pages/erc20.adoc | 10 +++++----- docs/modules/ROOT/pages/extensibility.adoc | 16 ++++++++-------- docs/modules/ROOT/pages/introspection.adoc | 4 ++-- docs/modules/ROOT/pages/proxies.adoc | 6 +++--- docs/modules/ROOT/pages/security.adoc | 8 ++++---- docs/modules/ROOT/pages/utilities.adoc | 2 +- 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/docs/modules/ROOT/pages/access.adoc b/docs/modules/ROOT/pages/access.adoc index 84c76ad8f..3f4e5c80b 100644 --- a/docs/modules/ROOT/pages/access.adoc +++ b/docs/modules/ROOT/pages/access.adoc @@ -1,4 +1,4 @@ -:ownable-cairo: link:https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/access/ownable/library.cairo[Ownable] +:ownable-cairo: link:https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/access/ownable/library.cairo[Ownable] :extensibility-pattern: xref:extensibility.adoc#the_pattern @@ -208,7 +208,7 @@ Most software uses access control systems that are role-based: some users are re For each role that you want to define, you will create a new _role identifier_ that is used to grant, revoke, and check if an account has that role (see <> for information on creating identifiers). -Here's a simple example of implementing `AccessControl` on a portion of an link:https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20.cairo[ERC20 token contract] which defines and sets the 'minter' role: +Here's a simple example of implementing `AccessControl` on a portion of an link:https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/token/erc20/presets/ERC20.cairo[ERC20 token contract] which defines and sets the 'minter' role: [,cairo] ---- diff --git a/docs/modules/ROOT/pages/accounts.adoc b/docs/modules/ROOT/pages/accounts.adoc index 02a19cc07..a61011eb8 100644 --- a/docs/modules/ROOT/pages/accounts.adoc +++ b/docs/modules/ROOT/pages/accounts.adoc @@ -70,7 +70,7 @@ await signer.send_transaction(account, some_contract_address, 'some_function', [ == Standard Interface -The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/account/IAccount.cairo[`IAccount.cairo`] contract interface contains the standard account interface proposed in https://github.com/OpenZeppelin/cairo-contracts/discussions/41[#41] and adopted by OpenZeppelin and Argent. +The https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/account/IAccount.cairo[`IAccount.cairo`] contract interface contains the standard account interface proposed in https://github.com/OpenZeppelin/cairo-contracts/discussions/41[#41] and adopted by OpenZeppelin and Argent. It implements https://eips.ethereum.org/EIPS/eip-1271[EIP-1271] and it is agnostic of signature validation and nonce management strategies. [,cairo] @@ -507,11 +507,11 @@ Each preset differs on the signature type being used by the Account. === Account -The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/account/presets/Account.cairo[`Account`] preset uses StarkNet keys to validate transactions. +The https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/account/presets/Account.cairo[`Account`] preset uses StarkNet keys to validate transactions. === Eth Account -The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/account/presets/EthAccount.cairo[`EthAccount`] preset supports Ethereum addresses, validating transactions with secp256k1 keys. +The https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/account/presets/EthAccount.cairo[`EthAccount`] preset supports Ethereum addresses, validating transactions with secp256k1 keys. == Account differentiation with ERC165 diff --git a/docs/modules/ROOT/pages/erc20.adoc b/docs/modules/ROOT/pages/erc20.adoc index 0734a3ac4..3c7e45ca2 100644 --- a/docs/modules/ROOT/pages/erc20.adoc +++ b/docs/modules/ROOT/pages/erc20.adoc @@ -137,7 +137,7 @@ await signer.send_transaction(account, erc20.contract_address, 'transfer', [reci ERC20 contracts can be extended by following the xref:extensibility.adoc#the_pattern[extensibility pattern]. The basic idea behind integrating the pattern is to import the requisite ERC20 methods from the ERC20 library and incorporate the extended logic thereafter. For example, let's say you wanted to implement a pausing mechanism. -The contract should first import the ERC20 methods and the extended logic from the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/security/pausable/library.cairo[Pausable library] i.e. `Pausable.pause`, `Pausable.unpause`. +The contract should first import the ERC20 methods and the extended logic from the https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/security/pausable/library.cairo[Pausable library] i.e. `Pausable.pause`, `Pausable.unpause`. Next, the contract should expose the methods with the extended logic therein like this: [,python] @@ -172,21 +172,21 @@ Each preset mints an initial supply which is especially necessary for presets th === ERC20 (basic) -The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20.cairo[`ERC20`] preset offers a quick and easy setup for deploying a basic ERC20 token. +The https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/token/erc20/presets/ERC20.cairo[`ERC20`] preset offers a quick and easy setup for deploying a basic ERC20 token. === ERC20Mintable -The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20Mintable.cairo[`ERC20Mintable`] preset allows the contract owner to mint new tokens. +The https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/token/erc20/presets/ERC20Mintable.cairo[`ERC20Mintable`] preset allows the contract owner to mint new tokens. === ERC20Pausable -The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20Pausable.cairo[`ERC20Pausable`] preset allows the contract owner to pause/unpause all state-modifying methods i.e. +The https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/token/erc20/presets/ERC20Pausable.cairo[`ERC20Pausable`] preset allows the contract owner to pause/unpause all state-modifying methods i.e. `transfer`, `approve`, etc. This preset proves useful for scenarios such as preventing trades until the end of an evaluation period and having an emergency switch for freezing all token transfers in the event of a large bug. === ERC20Upgradeable -The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20Upgradeable.cairo[`ERC20Upgradeable`] preset allows the contract owner to upgrade a contract by deploying a new ERC20 implementation contract while also maintaing the contract's state. +The https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/token/erc20/presets/ERC20Upgradeable.cairo[`ERC20Upgradeable`] preset allows the contract owner to upgrade a contract by deploying a new ERC20 implementation contract while also maintaing the contract's state. This preset proves useful for scenarios such as eliminating bugs and adding new features. For more on upgradeability, see xref:proxies.adoc#contract_upgrades[Contract upgrades]. diff --git a/docs/modules/ROOT/pages/extensibility.adoc b/docs/modules/ROOT/pages/extensibility.adoc index b342a83b3..442a2eebe 100644 --- a/docs/modules/ROOT/pages/extensibility.adoc +++ b/docs/modules/ROOT/pages/extensibility.adoc @@ -79,15 +79,15 @@ They can be deployed as-is or used as templates for customization. Some presets are: -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/account/Account.cairo[Account] +* https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/account/Account.cairo[Account] * https://github.com/OpenZeppelin/cairo-contracts/blob/main/tests/mocks/ERC165.cairo[ERC165] -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20Mintable.cairo[ERC20Mintable] -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20Pausable.cairo[ERC20Pausable] -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20Upgradeable.cairo[ERC20Upgradeable] -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20.cairo[ERC20] -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc721/presets/ERC721MintableBurnable.cairo[ERC721MintableBurnable] -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc721/presets/ERC721MintablePausable.cairo[ERC721MintablePausable] -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc721/enumerable/presets/ERC721EnumerableMintableBurnable.cairo[ERC721EnumerableMintableBurnable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/token/erc20/presets/ERC20Mintable.cairo[ERC20Mintable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/token/erc20/presets/ERC20Pausable.cairo[ERC20Pausable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/token/erc20/presets/ERC20Upgradeable.cairo[ERC20Upgradeable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/token/erc20/presets/ERC20.cairo[ERC20] +* https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/token/erc721/presets/ERC721MintableBurnable.cairo[ERC721MintableBurnable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/token/erc721/presets/ERC721MintablePausable.cairo[ERC721MintablePausable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/token/erc721/enumerable/presets/ERC721EnumerableMintableBurnable.cairo[ERC721EnumerableMintableBurnable] == Function names and coding style diff --git a/docs/modules/ROOT/pages/introspection.adoc b/docs/modules/ROOT/pages/introspection.adoc index 2cbd571a4..a889146a5 100644 --- a/docs/modules/ROOT/pages/introspection.adoc +++ b/docs/modules/ROOT/pages/introspection.adoc @@ -27,7 +27,7 @@ There may even not be any direct calls to them! ERC20 tokens may be sent to a contract that lacks a way to transfer them out of it, locking them forever). In these cases, a contract declaring its interface can be very helpful in preventing errors. -It should be noted that the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/utils/constants/library.cairo[constants library] includes constant variables referencing all of the interface ids used in these contracts. +It should be noted that the https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/utils/constants/library.cairo[constants library] includes constant variables referencing all of the interface ids used in these contracts. This allows for more legible code i.e. using `IERC165_ID` instead of `0x01ffc9a7`. @@ -84,7 +84,7 @@ end ---- NOTE: `supportsInterface` is camelCased because it is an exposed contract method as part of ERC165's interface. -This differs from library methods (such as `supports_interface` from the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/introspection/erc165/library.cairo[ERC165 library]) which are snake_cased and not exposed. +This differs from library methods (such as `supports_interface` from the https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/introspection/erc165/library.cairo[ERC165 library]) which are snake_cased and not exposed. See the xref:extensibility.adoc#function_names_and_coding_style[Function names and coding style] for more details. === IERC165 diff --git a/docs/modules/ROOT/pages/proxies.adoc b/docs/modules/ROOT/pages/proxies.adoc index 4ca1fdb8f..e5cf6ac06 100644 --- a/docs/modules/ROOT/pages/proxies.adoc +++ b/docs/modules/ROOT/pages/proxies.adoc @@ -97,7 +97,7 @@ This allows developers to add features, update logic, and fix bugs without touch === Proxy contract -The https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/upgrades/presets/Proxy.cairo[Proxy contract] includes two core methods: +The https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/upgrades/presets/Proxy.cairo[Proxy contract] includes two core methods: . The `\\__default__` method is a fallback method that redirects a function call and associated calldata to the implementation contract. . The `\\__l1_default__` method is also a fallback method; @@ -114,7 +114,7 @@ The proxy's fallback function redirects the function call to the implementation === Implementation contract The implementation contract, also known as the logic contract, receives the redirected function calls from the proxy contract. -The implementation contract should follow the xref:extensibility.adoc#the_pattern[Extensibility pattern] and import directly from the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/upgrades/library.cairo[Proxy library]. +The implementation contract should follow the xref:extensibility.adoc#the_pattern[Extensibility pattern] and import directly from the https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/upgrades/library.cairo[Proxy library]. The implementation contract should: @@ -348,7 +348,7 @@ They can be deployed as-is or used as templates for customization. Some presets include: -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20Upgradeable.cairo[ERC20Upgradeable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/token/erc20/presets/ERC20Upgradeable.cairo[ERC20Upgradeable] * more to come! have an idea? https://github.com/OpenZeppelin/cairo-contracts/issues/new/choose[open an issue]! diff --git a/docs/modules/ROOT/pages/security.adoc b/docs/modules/ROOT/pages/security.adoc index 8a386bbe8..3f3054d7f 100644 --- a/docs/modules/ROOT/pages/security.adoc +++ b/docs/modules/ROOT/pages/security.adoc @@ -83,14 +83,14 @@ In other words, `pause` cannot be invoked when already paused and vice versa. For a list of full implementations utilizing the Pausable library, see: -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/presets/ERC20Pausable.cairo[ERC20Pausable] -* https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc721/presets/ERC721MintablePausable.cairo[ERC721MintablePausable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/token/erc20/presets/ERC20Pausable.cairo[ERC20Pausable] +* https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/token/erc721/presets/ERC721MintablePausable.cairo[ERC721MintablePausable] == Reentrancy Guard A https://gus-tavo-guim.medium.com/reentrancy-attack-on-smart-contracts-how-to-identify-the-exploitable-and-an-example-of-an-attack-4470a2d8dfe4[reentrancy attack] occurs when the caller is able to obtain more resources than allowed by recursively calling a target's function. -Since Cairo does not support modifiers like Solidity, the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/security/reentrancyguard/library.cairo[`reentrancy guard`] library exposes two methods `_start` and `_end` to protect functions against reentrancy attacks. +Since Cairo does not support modifiers like Solidity, the https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/security/reentrancyguard/library.cairo[`reentrancy guard`] library exposes two methods `_start` and `_end` to protect functions against reentrancy attacks. The protected function must call `ReentrancyGuard._start` before the first function statement, and `ReentrancyGuard._end` before the return statement, as shown below: [,cairo] @@ -113,7 +113,7 @@ end === SafeUint256 -The SafeUint256 namespace in the https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/security/safemath/library.cairo[SafeMath library] offers arithmetic for unsigned 256-bit integers (uint256) by leveraging Cairo's Uint256 library and integrating overflow checks. +The SafeUint256 namespace in the https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/security/safemath/library.cairo[SafeMath library] offers arithmetic for unsigned 256-bit integers (uint256) by leveraging Cairo's Uint256 library and integrating overflow checks. Some of Cairo's Uint256 functions do not revert upon overflows. For instance, `uint256_add` will return a bit carry when the sum exceeds 256 bits. This library includes an additional assertion ensuring values do not overflow. diff --git a/docs/modules/ROOT/pages/utilities.adoc b/docs/modules/ROOT/pages/utilities.adoc index 1914dc809..82f78ffac 100644 --- a/docs/modules/ROOT/pages/utilities.adoc +++ b/docs/modules/ROOT/pages/utilities.adoc @@ -27,7 +27,7 @@ CAUTION: Expect this module to evolve (as it has already done). == Constants -To ease the readability of Cairo contracts, this project includes reusable https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/utils/constants/library.cairo[constant variables] like `UINT8_MAX`, or EIP165 interface IDs such as `IERC165_ID` or `IERC721_ID`. +To ease the readability of Cairo contracts, this project includes reusable https://github.com/OpenZeppelin/cairo-contracts/blob/ad399728e6fcd5956a4ed347fb5e8ee731d37ec4/src/openzeppelin/utils/constants/library.cairo[constant variables] like `UINT8_MAX`, or EIP165 interface IDs such as `IERC165_ID` or `IERC721_ID`. For more information on how interface ids are calculated, see the xref:introspection.adoc#interface_calculations[ERC165 documentation]. == Strings