Skip to content

Commit

Permalink
Refactor directory structure (#350)
Browse files Browse the repository at this point in the history
* add lib and preset dirs

* remove underscore from names, finish init reorganization

* fix mock names, adjusted paths

* update paths in tests

* update attribution

* Rename safemath.cairo to SafeMath.cairo

* Rename initializable.cairo to Initializable.cairo

* Rename pausable.cairo to Pausable.cairo

* update version in attribution

* add conventions to CONTRIBUTING.md

* update names and paths in docs

* fix paths in code snippets

* Apply suggestions from code review

Co-authored-by: Martín Triay <martriay@gmail.com>

* Update CONTRIBUTING.md

Co-authored-by: Martín Triay <martriay@gmail.com>

* update link paths

* update structure

* fix compilation msg and import snippet

* Apply suggestions from code review

* fix paths, move ERC721Holder

* remove upgrades dir

* fix pin

* update paths for proxy

* update links and code block imports

Co-authored-by: Martín Triay <martriay@gmail.com>
  • Loading branch information
andrew-fleming and martriay committed Jul 23, 2022
1 parent 02bb5f9 commit 243df2b
Show file tree
Hide file tree
Showing 72 changed files with 332 additions and 294 deletions.
28 changes: 27 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,33 @@ Before starting development, please [create an issue](https://github.com/OpenZep

Also, consider that snake case is used for Cairo development in general due to its strong Python bias, but in order to be compliant with the standards, EIP implementations must use camelCase. Therefore, EIP-based contracts will have their external functions written in camelCase while the rest of the codebase will be in snake_case.

And make sure to always include tests and documentation for the new developments.
And make sure to always include tests and documentation for the new developments. Please consider the following conventions:

- Naming
- libraries should be named `library.cairo`, e.g. `erc20/library.cairo`
- contracts should be PascalCased i.e. `MyContract.cairo`
- interfaces should be prefixed with an `I`, as in `IAccount.cairo`
- test modules should begin with `test_` followed by the contract name i.e. `test_MyContract.py`

- Structure
- libraries should cede their names to their parent directory and are named `library.cairo` instead
- interfaces should be alongside the library that the interface defines
- preset contracts should be within a `presets` directory of the library to which they are a preset
- Here are example paths:
- `openzeppelin.token.erc20.library`
- `openzeppelin.token.erc20.IERC20`
- `openzeppelin.token.erc20.presets.ERC20Mintable`
- And a visual guide:

```python
openzeppelin
└──token
└── erc20
├── library.cairo
├── IERC20.cairo
└── presets
└── ERC20Mintable.cairo
```

## Creating Pull Requests (PRs)

Expand Down
60 changes: 33 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ nile deploy MyToken <name> <symbol> <decimals> <initial_supply> <recipient> --al
from starkware.cairo.common.cairo_builtins import HashBuiltin
from starkware.cairo.common.uint256 import Uint256
from openzeppelin.security.pausable import Pausable
from openzeppelin.security.pausable.library import Pausable
from openzeppelin.token.erc20.library import ERC20
(...)
Expand Down Expand Up @@ -148,32 +148,38 @@ python -m pip install .
```bash
nile compile --directory src

🤖 Compiling all Cairo contracts in the openzeppelin directory
🔨 Compiling openzeppelin/introspection/ERC165.cairo
🔨 Compiling openzeppelin/introspection/IERC165.cairo
🔨 Compiling openzeppelin/token/erc721/ERC721_Mintable_Burnable.cairo
🔨 Compiling openzeppelin/token/erc721/ERC721_Mintable_Pausable.cairo
🔨 Compiling openzeppelin/token/erc721/library.cairo
🔨 Compiling openzeppelin/token/erc721/interfaces/IERC721_Metadata.cairo
🔨 Compiling openzeppelin/token/erc721/interfaces/IERC721.cairo
🔨 Compiling openzeppelin/token/erc721/interfaces/IERC721_Receiver.cairo
🔨 Compiling openzeppelin/token/erc721/utils/ERC721_Holder.cairo
🔨 Compiling openzeppelin/token/erc20/ERC20_Mintable.cairo
🔨 Compiling openzeppelin/token/erc20/ERC20.cairo
🔨 Compiling openzeppelin/token/erc20/library.cairo
🔨 Compiling openzeppelin/token/erc20/ERC20_Pausable.cairo
🔨 Compiling openzeppelin/token/erc20/interfaces/IERC20.cairo
🔨 Compiling openzeppelin/token/erc721_enumerable/ERC721_Enumerable_Mintable_Burnable.cairo
🔨 Compiling openzeppelin/token/erc721_enumerable/library.cairo
🔨 Compiling openzeppelin/token/erc721_enumerable/interfaces/IERC721_Enumerable.cairo
🔨 Compiling openzeppelin/security/pausable.cairo
🔨 Compiling openzeppelin/security/safemath.cairo
🔨 Compiling openzeppelin/security/initializable.cairo
🔨 Compiling openzeppelin/access/ownable.cairo
🔨 Compiling openzeppelin/account/IAccount.cairo
🔨 Compiling openzeppelin/account/Account.cairo
🔨 Compiling openzeppelin/account/AddressRegistry.cairo
🔨 Compiling openzeppelin/utils/constants.cairo
🤖 Compiling all Cairo contracts in the src directory
🔨 Compiling src/openzeppelin/token/erc20/library.cairo
🔨 Compiling src/openzeppelin/token/erc20/presets/ERC20Mintable.cairo
🔨 Compiling src/openzeppelin/token/erc20/presets/ERC20Pausable.cairo
🔨 Compiling src/openzeppelin/token/erc20/presets/ERC20Upgradeable.cairo
🔨 Compiling src/openzeppelin/token/erc20/presets/ERC20.cairo
🔨 Compiling src/openzeppelin/token/erc20/IERC20.cairo
🔨 Compiling src/openzeppelin/token/erc721/enumerable/library.cairo
🔨 Compiling src/openzeppelin/token/erc721/library.cairo
🔨 Compiling src/openzeppelin/token/erc721/utils/ERC721Holder.cairo
🔨 Compiling src/openzeppelin/token/erc721/presets/ERC721MintablePausable.cairo
🔨 Compiling src/openzeppelin/token/erc721/presets/ERC721MintableBurnable.cairo
🔨 Compiling src/openzeppelin/token/erc721/presets/ERC721EnumerableMintableBurnable.cairo
🔨 Compiling src/openzeppelin/token/erc721/IERC721.cairo
🔨 Compiling src/openzeppelin/token/erc721/IERC721Metadata.cairo
🔨 Compiling src/openzeppelin/token/erc721/IERC721Receiver.cairo
🔨 Compiling src/openzeppelin/token/erc721/enumerable/IERC721Enumerable.cairo
🔨 Compiling src/openzeppelin/access/ownable/library.cairo
🔨 Compiling src/openzeppelin/security/reentrancyguard/library.cairo
🔨 Compiling src/openzeppelin/security/safemath/library.cairo
🔨 Compiling src/openzeppelin/security/pausable/library.cairo
🔨 Compiling src/openzeppelin/security/initializable/library.cairo
🔨 Compiling src/openzeppelin/utils/constants/library.cairo
🔨 Compiling src/openzeppelin/introspection/erc165/library.cairo
🔨 Compiling src/openzeppelin/introspection/erc165/IERC165.cairo
🔨 Compiling src/openzeppelin/upgrades/library.cairo
🔨 Compiling src/openzeppelin/upgrades/presets/Proxy.cairo
🔨 Compiling src/openzeppelin/account/library.cairo
🔨 Compiling src/openzeppelin/account/presets/EthAccount.cairo
🔨 Compiling src/openzeppelin/account/presets/Account.cairo
🔨 Compiling src/openzeppelin/account/presets/AddressRegistry.cairo
🔨 Compiling src/openzeppelin/account/IAccount.cairo
✅ Done
```

Expand Down
16 changes: 8 additions & 8 deletions docs/Access.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ Access control—that is, "who is allowed to do this thing"—is incredibly impo

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 [Ownable](../src/openzeppelin/access/ownable/library.cairo) for implementing ownership in your contracts.

### 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 [Ownable](../src/openzeppelin/access/library.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
from openzeppelin.access.ownable import Ownable
from openzeppelin.access.ownable.library import Ownable
@constructor
func constructor{
Expand All @@ -64,7 +64,7 @@ end
To restrict a function's access to the owner only, add in the `assert_only_owner` method:

```cairo
from openzeppelin.access.ownable import Ownable
from openzeppelin.access.ownable.library import Ownable
func protected_function{
syscall_ptr : felt*,
Expand Down Expand Up @@ -216,12 +216,12 @@ 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 [Creating role identifiers](#creating-role-identifiers) for information on creating identifiers).

Here's a simple example of implementing `AccessControl` on a portion of an [ERC20 token contract](../src/openzeppelin/token/erc20/ERC20.cairo) which defines and sets the 'minter' role:
Here's a simple example of implementing `AccessControl` on a portion of an [ERC20 token contract](../src/openzeppelin/token/erc20/presets/ERC20.cairo) 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
Expand Down Expand Up @@ -264,7 +264,7 @@ Let’s augment our ERC20 token example by also defining a 'burner' role, which
```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
Expand Down Expand Up @@ -331,7 +331,7 @@ from openzeppelin.token.erc20.library import ERC20
from openzeppelin.access.accesscontrol import AccessControl
from openzeppelin.utils.constants import DEFAULT_ADMIN_ROLE
from openzeppelin.utils.constants.library import DEFAULT_ADMIN_ROLE
const MINTER_ROLE = 0x4f96f87f6963bb246f2c30526628466840c642dc5c50d5a67777c6cc0e44ab5
Expand Down
11 changes: 7 additions & 4 deletions docs/Account.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,15 @@ In Python, this would look as follows:

```python
from starkware.starknet.testing.starknet import Starknet
from utils import get_contract_class
from signers import MockSigner

signer = MockSigner(123456789987654321)
starknet = await Starknet.empty()

# 1. Deploy Account
account = await starknet.deploy(
"contracts/Account.cairo",
get_contract_class("Account"),
constructor_calldata=[signer.public_key]
)

Expand Down Expand Up @@ -456,11 +459,11 @@ The following contract presets are ready to deploy and can be used as-is for qui

### Account

The [`Account`](../src/openzeppelin/account/Account.cairo) preset uses StarkNet keys to validate transactions.
The [`Account`](../src/openzeppelin/account/presets/Account.cairo) preset uses StarkNet keys to validate transactions.

### Eth Account

The [`EthAccount`](../src/openzeppelin/account/EthAccount.cairo) preset supports Ethereum addresses, validating transactions with secp256k1 keys.
The [`EthAccount`](../src/openzeppelin/account/presets/EthAccount.cairo) preset supports Ethereum addresses, validating transactions with secp256k1 keys.

## Account differentiation with ERC165

Expand All @@ -478,7 +481,7 @@ Account contract developers are encouraged to implement the [standard Account in

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 functionality 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.
Some other validation schemes to look out for in the future:

Expand Down
28 changes: 14 additions & 14 deletions docs/ERC20.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ The ERC20 token standard is a specification for [fungible tokens](https://docs.o
- [Extensibility](#extensibility)
- [Presets](#presets)
- [ERC20 (basic)](#erc20-basic)
- [ERC20_Mintable](#erc20_mintable)
- [ERC20_Pausable](#erc20_pausable)
- [ERC20_Upgradeable](#erc20_upgradeable)
- [ERC20Mintable](#erc20mintable)
- [ERC20Pausable](#erc20pausable)
- [ERC20Upgradeable](#erc20upgradeable)
- [API Specification](#api-specification)
- [Methods](#methods)
- [`name`](#name)
Expand Down Expand Up @@ -100,7 +100,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
Expand All @@ -127,7 +127,7 @@ await signer.send_transaction(account, erc20.contract_address, 'transfer', [reci

## 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 [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/library.cairo) i.e. `Pausable_pause`, `Pausable_unpause`. Next, the contract should expose the methods with the extended logic therein like this:

```python
@external
Expand All @@ -136,8 +136,8 @@ func transfer{
pedersen_ptr : HashBuiltin*,
range_check_ptr
}(recipient: felt, amount: Uint256) -> (success: felt):
Pausable_when_not_paused() # imported extended logic
ERC20_transfer(recipient, amount) # imported library method
Pausable.when_not_paused() # imported extended logic
ERC20.transfer(recipient, amount) # imported library method
return (TRUE)
end
```
Expand All @@ -158,19 +158,19 @@ The following contract presets are ready to deploy and can be used as-is for qui

### ERC20 (basic)

The [`ERC20`](../src/openzeppelin/token/erc20/ERC20.cairo) preset offers a quick and easy setup for deploying a basic ERC20 token.
The [`ERC20`](../src/openzeppelin/token/erc20/presets/ERC20.cairo) preset offers a quick and easy setup for deploying a basic ERC20 token.

### ERC20_Mintable
### ERC20Mintable

The [`ERC20_Mintable`](../src/openzeppelin/token/erc20/ERC20_Mintable.cairo) preset allows the contract owner to mint new tokens.
The [`ERC20Mintable`](../src/openzeppelin/token/erc20/presets/ERC20Mintable.cairo) preset allows the contract owner to mint new tokens.

### ERC20_Pausable
### ERC20Pausable

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 [`ERC20Pausable`](../src/openzeppelin/token/erc20/presets/ERC20Pausable.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.

### ERC20_Upgradeable
### ERC20Upgradeable

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 [`ERC20Upgradeable`](../src/openzeppelin/token/erc20/presets/ERC20Upgradeable.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).

## API Specification

Expand Down
Loading

0 comments on commit 243df2b

Please sign in to comment.