├── pytest.ini ├── requirements.txt ├── src └── codefordao │ ├── libraries │ ├── Structs.cairo │ ├── merkle.cairo │ └── utils.cairo │ ├── modules │ └── Payroll.cairo │ └── core │ ├── Governor.cairo │ ├── Treasury.cairo │ ├── Module.cairo │ ├── Share.cairo │ └── Membership.cairo ├── pyproject.toml ├── setup.py ├── setup.cfg ├── LICENSE ├── tests ├── test_ERC20.py ├── test_ERC721.py └── utils.py ├── .gitignore └── README.md /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | asyncio_mode = auto 3 | log_cli = true -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | wheel 2 | cairo-lang 3 | cairo-nile 4 | pytest-cairo 5 | tox 6 | immutablex-starknet 7 | git+https://github.com/OpenZeppelin/cairo-contracts.git -------------------------------------------------------------------------------- /src/codefordao/libraries/Structs.cairo: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | %lang starknet 4 | 5 | struct ContractAddressType: 6 | member membership: felt 7 | member governor: felt 8 | member treasury: felt 9 | member share_token: felt 10 | member share_governor: felt 11 | end -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | # AVOID CHANGING REQUIRES: IT WILL BE UPDATED BY PYSCAFFOLD! 3 | requires = ["setuptools>=46.1.0", "setuptools_scm[toml]>=5", "wheel"] 4 | build-backend = "setuptools.build_meta" 5 | 6 | [tool.setuptools_scm] 7 | # See configuration details in https://github.com/pypa/setuptools_scm 8 | version_scheme = "no-guess-dev" -------------------------------------------------------------------------------- /src/codefordao/modules/Payroll.cairo: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | %lang starknet 4 | 5 | from starkware.cairo.common.cairo_builtins import HashBuiltin 6 | 7 | # 8 | # Initializer 9 | # 10 | @constructor 11 | func constructor{ 12 | syscall_ptr: felt*, 13 | pedersen_ptr: HashBuiltin*, 14 | range_check_ptr 15 | }( 16 | ): 17 | return () 18 | end -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | if __name__ == "__main__": 4 | try: 5 | setup(use_scm_version={"version_scheme": "no-guess-dev"}) 6 | except: # noqa 7 | print( 8 | "\n\nAn error occurred while building the project, " 9 | "please ensure you have the most updated version of setuptools, " 10 | "setuptools_scm and wheel with:\n" 11 | " pip install -U setuptools setuptools_scm wheel\n\n" 12 | ) 13 | raise -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = codefordao-cairo-contracts 3 | version = attr: codefordao-cairo-contracts.__version__ 4 | description = CodeforDAO contracts in Cairo lang 5 | author = GUO Yu 6 | author_email = contact@codefordao.org 7 | license = MIT 8 | long_description = file: README.md 9 | long_description_content_type = text/markdown; charset=UTF-8 10 | url = https://github.com/CodeforDAO/cairo-contracts 11 | platforms = any 12 | classifiers = 13 | Operating System :: OS Independent 14 | 15 | [options] 16 | zip_safe = False 17 | packages = find_namespace: 18 | include_package_data = True 19 | package_dir = 20 | =src 21 | 22 | install_requires = 23 | importlib-metadata>=4.0 24 | 25 | [options.packages.find] 26 | where = src 27 | exclude = 28 | tests 29 | 30 | [options.package_data] 31 | codefordao = "*.cairo" 32 | 33 | [options.extras_require] 34 | testing = 35 | setuptools 36 | tox 37 | pytest -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 CodeforDAO 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/test_ERC20.py: -------------------------------------------------------------------------------- 1 | """erc20.cairo test file.""" 2 | import pytest 3 | from starkware.starknet.testing.starknet import Starknet 4 | from utils import ( 5 | cached_contract, get_contract_def, to_uint, str_to_felt 6 | ) 7 | 8 | # testing vars 9 | OWNER = 42 10 | NAME = str_to_felt("MyToken") 11 | 12 | @pytest.fixture 13 | def contract_defs(): 14 | erc20_def = get_contract_def('ERC20.cairo') 15 | return erc20_def 16 | 17 | @pytest.fixture 18 | async def erc20_init(contract_defs): 19 | erc20_def = contract_defs 20 | starknet = await Starknet.empty() 21 | erc20 = await starknet.deploy( 22 | contract_def=erc20_def, 23 | constructor_calldata=[OWNER] 24 | ) 25 | return ( 26 | starknet.state, 27 | erc20, 28 | ) 29 | 30 | @pytest.fixture 31 | def erc20_factory(contract_defs, erc20_init): 32 | erc20_def = contract_defs 33 | state, erc20 = erc20_init 34 | _state = state.copy() 35 | erc20 = cached_contract(_state, erc20_def, erc20) 36 | return erc20 37 | 38 | @pytest.mark.asyncio 39 | async def test_initial_data(erc20_factory): 40 | erc20 = erc20_factory 41 | execution_info = await erc20.name().call() 42 | assert execution_info.result.name == NAME -------------------------------------------------------------------------------- /tests/test_ERC721.py: -------------------------------------------------------------------------------- 1 | """ERC721.cairo test file.""" 2 | import pytest 3 | from starkware.starknet.testing.starknet import Starknet 4 | from utils import ( 5 | cached_contract, get_contract_def, str_to_felt 6 | ) 7 | 8 | # testing vars 9 | OWNER = 42 10 | NAME = str_to_felt("MyToken") 11 | 12 | @pytest.fixture 13 | def contract_defs(): 14 | erc721_def = get_contract_def('ERC721.cairo') 15 | return erc721_def 16 | 17 | @pytest.fixture 18 | async def erc721_init(contract_defs): 19 | erc721_def = contract_defs 20 | starknet = await Starknet.empty() 21 | erc721 = await starknet.deploy( 22 | contract_def=erc721_def, 23 | constructor_calldata=[OWNER] 24 | ) 25 | return ( 26 | starknet.state, 27 | erc721, 28 | ) 29 | 30 | @pytest.fixture 31 | def erc721_factory(contract_defs, erc721_init): 32 | erc721_def = contract_defs 33 | state, erc721 = erc721_init 34 | _state = state.copy() 35 | erc721 = cached_contract(_state, erc721_def, erc721) 36 | return erc721 37 | 38 | @pytest.mark.asyncio 39 | async def test_initial_data(erc721_factory): 40 | erc721 = erc721_factory 41 | execution_info = await erc721.name().call() 42 | assert execution_info.result.name == NAME -------------------------------------------------------------------------------- /src/codefordao/libraries/merkle.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin 4 | from starkware.cairo.common.hash import hash2 5 | from starkware.cairo.common.math_cmp import is_le_felt 6 | 7 | # verifies a merkle proof 8 | func merkle_verify{ 9 | pedersen_ptr: HashBuiltin*, 10 | range_check_ptr 11 | }( 12 | leaf: felt, 13 | root: felt, 14 | proof_len: felt, 15 | proof: felt* 16 | ) -> (res: felt): 17 | let (calc_root) = calc_merkle_root(leaf, proof_len, proof) 18 | # check if calculated root is equal to expected 19 | if calc_root == root: 20 | return (1) 21 | else: 22 | return (0) 23 | end 24 | end 25 | 26 | # calculates the merkle root of a given proof 27 | func calc_merkle_root{ 28 | pedersen_ptr: HashBuiltin*, 29 | range_check_ptr 30 | }( 31 | curr: felt, 32 | proof_len: felt, 33 | proof: felt* 34 | ) -> (res: felt): 35 | alloc_locals 36 | 37 | if proof_len == 0: 38 | return (curr) 39 | end 40 | 41 | local node 42 | local proof_elem = [proof] 43 | let (le) = is_le_felt(curr, proof_elem) 44 | 45 | if le == 1: 46 | let (n) = hash2{hash_ptr=pedersen_ptr}(curr, proof_elem) 47 | node = n 48 | else: 49 | let (n) = hash2{hash_ptr=pedersen_ptr}(proof_elem, curr) 50 | node = n 51 | end 52 | 53 | let (res) = calc_merkle_root(node, proof_len-1, proof+1) 54 | return (res) 55 | end 56 | -------------------------------------------------------------------------------- /src/codefordao/core/Governor.cairo: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | %lang starknet 4 | 5 | from starkware.cairo.common.cairo_builtins import HashBuiltin 6 | from openzeppelin.introspection.ERC165 import ERC165 7 | 8 | # 9 | # Initializer 10 | # 11 | @constructor 12 | func constructor{ 13 | syscall_ptr: felt*, 14 | pedersen_ptr: HashBuiltin*, 15 | range_check_ptr 16 | }( 17 | name: felt, 18 | token_addr: felt, 19 | treasury_addr: felt 20 | settings: felt 21 | ): 22 | return () 23 | end 24 | 25 | # 26 | # Getters 27 | # 28 | 29 | @view 30 | func quorum{ 31 | syscall_ptr: felt*, 32 | pedersen_ptr: HashBuiltin*, 33 | range_check_ptr 34 | }(block_number: felt): 35 | return () 36 | end 37 | 38 | @view 39 | func getVotes{ 40 | syscall_ptr: felt*, 41 | pedersen_ptr: HashBuiltin*, 42 | range_check_ptr 43 | }(addr: felt, block_number: felt): 44 | return () 45 | end 46 | 47 | @view 48 | func state{ 49 | syscall_ptr: felt*, 50 | pedersen_ptr: HashBuiltin*, 51 | range_check_ptr 52 | }(proposal_id: felt): 53 | return () 54 | end 55 | 56 | @view 57 | func proposalThreshold{ 58 | syscall_ptr: felt*, 59 | pedersen_ptr: HashBuiltin*, 60 | range_check_ptr 61 | }(): 62 | return () 63 | end 64 | 65 | @view 66 | func supportsInterface{ 67 | syscall_ptr: felt*, 68 | pedersen_ptr: HashBuiltin*, 69 | range_check_ptr 70 | }(interfaceId: felt) -> (success: felt): 71 | let (success) = ERC165.supports_interface(interfaceId) 72 | return (success) 73 | end 74 | 75 | # 76 | # Externals 77 | # 78 | @external 79 | func propose{ 80 | syscall_ptr: felt*, 81 | pedersen_ptr: HashBuiltin*, 82 | range_check_ptr 83 | }( 84 | ): 85 | return () 86 | end -------------------------------------------------------------------------------- /src/codefordao/core/Treasury.cairo: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | %lang starknet 4 | 5 | from starkware.cairo.common.cairo_builtins import HashBuiltin 6 | 7 | # 8 | # Storage 9 | # 10 | @storage_var 11 | func contracts_addresses(contract_type: ContractType) -> (addr: felt): 12 | end 13 | 14 | # 15 | # Initializer 16 | # 17 | @constructor 18 | func constructor{ 19 | syscall_ptr: felt*, 20 | pedersen_ptr: HashBuiltin*, 21 | range_check_ptr 22 | }( 23 | timelock_delay: felt, 24 | membership_address: felt, 25 | share_address: felt 26 | ): 27 | return () 28 | end 29 | 30 | # 31 | # Externals 32 | # 33 | 34 | @external 35 | func vestingShare{ 36 | syscall_ptr: felt*, 37 | pedersen_ptr: HashBuiltin*, 38 | range_check_ptr 39 | }(uri: felt, shareRatio: felt): 40 | Ownable.assert_only_owner() 41 | return () 42 | end 43 | 44 | @external 45 | func updateInvestmentSettings{ 46 | syscall_ptr: felt*, 47 | pedersen_ptr: HashBuiltin*, 48 | range_check_ptr 49 | }(settings: felt): 50 | Ownable.assert_only_owner() 51 | return () 52 | end 53 | 54 | @external 55 | func updateShareSplit{ 56 | syscall_ptr: felt*, 57 | pedersen_ptr: HashBuiltin*, 58 | range_check_ptr 59 | }(share_split: felt): 60 | Ownable.assert_only_owner() 61 | return () 62 | end 63 | 64 | @external 65 | func invest{ 66 | syscall_ptr: felt*, 67 | pedersen_ptr: HashBuiltin*, 68 | range_check_ptr 69 | }(amount: felt): 70 | check_investment_enabled() 71 | Ownable.assert_only_owner() 72 | return () 73 | end 74 | 75 | @external 76 | func investInERC20{ 77 | syscall_ptr: felt*, 78 | pedersen_ptr: HashBuiltin*, 79 | range_check_ptr 80 | }(amount: felt): 81 | check_investment_enabled() 82 | Ownable.assert_only_owner() 83 | return () 84 | end 85 | 86 | @external 87 | func pullModulePayment{ 88 | syscall_ptr: felt*, 89 | pedersen_ptr: HashBuiltin*, 90 | range_check_ptr 91 | }(amount: felt): 92 | Ownable.assert_only_owner() 93 | return () 94 | end 95 | 96 | @external 97 | func approveModulePayment{ 98 | syscall_ptr: felt*, 99 | pedersen_ptr: HashBuiltin*, 100 | range_check_ptr 101 | }(amount: felt): 102 | Ownable.assert_only_owner() 103 | return () 104 | end -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | artifacts/ 2 | *DS_STORE 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Env 12 | *.env 13 | *.accounts.json 14 | accounts.json 15 | 16 | # Distribution / packaging 17 | .Python 18 | build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | .eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | wheels/ 30 | pip-wheel-metadata/ 31 | share/python-wheels/ 32 | *.egg-info/ 33 | .installed.cfg 34 | *.egg 35 | MANIFEST 36 | 37 | # PyInstaller 38 | # Usually these files are written by a python script from a template 39 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 40 | *.manifest 41 | *.spec 42 | 43 | # Installer logs 44 | pip-log.txt 45 | pip-delete-this-directory.txt 46 | 47 | # Unit test / coverage reports 48 | htmlcov/ 49 | .tox/ 50 | .nox/ 51 | .coverage 52 | .coverage.* 53 | .cache 54 | nosetests.xml 55 | coverage.xml 56 | *.cover 57 | *.py,cover 58 | .hypothesis/ 59 | .pytest_cache/ 60 | 61 | # Translations 62 | *.mo 63 | *.pot 64 | 65 | # Django stuff: 66 | *.log 67 | local_settings.py 68 | db.sqlite3 69 | db.sqlite3-journal 70 | 71 | # Flask stuff: 72 | instance/ 73 | .webassets-cache 74 | 75 | # Scrapy stuff: 76 | .scrapy 77 | 78 | # Sphinx documentation 79 | docs/_build/ 80 | 81 | # PyBuilder 82 | target/ 83 | 84 | # Jupyter Notebook 85 | .ipynb_checkpoints 86 | 87 | # IPython 88 | profile_default/ 89 | ipython_config.py 90 | 91 | # pyenv 92 | .python-version 93 | 94 | # pipenv 95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 98 | # install all needed dependencies. 99 | #Pipfile.lock 100 | 101 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 102 | __pypackages__/ 103 | 104 | # Celery stuff 105 | celerybeat-schedule 106 | celerybeat.pid 107 | 108 | # SageMath parsed files 109 | *.sage.py 110 | 111 | # Environments 112 | .env 113 | .venv 114 | env/ 115 | venv/ 116 | ENV/ 117 | env.bak/ 118 | venv.bak/ 119 | 120 | # Spyder project settings 121 | .spyderproject 122 | .spyproject 123 | 124 | # Rope project settings 125 | .ropeproject 126 | 127 | # mkdocs documentation 128 | /site 129 | 130 | # mypy 131 | .mypy_cache/ 132 | .dmypy.json 133 | dmypy.json 134 | 135 | # Pyre type checker 136 | .pyre/ 137 | 138 | 139 | node.json 140 | *.deployments.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 |

5 | CodeforDAO Contracts in Cairo (StarkNet) 6 |

7 |

8 | Base on, build upon and code for DAOs. 9 |

10 |

11 | Make DAO the next generation of productivity tools for global collaboration. 12 |

13 |

14 | Follow us on Twitter @codefordao. 15 |

16 | 17 |

18 | 19 | mit license 20 | 21 |

22 | 23 | > **This project is a work in progress, it has not been audited for code security and is being deployed in local development and test networks at this time. Please use with caution.** 24 | 25 | The CodeforDAO contract is a set of DAO infrastructure and efficiency tools with member NFT at its core. 26 | 27 | It is centered on a set of membership contracts for the `ERC721` protocol, the creation of its counterpart share contracts, two parallel governance frameworks and a timelock vault contract. 28 | 29 | It introduces some basic features to DAO, including vault contracts for self-service participation in investments, a set of modular frameworks to support aggressive governance. 30 | 31 | Read the full documents in this [Solidity Repo](https://github.com/CodeforDAO/contracts) of CodeforDAO contracts 32 | 33 | ## Set up the project 34 | 35 | Before creating virtual environment, please **make sure** to install Python `3.7.12` using the Python version management tool and activate that version. 36 | 37 | ### Create a Python virtual environment 38 | 39 | ```bash 40 | python -m venv env 41 | source env/bin/activate 42 | ``` 43 | 44 | ### Install the requirements 45 | 46 | ```bash 47 | pip install -r requirements.txt 48 | ``` 49 | 50 | **Notice**: this project use the latest version of OpenZeppelin contract for Cairo instead of the stable release of it. 51 | 52 | ## Compile 53 | 54 | ```bash 55 | nile compile --directory src 56 | ``` 57 | 58 | ## Running tests 59 | 60 | Running spec tests where you can find them in `./test` folder 61 | 62 | ```bash 63 | $ pytest tests 64 | ``` 65 | 66 | ## Use this module in your project 67 | 68 | **Note:** these smart contracts are not designed to be library contracts(except `Module.cairo`), and you can fork these contracts locally to modify them yourself, rather than importing them directly by a git link. 69 | 70 | ```bash 71 | pip install git+https://github.com/CodeforDAO/cairo-contracts.git 72 | ``` 73 | 74 | ## License 75 | 76 | This project is released under the [MIT](LICENSE). 77 | -------------------------------------------------------------------------------- /src/codefordao/libraries/utils.cairo: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | %lang starknet 4 | 5 | from starkware.cairo.common.alloc import alloc 6 | from starkware.cairo.common.cairo_builtins import HashBuiltin 7 | 8 | struct ArrayInfo: 9 | member key : felt 10 | member len : felt 11 | end 12 | 13 | @storage_var 14 | func _array_key_index() -> (res: felt): 15 | end 16 | 17 | @storage_var 18 | func _arrays(key: felt, index: felt) -> (res: felt): 19 | end 20 | 21 | @storage_var 22 | func _array_info(key: felt) -> (res: ArrayInfo): 23 | end 24 | 25 | namespace Array: 26 | # Save array to storage with increasing key. 27 | func save{ 28 | syscall_ptr : felt*, 29 | pedersen_ptr : HashBuiltin*, 30 | range_check_ptr 31 | }( 32 | arr_len: felt, 33 | arr: felt* 34 | ) -> (key: felt): 35 | alloc_locals 36 | 37 | with_attr error_message("Arrays.push: invalid length of giving array"): 38 | assert arr_len = 0 39 | end 40 | 41 | let (local k) = _array_key_index.read() 42 | let info = ArrayInfo(key=k, len=arr_len) 43 | 44 | _array_info.write(key=k, value=info) 45 | _write_array(key=k, arr_index=0, arr_len=arr_len, arr=arr) 46 | _array_key_index.write(k + 1) 47 | 48 | return (k) 49 | end 50 | 51 | # Fetch array item by key and index 52 | func get_item{ 53 | syscall_ptr : felt*, 54 | pedersen_ptr : HashBuiltin*, 55 | range_check_ptr 56 | }( 57 | key: felt, 58 | index: felt 59 | ) -> (res: felt): 60 | let (res) = _arrays.read(key=key, index=index) 61 | return (res) 62 | end 63 | 64 | func get_array{ 65 | syscall_ptr : felt*, 66 | pedersen_ptr : HashBuiltin*, 67 | range_check_ptr 68 | }(key: felt) -> ( 69 | arr_len: felt, 70 | arr: felt* 71 | ): 72 | alloc_locals 73 | 74 | let (arr) = alloc() 75 | let (local arr_info) = _array_info.read(key) 76 | 77 | if arr_info.len == 0: 78 | return (arr_len=arr_info.len, arr=arr) 79 | end 80 | 81 | _read_array(key=key, arr_index=0, arr_len=arr_info.len, arr=arr) 82 | return (arr_len=arr_info.len, arr=arr) 83 | end 84 | 85 | # Store arrays into multi-dimensional maps by recursion 86 | func _write_array{ 87 | syscall_ptr : felt*, 88 | pedersen_ptr : HashBuiltin*, 89 | range_check_ptr 90 | }( 91 | key: felt, 92 | arr_index: felt, 93 | arr_len: felt, 94 | arr: felt* 95 | ): 96 | if arr_index == arr_len: 97 | return () 98 | end 99 | 100 | _arrays.write(key=key, index=arr_index, value=[arr]) 101 | _write_array(key=key, arr_index=arr_index + 1, arr_len=arr_len, arr=arr + 1) 102 | 103 | return () 104 | end 105 | 106 | func _read_array{ 107 | syscall_ptr : felt*, 108 | pedersen_ptr : HashBuiltin*, 109 | range_check_ptr 110 | }( 111 | key: felt, 112 | arr_index: felt, 113 | arr_len: felt, 114 | arr: felt* 115 | ): 116 | if arr_index == arr_len: 117 | return () 118 | end 119 | 120 | let (item) = _arrays.read(key=key, index=arr_index) 121 | assert arr[arr_index] = item 122 | 123 | _read_array( 124 | key=key, 125 | arr_index=arr_index + 1, 126 | arr_len=arr_len, 127 | arr=arr 128 | ) 129 | 130 | return () 131 | end 132 | end -------------------------------------------------------------------------------- /src/codefordao/core/Module.cairo: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | %lang starknet 4 | 5 | from starkware.cairo.common.alloc import alloc 6 | from starkware.cairo.common.cairo_builtins import HashBuiltin 7 | from starkware.cairo.common.math import assert_le, assert_lt 8 | from starkware.starknet.common.syscalls import call_contract, get_caller_address 9 | 10 | from src.codefordao.libraries.structs import ContractAddressType 11 | from src.codefordao.libraries.utils import Arrays 12 | 13 | # 14 | # Events 15 | # 16 | 17 | @event 18 | func ModuleProposalCreated( 19 | module_addr: felt, 20 | operator_addr : felt, 21 | id : felt, 22 | timestamp : felt 23 | ): 24 | end 25 | 26 | @event 27 | func ModuleProposalConfirmed( 28 | module_addr: felt, 29 | operator_addr : felt, 30 | id : felt, 31 | timestamp : felt 32 | ): 33 | end 34 | 35 | @event 36 | func ModuleProposalScheduled( 37 | module_addr: felt, 38 | operator_addr : felt, 39 | id : felt, 40 | timestamp : felt 41 | ): 42 | end 43 | 44 | @event 45 | func ModuleProposalExecuted( 46 | module_addr: felt, 47 | operator_addr : felt, 48 | id : felt, 49 | timestamp : felt 50 | ): 51 | end 52 | 53 | @event 54 | func ModuleProposalCancelled( 55 | module_addr: felt, 56 | operator_addr : felt, 57 | id : felt, 58 | timestamp : felt 59 | ): 60 | end 61 | 62 | # 63 | # Structs 64 | # 65 | 66 | struct MicroProposal: 67 | member target : felt 68 | member function_selector : felt 69 | member calldata_len : felt 70 | member confirmations : felt 71 | member status: felt 72 | end 73 | 74 | # 75 | # Storage 76 | # 77 | 78 | @storage_var 79 | func _name() -> (uri: felt): 80 | end 81 | 82 | @storage_var 83 | func _description() -> (uri: felt): 84 | end 85 | 86 | @storage_var 87 | func _operators_key() -> (res: felt): 88 | end 89 | 90 | @storage_var 91 | func _addresses(addr_type: ContractAddressType) -> (addr: felt): 92 | end 93 | 94 | namespace CodeforDAO_Module: 95 | 96 | # 97 | # Initializer 98 | # 99 | func initializer{ 100 | syscall_ptr: felt*, 101 | pedersen_ptr: HashBuiltin*, 102 | range_check_ptr 103 | }( 104 | name: felt, 105 | description: felt, 106 | membership_addr: felt, 107 | operators_len: felt, 108 | operators: felt*, 109 | timelock_delay: felt 110 | ): 111 | _name.write(name) 112 | _description.write(description) 113 | _addresses.write(addr_type=ContractAddressType.membership, addr=membership_addr) 114 | 115 | let (_key) = Arrays.push(arr_len=operators_len, arr=operators) 116 | _operators_key.write(_key) 117 | 118 | return () 119 | end 120 | 121 | # 122 | # Public functions 123 | # 124 | func operators{ 125 | pedersen_ptr: HashBuiltin*, 126 | syscall_ptr: felt*, 127 | range_check_ptr 128 | }() -> ( 129 | operators_len: felt, 130 | operators: felt* 131 | ): 132 | 133 | let (key) = _operators_key.read() 134 | let (arr_len, arr) = Arrays.get_array(key) 135 | 136 | return (operators_len=arr_len, operators=arr) 137 | end 138 | 139 | func getMembershipTokenId{ 140 | pedersen_ptr: HashBuiltin*, 141 | syscall_ptr: felt*, 142 | range_check_ptr 143 | }() -> (): 144 | end 145 | 146 | func getAddressByMemberId{ 147 | pedersen_ptr: HashBuiltin*, 148 | syscall_ptr: felt*, 149 | range_check_ptr 150 | }() -> (): 151 | end 152 | 153 | func getProposal{ 154 | pedersen_ptr: HashBuiltin*, 155 | syscall_ptr: felt*, 156 | range_check_ptr 157 | }(proposal_id: felt) -> (): 158 | end 159 | 160 | func propose{ 161 | pedersen_ptr: HashBuiltin*, 162 | syscall_ptr: felt*, 163 | range_check_ptr 164 | }() -> (): 165 | end 166 | 167 | func confirm{ 168 | pedersen_ptr: HashBuiltin*, 169 | syscall_ptr: felt*, 170 | range_check_ptr 171 | }(proposal_id: felt) -> (): 172 | end 173 | 174 | func schedule{ 175 | pedersen_ptr: HashBuiltin*, 176 | syscall_ptr: felt*, 177 | range_check_ptr 178 | }(proposal_id: felt) -> (): 179 | end 180 | 181 | func execute{ 182 | pedersen_ptr: HashBuiltin*, 183 | syscall_ptr: felt*, 184 | range_check_ptr 185 | }(proposal_id: felt) -> (): 186 | end 187 | 188 | func cancel{ 189 | pedersen_ptr: HashBuiltin*, 190 | syscall_ptr: felt*, 191 | range_check_ptr 192 | }(proposal_id: felt) -> (): 193 | end 194 | 195 | # 196 | # Internal 197 | # 198 | func pullPayments{ 199 | pedersen_ptr: HashBuiltin*, 200 | syscall_ptr: felt*, 201 | range_check_ptr 202 | }() -> (): 203 | end 204 | end -------------------------------------------------------------------------------- /src/codefordao/core/Share.cairo: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | %lang starknet 4 | 5 | from starkware.cairo.common.cairo_builtins import HashBuiltin 6 | from starkware.cairo.common.uint256 import Uint256 7 | from starkware.cairo.common.bool import TRUE 8 | from starkware.starknet.common.syscalls import get_caller_address 9 | 10 | from openzeppelin.token.erc20.library import ERC20 11 | from openzeppelin.security.pausable import Pausable 12 | from openzeppelin.access.ownable import Ownable 13 | 14 | # 15 | # @title Share 16 | # @notice The share contract determines the issuance and suspension of share tokens, 17 | # as well as the administrator role. 18 | # Basically it is a pre-defined contract for erc20 token but support ERC20Votes. 19 | # 20 | 21 | @constructor 22 | func constructor{ 23 | syscall_ptr: felt*, 24 | pedersen_ptr: HashBuiltin*, 25 | range_check_ptr 26 | }( 27 | name: felt, 28 | symbol: felt, 29 | owner: felt 30 | ): 31 | ERC20.initializer(name, symbol, 18) 32 | Ownable.initializer(owner) 33 | return () 34 | end 35 | 36 | # 37 | # Getters 38 | # 39 | 40 | @view 41 | func name{ 42 | syscall_ptr: felt*, 43 | pedersen_ptr: HashBuiltin*, 44 | range_check_ptr 45 | }() -> (name: felt): 46 | let (name) = ERC20.name() 47 | return (name) 48 | end 49 | 50 | @view 51 | func symbol{ 52 | syscall_ptr: felt*, 53 | pedersen_ptr: HashBuiltin*, 54 | range_check_ptr 55 | }() -> (symbol: felt): 56 | let (symbol) = ERC20.symbol() 57 | return (symbol) 58 | end 59 | 60 | @view 61 | func totalSupply{ 62 | syscall_ptr: felt*, 63 | pedersen_ptr: HashBuiltin*, 64 | range_check_ptr 65 | }() -> (totalSupply: Uint256): 66 | let (totalSupply) = ERC20_totalSupply() 67 | return (totalSupply) 68 | end 69 | 70 | @view 71 | func decimals{ 72 | syscall_ptr: felt*, 73 | pedersen_ptr: HashBuiltin*, 74 | range_check_ptr 75 | }() -> (decimals: felt): 76 | let (decimals) = ERC20_decimals() 77 | return (decimals) 78 | end 79 | 80 | @view 81 | func balanceOf{ 82 | syscall_ptr: felt*, 83 | pedersen_ptr: HashBuiltin*, 84 | range_check_ptr 85 | }(account: felt) -> (balance: Uint256): 86 | let (balance: Uint256) = ERC20.balance_of(account) 87 | return (balance) 88 | end 89 | 90 | @view 91 | func allowance{ 92 | syscall_ptr: felt*, 93 | pedersen_ptr: HashBuiltin*, 94 | range_check_ptr 95 | }(owner: felt, spender: felt) -> (remaining: Uint256): 96 | let (remaining: Uint256) = ERC20.allowance(owner, spender) 97 | return (remaining) 98 | end 99 | 100 | @view 101 | func owner{ 102 | syscall_ptr : felt*, 103 | pedersen_ptr : HashBuiltin*, 104 | range_check_ptr 105 | }() -> (owner: felt): 106 | let (owner: felt) = Ownable.owner() 107 | return (owner) 108 | end 109 | 110 | @view 111 | func paused{ 112 | syscall_ptr: felt*, 113 | pedersen_ptr: HashBuiltin*, 114 | range_check_ptr 115 | }() -> (paused: felt): 116 | let (paused) = Pausable.is_paused() 117 | return (paused) 118 | end 119 | 120 | # 121 | # Externals 122 | # 123 | 124 | @external 125 | func transfer{ 126 | syscall_ptr: felt*, 127 | pedersen_ptr: HashBuiltin*, 128 | range_check_ptr 129 | }(recipient: felt, amount: Uint256) -> (success: felt): 130 | Pausable.assert_not_paused() 131 | ERC20.transfer(recipient, amount) 132 | return (TRUE) 133 | end 134 | 135 | @external 136 | func transferFrom{ 137 | syscall_ptr: felt*, 138 | pedersen_ptr: HashBuiltin*, 139 | range_check_ptr 140 | }( 141 | sender: felt, 142 | recipient: felt, 143 | amount: Uint256 144 | ) -> (success: felt): 145 | Pausable.assert_not_paused() 146 | ERC20.transfer_from(sender, recipient, amount) 147 | return (TRUE) 148 | end 149 | 150 | @external 151 | func approve{ 152 | syscall_ptr: felt*, 153 | pedersen_ptr: HashBuiltin*, 154 | range_check_ptr 155 | }(spender: felt, amount: Uint256) -> (success: felt): 156 | Pausable.assert_not_paused() 157 | ERC20.approve(spender, amount) 158 | return (TRUE) 159 | end 160 | 161 | @external 162 | func increaseAllowance{ 163 | syscall_ptr: felt*, 164 | pedersen_ptr: HashBuiltin*, 165 | range_check_ptr 166 | }(spender: felt, added_value: Uint256) -> (success: felt): 167 | Pausable.assert_not_paused() 168 | ERC20.increase_allowance(spender, added_value) 169 | return (TRUE) 170 | end 171 | 172 | @external 173 | func decreaseAllowance{ 174 | syscall_ptr: felt*, 175 | pedersen_ptr: HashBuiltin*, 176 | range_check_ptr 177 | }(spender: felt, subtracted_value: Uint256) -> (success: felt): 178 | Pausable.assert_not_paused() 179 | ERC20.decrease_allowance(spender, subtracted_value) 180 | return (TRUE) 181 | end 182 | 183 | @external 184 | func transferOwnership{ 185 | syscall_ptr: felt*, 186 | pedersen_ptr: HashBuiltin*, 187 | range_check_ptr 188 | }(newOwner: felt): 189 | Ownable.transfer_ownership(newOwner) 190 | return () 191 | end 192 | 193 | @external 194 | func renounceOwnership{ 195 | syscall_ptr: felt*, 196 | pedersen_ptr: HashBuiltin*, 197 | range_check_ptr 198 | }(): 199 | Ownable.renounce_ownership() 200 | return () 201 | end 202 | 203 | @external 204 | func burn{ 205 | syscall_ptr: felt*, 206 | pedersen_ptr: HashBuiltin*, 207 | range_check_ptr 208 | }(amount: Uint256): 209 | Pausable.assert_not_paused() 210 | let (owner) = get_caller_address() 211 | ERC20._burn(owner, amount) 212 | return () 213 | end 214 | 215 | @external 216 | func pause{ 217 | syscall_ptr: felt*, 218 | pedersen_ptr: HashBuiltin*, 219 | range_check_ptr 220 | }(): 221 | Ownable.assert_only_owner() 222 | Pausable._pause() 223 | return () 224 | end 225 | 226 | @external 227 | func unpause{ 228 | syscall_ptr: felt*, 229 | pedersen_ptr: HashBuiltin*, 230 | range_check_ptr 231 | }(): 232 | Ownable.assert_only_owner() 233 | Pausable._unpause() 234 | return () 235 | end 236 | 237 | @external 238 | func mint{ 239 | syscall_ptr: felt*, 240 | pedersen_ptr: HashBuiltin*, 241 | range_check_ptr 242 | }(to: felt, amount: Uint256): 243 | Ownable.assert_only_owner() 244 | ERC20._mint(to, amount) 245 | return () 246 | end 247 | -------------------------------------------------------------------------------- /tests/utils.py: -------------------------------------------------------------------------------- 1 | """Utilities for testing Cairo contracts.""" 2 | #Copied from https://github.com/OpenZeppelin/cairo-contracts/blob/main/tests/utils.py 3 | from pathlib import Path 4 | import math 5 | from starkware.cairo.common.hash_state import compute_hash_on_elements 6 | from starkware.crypto.signature.signature import private_to_stark_key, sign 7 | from starkware.starknet.public.abi import get_selector_from_name 8 | from starkware.starknet.compiler.compile import compile_starknet_files 9 | from starkware.starkware_utils.error_handling import StarkException 10 | from starkware.starknet.testing.starknet import StarknetContract 11 | from starkware.starknet.business_logic.execution.objects import Event 12 | 13 | 14 | MAX_UINT256 = (2**128 - 1, 2**128 - 1) 15 | INVALID_UINT256 = (MAX_UINT256[0] + 1, MAX_UINT256[1]) 16 | ZERO_ADDRESS = 0 17 | TRUE = 1 18 | FALSE = 0 19 | 20 | TRANSACTION_VERSION = 0 21 | 22 | _root = Path(__file__).parent.parent 23 | 24 | 25 | def contract_path(name): 26 | if name.startswith("tests/"): 27 | return str(_root / name) 28 | else: 29 | return str(_root / "src" / name) 30 | 31 | 32 | def str_to_felt(text): 33 | b_text = bytes(text, "ascii") 34 | return int.from_bytes(b_text, "big") 35 | 36 | 37 | def felt_to_str(felt): 38 | b_felt = felt.to_bytes(31, "big") 39 | return b_felt.decode() 40 | 41 | 42 | def assert_event_emitted(tx_exec_info, from_address, name, data): 43 | assert Event( 44 | from_address=from_address, 45 | keys=[get_selector_from_name(name)], 46 | data=data, 47 | ) in tx_exec_info.raw_events 48 | 49 | 50 | def uint(a): 51 | return(a, 0) 52 | 53 | 54 | def to_uint(a): 55 | """Takes in value, returns uint256-ish tuple.""" 56 | return (a & ((1 << 128) - 1), a >> 128) 57 | 58 | 59 | def from_uint(uint): 60 | """Takes in uint256-ish tuple, returns value.""" 61 | return uint[0] + (uint[1] << 128) 62 | 63 | 64 | def add_uint(a, b): 65 | """Returns the sum of two uint256-ish tuples.""" 66 | a = from_uint(a) 67 | b = from_uint(b) 68 | c = a + b 69 | return to_uint(c) 70 | 71 | 72 | def sub_uint(a, b): 73 | """Returns the difference of two uint256-ish tuples.""" 74 | a = from_uint(a) 75 | b = from_uint(b) 76 | c = a - b 77 | return to_uint(c) 78 | 79 | 80 | def mul_uint(a, b): 81 | """Returns the product of two uint256-ish tuples.""" 82 | a = from_uint(a) 83 | b = from_uint(b) 84 | c = a * b 85 | return to_uint(c) 86 | 87 | 88 | def div_rem_uint(a, b): 89 | """Returns the quotient and remainder of two uint256-ish tuples.""" 90 | a = from_uint(a) 91 | b = from_uint(b) 92 | c = math.trunc(a / b) 93 | m = a % b 94 | return (to_uint(c), to_uint(m)) 95 | 96 | 97 | async def assert_revert(fun, reverted_with=None): 98 | try: 99 | await fun 100 | assert False 101 | except StarkException as err: 102 | _, error = err.args 103 | if reverted_with is not None: 104 | assert reverted_with in error['message'] 105 | 106 | 107 | def assert_event_emitted(tx_exec_info, from_address, name, data): 108 | assert Event( 109 | from_address=from_address, 110 | keys=[get_selector_from_name(name)], 111 | data=data, 112 | ) in tx_exec_info.raw_events 113 | 114 | 115 | def get_contract_def(path): 116 | """Returns the contract definition from the contract path""" 117 | path = contract_path(path) 118 | contract_def = compile_starknet_files( 119 | files=[path], 120 | debug_info=True 121 | ) 122 | return contract_def 123 | 124 | 125 | def cached_contract(state, definition, deployed): 126 | """Returns the cached contract""" 127 | contract = StarknetContract( 128 | state=state, 129 | abi=definition.abi, 130 | contract_address=deployed.contract_address, 131 | deploy_execution_info=deployed.deploy_execution_info 132 | ) 133 | return contract 134 | 135 | 136 | class Signer(): 137 | """ 138 | Utility for sending signed transactions to an Account on Starknet. 139 | 140 | Parameters 141 | ---------- 142 | 143 | private_key : int 144 | 145 | Examples 146 | --------- 147 | Constructing a Signer object 148 | 149 | >>> signer = Signer(1234) 150 | 151 | Sending a transaction 152 | 153 | >>> await signer.send_transaction(account, 154 | account.contract_address, 155 | 'set_public_key', 156 | [other.public_key] 157 | ) 158 | 159 | """ 160 | 161 | def __init__(self, private_key): 162 | self.private_key = private_key 163 | self.public_key = private_to_stark_key(private_key) 164 | 165 | def sign(self, message_hash): 166 | return sign(msg_hash=message_hash, priv_key=self.private_key) 167 | 168 | async def send_transaction(self, account, to, selector_name, calldata, nonce=None, max_fee=0): 169 | return await self.send_transactions(account, [(to, selector_name, calldata)], nonce, max_fee) 170 | 171 | async def send_transactions(self, account, calls, nonce=None, max_fee=0): 172 | if nonce is None: 173 | execution_info = await account.get_nonce().call() 174 | nonce, = execution_info.result 175 | 176 | calls_with_selector = [ 177 | (call[0], get_selector_from_name(call[1]), call[2]) for call in calls] 178 | (call_array, calldata) = from_call_to_call_array(calls) 179 | 180 | message_hash = hash_multicall( 181 | account.contract_address, calls_with_selector, nonce, max_fee) 182 | sig_r, sig_s = self.sign(message_hash) 183 | 184 | return await account.__execute__(call_array, calldata, nonce).invoke(signature=[sig_r, sig_s]) 185 | 186 | 187 | def from_call_to_call_array(calls): 188 | call_array = [] 189 | calldata = [] 190 | for i, call in enumerate(calls): 191 | assert len(call) == 3, "Invalid call parameters" 192 | entry = (call[0], get_selector_from_name( 193 | call[1]), len(calldata), len(call[2])) 194 | call_array.append(entry) 195 | calldata.extend(call[2]) 196 | return (call_array, calldata) 197 | 198 | 199 | def hash_multicall(sender, calls, nonce, max_fee): 200 | hash_array = [] 201 | for call in calls: 202 | call_elements = [call[0], call[1], compute_hash_on_elements(call[2])] 203 | hash_array.append(compute_hash_on_elements(call_elements)) 204 | 205 | message = [ 206 | str_to_felt('StarkNet Transaction'), 207 | sender, 208 | compute_hash_on_elements(hash_array), 209 | nonce, 210 | max_fee, 211 | TRANSACTION_VERSION 212 | ] 213 | return compute_hash_on_elements(message) -------------------------------------------------------------------------------- /src/codefordao/core/Membership.cairo: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | %lang starknet 4 | 5 | from starkware.cairo.common.cairo_builtins import HashBuiltin 6 | from starkware.cairo.common.hash import hash2 7 | from starkware.cairo.common.uint256 import Uint256 8 | from starkware.starknet.common.syscalls import get_caller_address 9 | 10 | from openzeppelin.token.erc721.library import ( 11 | ERC721_name, 12 | ERC721_symbol, 13 | ERC721_balanceOf, 14 | ERC721_ownerOf, 15 | ERC721_getApproved, 16 | ERC721_isApprovedForAll, 17 | ERC721_tokenURI, 18 | ERC721_approve, 19 | ERC721_setApprovalForAll, 20 | ERC721_transferFrom, 21 | ERC721_safeTransferFrom, 22 | ERC721_burn, 23 | ERC721_safeMint, 24 | ERC721_initializer, 25 | ERC721_only_token_owner, 26 | ERC721_setTokenURI, 27 | ) 28 | 29 | from openzeppelin.token.erc721_enumerable.library import ( 30 | ERC721_Enumerable_initializer, 31 | ERC721_Enumerable_totalSupply, 32 | ERC721_Enumerable_tokenByIndex, 33 | ERC721_Enumerable_tokenOfOwnerByIndex, 34 | ERC721_Enumerable_mint, 35 | ERC721_Enumerable_burn, 36 | ERC721_Enumerable_transferFrom, 37 | ERC721_Enumerable_safeTransferFrom 38 | ) 39 | 40 | from openzeppelin.introspection.ERC165 import ERC165 41 | from openzeppelin.security.pausable import Pausable 42 | from immutablex.starknet.access.AccessControl import AccessControl, DEFAULT_ADMIN_ROLE 43 | 44 | from src.codefordao.libraries.structs import ContractAddressType 45 | from src.codefordao.libraries.merkle import merkle_verify 46 | 47 | # 48 | # Constants 49 | # 50 | 51 | const MINTER_ROLE = 'MINTER_ROLE' 52 | const INVITER_ROLE = 'INVITER_ROLE' 53 | 54 | # 55 | # Storage 56 | # 57 | @storage_var 58 | func contract_uri() -> (uri: felt): 59 | end 60 | 61 | @storage_var 62 | func investor(token_id: Uint256) -> (res: felt): 63 | end 64 | 65 | @storage_var 66 | func contracts_addresses(contract_type: ContractAddressType) -> (addr: felt): 67 | end 68 | 69 | @storage_var 70 | func merkle_root() -> (root: felt): 71 | end 72 | 73 | # 74 | # Initializer 75 | # 76 | @constructor 77 | func constructor{ 78 | syscall_ptr: felt*, 79 | pedersen_ptr: HashBuiltin*, 80 | range_check_ptr 81 | }( 82 | name: felt, 83 | symbol: felt, 84 | default_admin: felt 85 | ): 86 | ERC721_initializer(name, symbol) 87 | ERC721_Enumerable_initializer() 88 | AccessControl.initializer(default_admin) 89 | return () 90 | end 91 | 92 | # 93 | # Getters 94 | # 95 | 96 | @view 97 | func totalSupply{ 98 | pedersen_ptr: HashBuiltin*, 99 | syscall_ptr: felt*, 100 | range_check_ptr 101 | }() -> (totalSupply: Uint256): 102 | let (totalSupply: Uint256) = ERC721_Enumerable_totalSupply() 103 | return (totalSupply) 104 | end 105 | 106 | @view 107 | func tokenByIndex{ 108 | pedersen_ptr: HashBuiltin*, 109 | syscall_ptr: felt*, 110 | range_check_ptr 111 | }(index: Uint256) -> (tokenId: Uint256): 112 | let (tokenId: Uint256) = ERC721_Enumerable_tokenByIndex(index) 113 | return (tokenId) 114 | end 115 | 116 | @view 117 | func tokenOfOwnerByIndex{ 118 | pedersen_ptr: HashBuiltin*, 119 | syscall_ptr: felt*, 120 | range_check_ptr 121 | }(owner: felt, index: Uint256) -> (tokenId: Uint256): 122 | let (tokenId: Uint256) = ERC721_Enumerable_tokenOfOwnerByIndex(owner, index) 123 | return (tokenId) 124 | end 125 | 126 | @view 127 | func supportsInterface{ 128 | syscall_ptr: felt*, 129 | pedersen_ptr: HashBuiltin*, 130 | range_check_ptr 131 | }(interfaceId: felt) -> (success: felt): 132 | let (success) = ERC165.supports_interface(interfaceId) 133 | return (success) 134 | end 135 | 136 | @view 137 | func name{ 138 | syscall_ptr: felt*, 139 | pedersen_ptr: HashBuiltin*, 140 | range_check_ptr 141 | }() -> (name: felt): 142 | let (name) = ERC721_name() 143 | return (name) 144 | end 145 | 146 | @view 147 | func symbol{ 148 | syscall_ptr: felt*, 149 | pedersen_ptr: HashBuiltin*, 150 | range_check_ptr 151 | }() -> (symbol: felt): 152 | let (symbol) = ERC721_symbol() 153 | return (symbol) 154 | end 155 | 156 | @view 157 | func balanceOf{ 158 | syscall_ptr: felt*, 159 | pedersen_ptr: HashBuiltin*, 160 | range_check_ptr 161 | }(owner: felt) -> (balance: Uint256): 162 | let (balance) = ERC721_balanceOf(owner) 163 | return (balance) 164 | end 165 | 166 | @view 167 | func ownerOf{ 168 | syscall_ptr: felt*, 169 | pedersen_ptr: HashBuiltin*, 170 | range_check_ptr 171 | }(token_id: Uint256) -> (owner: felt): 172 | let (owner) = ERC721_ownerOf(token_id) 173 | return (owner) 174 | end 175 | 176 | @view 177 | func getApproved{ 178 | syscall_ptr: felt*, 179 | pedersen_ptr: HashBuiltin*, 180 | range_check_ptr 181 | }(token_id: Uint256) -> (approved: felt): 182 | let (approved) = ERC721_getApproved(token_id) 183 | return (approved) 184 | end 185 | 186 | @view 187 | func isApprovedForAll{ 188 | syscall_ptr: felt*, 189 | pedersen_ptr: HashBuiltin*, 190 | range_check_ptr 191 | }(owner: felt, operator: felt) -> (isApproved: felt): 192 | let (isApproved) = ERC721_isApprovedForAll(owner, operator) 193 | return (isApproved) 194 | end 195 | 196 | @view 197 | func tokenURI{ 198 | syscall_ptr: felt*, 199 | pedersen_ptr: HashBuiltin*, 200 | range_check_ptr 201 | }(tokenId: Uint256) -> (tokenURI: felt): 202 | let (tokenURI) = ERC721_tokenURI(tokenId) 203 | return (tokenURI) 204 | end 205 | 206 | @view 207 | func contractURI{ 208 | syscall_ptr: felt*, 209 | pedersen_ptr: HashBuiltin*, 210 | range_check_ptr 211 | }() -> (owner: felt): 212 | let (uri) = contract_uri() 213 | return (uri) 214 | end 215 | 216 | @view 217 | func isInvestor{ 218 | syscall_ptr: felt*, 219 | pedersen_ptr: HashBuiltin*, 220 | range_check_ptr 221 | }(token_id: Uint256) -> (res: felt): 222 | let (res) = investor.read(token_id) 223 | return (res) 224 | end 225 | 226 | @view 227 | func paused{ 228 | syscall_ptr: felt*, 229 | pedersen_ptr: HashBuiltin*, 230 | range_check_ptr 231 | }() -> (paused: felt): 232 | let (paused) = Pausable.is_paused() 233 | return (paused) 234 | end 235 | 236 | @view 237 | func hasRole{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}( 238 | role : felt, account : felt 239 | ) -> (res : felt): 240 | let (res) = AccessControl.has_role(role, account) 241 | return (res) 242 | end 243 | 244 | @view 245 | func getRoleAdmin{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}( 246 | role : felt 247 | ) -> (role_admin : felt): 248 | let (role_admin) = AccessControl.get_role_admin(role) 249 | return (role_admin) 250 | end 251 | 252 | # 253 | # Externals 254 | # 255 | 256 | @external 257 | func setContractURI{ 258 | syscall_ptr: felt*, 259 | pedersen_ptr: HashBuiltin*, 260 | range_check_ptr 261 | }(uri: felt): 262 | AccessControl.only_role(DEFAULT_ADMIN_ROLE) 263 | contract_uri.write(uri) 264 | return () 265 | end 266 | 267 | @external 268 | func setMerkleRoot{ 269 | syscall_ptr: felt*, 270 | pedersen_ptr: HashBuiltin*, 271 | range_check_ptr 272 | }(root: felt): 273 | AccessControl.only_role(INVITER_ROLE) 274 | merkle_root.write(root) 275 | return () 276 | end 277 | 278 | @external 279 | func setupGovernor{ 280 | syscall_ptr: felt*, 281 | pedersen_ptr: HashBuiltin*, 282 | range_check_ptr 283 | }( 284 | governor_addr: felt, 285 | treasury_addr: felt, 286 | share_token_addr: felt, 287 | share_governor_addr: felt 288 | ): 289 | AccessControl.only_role(DEFAULT_ADMIN_ROLE) 290 | contracts_addresses.write(ContractAddressType.treasury, treasury_addr) 291 | contracts_addresses.write(ContractAddressType.governor, governor_addr) 292 | contracts_addresses.write(ContractAddressType.share_token, share_token_addr) 293 | contracts_addresses.write(ContractAddressType.share_governor, share_governor_addr) 294 | return () 295 | end 296 | 297 | @external 298 | func approve{ 299 | syscall_ptr: felt*, 300 | pedersen_ptr: HashBuiltin*, 301 | range_check_ptr 302 | }(to: felt, tokenId: Uint256): 303 | Pausable.assert_not_paused() 304 | ERC721_approve(to, tokenId) 305 | return () 306 | end 307 | 308 | @external 309 | func setApprovalForAll{ 310 | syscall_ptr: felt*, 311 | pedersen_ptr: HashBuiltin*, 312 | range_check_ptr 313 | }(operator: felt, approved: felt): 314 | Pausable.assert_not_paused() 315 | ERC721_setApprovalForAll(operator, approved) 316 | return () 317 | end 318 | 319 | @external 320 | func transferFrom{ 321 | syscall_ptr: felt*, 322 | pedersen_ptr: HashBuiltin*, 323 | range_check_ptr 324 | }(from_: felt, to: felt, tokenId: Uint256): 325 | Pausable.assert_not_paused() 326 | ERC721_Enumerable_transferFrom(from_, to, tokenId) 327 | return () 328 | end 329 | 330 | @external 331 | func safeTransferFrom{ 332 | syscall_ptr: felt*, 333 | pedersen_ptr: HashBuiltin*, 334 | range_check_ptr 335 | }(from_: felt, to: felt, tokenId: Uint256, data_len: felt, data: felt*): 336 | Pausable.assert_not_paused() 337 | ERC721_Enumerable_safeTransferFrom(from_, to, tokenId, data_len, data) 338 | return () 339 | end 340 | 341 | @external 342 | func pause{ 343 | syscall_ptr: felt*, 344 | pedersen_ptr: HashBuiltin*, 345 | range_check_ptr 346 | }(): 347 | AccessControl.only_role(DEFAULT_ADMIN_ROLE) 348 | Pausable._pause() 349 | return () 350 | end 351 | 352 | @external 353 | func unpause{ 354 | syscall_ptr: felt*, 355 | pedersen_ptr: HashBuiltin*, 356 | range_check_ptr 357 | }(): 358 | AccessControl.only_role(DEFAULT_ADMIN_ROLE) 359 | Pausable._unpause() 360 | return () 361 | end 362 | 363 | @external 364 | func burn{ 365 | syscall_ptr: felt*, 366 | pedersen_ptr: HashBuiltin*, 367 | range_check_ptr 368 | }(tokenId: Uint256): 369 | Pausable.assert_not_paused() 370 | ERC721_only_token_owner(tokenId) 371 | ERC721_Enumerable_burn(tokenId) 372 | return () 373 | end 374 | 375 | @external 376 | func mint{ 377 | pedersen_ptr: HashBuiltin*, 378 | syscall_ptr: felt*, 379 | range_check_ptr 380 | }( 381 | proof_len: felt, 382 | proof: felt* 383 | token_id: Uint256 384 | ): 385 | alloc_locals 386 | 387 | Pausable.assert_not_paused() 388 | 389 | let (sender) = get_caller_address() 390 | let local amount = Uint256(0, 1) 391 | let (amount_hash) = hash2{hash_ptr=pedersen_ptr}(amount.low, amount.high) 392 | let (leaf) = hash2{hash_ptr=pedersen_ptr}(sender, amount_hash) 393 | 394 | let (local root) = merkle_root.read() 395 | let (proof_valid) = merkle_verify(leaf, root, proof_len, proof) 396 | 397 | assert proof_valid = 1 398 | 399 | ERC721_Enumerable_mint(sender, tokenId) 400 | return () 401 | end 402 | 403 | @external 404 | func setTokenURI{ 405 | pedersen_ptr: HashBuiltin*, 406 | syscall_ptr: felt*, 407 | range_check_ptr 408 | }(tokenId: Uint256, tokenURI: felt): 409 | AccessControl.only_role(DEFAULT_ADMIN_ROLE) 410 | ERC721_setTokenURI(tokenId, tokenURI) 411 | return () 412 | end 413 | 414 | # 415 | # Access control related functions 416 | # 417 | 418 | @external 419 | func grantRole{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}( 420 | role : felt, 421 | account : felt 422 | ): 423 | AccessControl.grant_role(role, account) 424 | return () 425 | end 426 | 427 | @external 428 | func revokeRole{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}( 429 | role : felt, 430 | account : felt 431 | ): 432 | AccessControl.revoke_role(role, account) 433 | return () 434 | end 435 | --------------------------------------------------------------------------------