├── .github └── workflows │ └── run_tests.yaml ├── .gitignore ├── README.md ├── Scarb.toml ├── artifacts ├── abis │ ├── attributes_registry.json │ ├── auction.json │ ├── auction_onchain.json │ ├── booklet_nft.json │ ├── box_nft.json │ ├── briq.json │ ├── briq_factory.json │ ├── proxy.json │ ├── set_nft.json │ ├── shape_attribute.json │ ├── shape_store.json │ └── shape_store_ducks.json ├── attributes_registry.json ├── auction.json ├── auction_onchain.json ├── auction_onchain_data_goerli.json ├── auction_onchain_data_mainnet.json ├── booklet_nft.json ├── box_nft.json ├── briq.json ├── briq_factory.json ├── proxy.json ├── set_nft.json ├── shape_attribute.json ├── shape_store.json ├── shape_store_ducks.json └── shape_store_zenducks.json ├── briq_protocol ├── __init__.py ├── __main__.py ├── binomial_ifs.py ├── data │ ├── artifacts │ └── contracts ├── gen_shape_check.py ├── generate_auction.py ├── generate_box.py ├── generate_interface.py ├── generate_shape.py └── shape_utils.py ├── contracts ├── attributes_registry.cairo ├── attributes_registry │ ├── attributes.cairo │ └── collections.cairo ├── auction.cairo ├── auction │ ├── auction_lib.cairo │ ├── data.cairo │ └── raffle.cairo ├── auction_onchain.cairo ├── auction_onchain │ ├── allowlist_mainnet.cairo │ ├── allowlist_test.cairo │ ├── allowlist_testnet.cairo │ ├── bid.cairo │ ├── data_link.cairo │ ├── data_mainnet.cairo │ ├── data_test.cairo │ ├── data_testnet.cairo │ └── payment_token.cairo ├── booklet_nft.cairo ├── booklet_nft │ ├── attribute.cairo │ ├── minting.cairo │ └── token_uri.cairo ├── box_nft.cairo ├── box_nft │ ├── data.cairo │ ├── minting.cairo │ ├── token_uri.cairo │ └── unboxing.cairo ├── briq.cairo ├── briq │ ├── balance_enumerability.cairo │ ├── convert_mutate.cairo │ ├── minting.cairo │ └── transferability.cairo ├── briq_factory.cairo ├── ecosystem │ ├── genesis_collection.cairo │ ├── to_attributes_registry.cairo │ ├── to_booklet.cairo │ ├── to_box.cairo │ ├── to_briq.cairo │ ├── to_factory.cairo │ ├── to_migration.cairo │ └── to_set.cairo ├── library_erc1155 │ ├── IERC1155.cairo │ ├── IERC1155_OZ.cairo │ ├── approvals.cairo │ ├── approvals_detailed.cairo │ ├── balance.cairo │ ├── token_uri.cairo │ └── transferability.cairo ├── library_erc721 │ ├── IERC721.cairo │ ├── IERC721_enumerable.cairo │ ├── approvals.cairo │ ├── balance.cairo │ ├── enumerability.cairo │ ├── transferability.cairo │ └── transferability_enum.cairo ├── mocks │ ├── attributes_registry_mock.cairo │ ├── briq_mock.cairo │ ├── set_mock.cairo │ ├── shape_mock.cairo │ └── uint256.cairo ├── set_nft.cairo ├── set_nft │ ├── assembly.cairo │ ├── token_uri.cairo │ └── token_uri_ext.cairo ├── shape │ ├── attribute.cairo │ ├── construction_guards.cairo │ ├── data.cairo │ ├── data_ducks.cairo │ ├── data_zenducks.cairo │ ├── shape_store.cairo │ ├── shape_store_ducks.cairo │ └── shape_store_zenducks.cairo ├── shape_attribute.cairo ├── types.cairo ├── upgrades │ ├── proxy.cairo │ └── upgradable_mixin.cairo ├── utilities │ ├── IERC165.cairo │ ├── Uint256_felt_conv.cairo │ ├── authorization.cairo │ └── token_uri.cairo └── vendor │ ├── cairopen │ ├── binary │ │ ├── README.md │ │ └── bits.cairo │ ├── hash │ │ ├── README.md │ │ └── sha256.cairo │ ├── math │ │ ├── README.md │ │ └── array.cairo │ └── string │ │ ├── ASCII.cairo │ │ ├── README.md │ │ ├── constants.cairo │ │ ├── libs │ │ ├── conversion.cairo │ │ ├── manipulation.cairo │ │ └── storage.cairo │ │ ├── string.cairo │ │ └── utils.cairo │ ├── caistring │ ├── array.cairo │ └── str.cairo │ └── openzeppelin │ ├── access │ └── ownable │ │ └── library.cairo │ ├── security │ └── safemath │ │ └── library.cairo │ ├── token │ ├── erc20 │ │ ├── IERC20.cairo │ │ ├── library.cairo │ │ └── presets │ │ │ ├── ERC20.cairo │ │ │ ├── ERC20Burnable.cairo │ │ │ ├── ERC20Mintable.cairo │ │ │ ├── ERC20Pausable.cairo │ │ │ └── ERC20Upgradeable.cairo │ └── erc721 │ │ ├── IERC721.cairo │ │ ├── IERC721Metadata.cairo │ │ ├── IERC721Receiver.cairo │ │ ├── enumerable │ │ ├── IERC721Enumerable.cairo │ │ ├── library.cairo │ │ └── presets │ │ │ ├── ERC721EnumerableMintableBurnable.cairo │ │ │ └── utils │ │ │ └── ERC721Holder.cairo │ │ ├── library.cairo │ │ └── presets │ │ ├── ERC721MintableBurnable.cairo │ │ └── ERC721MintablePausable.cairo │ ├── upgrades │ ├── library.cairo │ └── presets │ │ └── Proxy.cairo │ └── utils │ └── constants │ └── library.cairo ├── docs ├── GenesisSale.png ├── Readme.md ├── attributes │ ├── AttributesRegistry.png │ └── Readme.md ├── banner.png ├── briq │ └── Readme.md ├── set │ └── Readme.md └── squarebriq.jpg ├── pyproject.toml ├── scripts-old ├── compile.sh ├── deploy.py ├── deploy_briq_factory.sh ├── node.sh ├── setup_contracts.sh ├── setup_devnet_env.sh ├── setup_mainnet_env.sh ├── setup_testnet_env.sh ├── wave2.sh ├── wave_briqmas.sh └── wave_ducks.sh ├── scripts ├── deploy.sh ├── katana_account.json ├── katana_signer.json └── setup_katana.sh ├── src-old ├── attributes_registry.cairo ├── attributes_registry │ ├── attributes.cairo │ └── collections.cairo ├── booklet_nft.cairo ├── booklet_nft │ ├── attribute.cairo │ ├── minting.cairo │ └── token_uri.cairo ├── box_nft.cairo ├── box_nft │ ├── minting.cairo │ ├── token_uri.cairo │ └── unboxing.cairo ├── briq.cairo ├── briq │ ├── balance_enumerability.cairo │ ├── minting.cairo │ └── transferability.cairo ├── briq_factory.cairo ├── constants.cairo ├── ecosystem.cairo ├── ecosystem │ ├── genesis_collection.cairo │ ├── to_attributes_registry.cairo │ ├── to_booklet.cairo │ ├── to_box.cairo │ ├── to_briq.cairo │ └── to_set.cairo ├── lib.cairo ├── library_erc1155.cairo ├── library_erc1155 │ ├── approvals.cairo │ ├── balance.cairo │ └── transferability.cairo ├── library_erc721.cairo ├── library_erc721 │ ├── approvals.cairo │ ├── balance.cairo │ └── transferability.cairo ├── set_nft.cairo ├── set_nft │ ├── assembly.cairo │ └── token_uri.cairo ├── shape.cairo ├── shape │ ├── attribute.cairo │ └── shape_store.cairo ├── temp.cairo ├── temp │ └── Proxy.cairo ├── types.cairo ├── utilities.cairo ├── utilities │ ├── IERC165.cairo │ ├── authorization.cairo │ └── token_uri.cairo └── utils.cairo ├── src ├── attributes │ ├── attribute_group.cairo │ └── attributes.cairo ├── booklet │ └── attribute.cairo ├── box_nft │ └── unboxing.cairo ├── briq_factory.cairo ├── briq_factory │ ├── constants.cairo │ └── models.cairo ├── cumulative_balance.cairo ├── erc │ ├── erc1155 │ │ ├── interface.cairo │ │ ├── internal_trait.cairo │ │ └── models.cairo │ ├── erc721 │ │ ├── interface.cairo │ │ ├── internal_trait.cairo │ │ └── models.cairo │ └── mint_burn.cairo ├── felt_math.cairo ├── lib.cairo ├── migrate.cairo ├── set_nft │ └── assembly.cairo ├── supports_interface.cairo ├── tests │ ├── briq_counter.cairo │ ├── contract_upgrade.cairo │ ├── shapes.cairo │ ├── test_attributes.cairo │ ├── test_box_nft.cairo │ ├── test_briq_factory.cairo │ ├── test_briq_token.cairo │ ├── test_check_fts_and_shape_match.cairo │ ├── test_migrate.cairo │ ├── test_set_nft.cairo │ ├── test_set_nft_1155.cairo │ ├── test_set_nft_multiple.cairo │ ├── test_shape_packing.cairo │ ├── test_truck_shape.cairo │ ├── test_upgradeable.cairo │ ├── test_uri.cairo │ ├── test_utils.cairo │ └── test_world_config.cairo ├── tokens │ ├── booklet_briqmas.cairo │ ├── booklet_ducks.cairo │ ├── booklet_ducks_frens.cairo │ ├── booklet_lil_ducks.cairo │ ├── booklet_starknet_planet.cairo │ ├── box_nft_briqmas.cairo │ ├── box_nft_sp.cairo │ ├── briq_token.cairo │ ├── set_nft.cairo │ ├── set_nft_1155_ducks_frens.cairo │ ├── set_nft_1155_lil_ducks.cairo │ ├── set_nft_briqmas.cairo │ ├── set_nft_ducks.cairo │ └── set_nft_sp.cairo ├── types.cairo ├── uri.cairo └── world_config.cairo ├── tests-old ├── __init__.py ├── attributes_registry_test.cairo ├── attributes_registry_test.py ├── auction_onchain_test.py ├── auction_test.py ├── booklet_factory_perf_test_disabled.py ├── booklet_test.cairo ├── booklet_test.py ├── box_test.py ├── briq_factory_test.cairo ├── briq_factory_test.py ├── briq_impl_test.py ├── cairo1_test.py ├── cairo_project.toml ├── conftest.py ├── genesis_sale_test.py ├── lib.cairo ├── proxy_test.py ├── set_box_assembly_test.py ├── set_impl_test.py ├── shape_check_test.py ├── shape_ducks_test.py ├── shape_test.cairo ├── shape_test.py ├── uint256_test.py └── uri_string.cairo ├── tox.ini └── vs-briq.code-workspace /.github/workflows/run_tests.yaml: -------------------------------------------------------------------------------- 1 | name: run_tests 2 | on: 3 | push: 4 | branches: [ main ] 5 | pull_request: 6 | 7 | jobs: 8 | deploy: 9 | name: "Run tests" 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | - name: Setup python 15 | uses: actions/setup-python@v2 16 | with: 17 | python-version: "3.9" 18 | - run: python -m pip install -r requirements.txt 19 | - run: python -m pytest -k "not integration" 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | __pycache__/ 4 | 5 | !.gitignore 6 | !README.md 7 | !requirements.txt 8 | !setup.py 9 | !tox.ini 10 | 11 | !.github/ 12 | !artifacts/ 13 | !contracts/ 14 | !briq_protocol/ 15 | !docs/ 16 | !tests/ 17 | 18 | !pyproject.toml 19 | 20 | !Scarb.toml 21 | !src 22 | !src-old 23 | !tests-old 24 | 25 | !scripts/ 26 | scripts/* 27 | !scripts/deploy.sh 28 | !scripts/setup_katana.sh 29 | !scripts/katana_account.json 30 | !scripts/katana_signer.json 31 | !scripts-old 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Briq Protocol 2 | 3 | This repository contains the Cairo code for briq StarkNet contracts. 4 | 5 | ## High-level overview of contracts: 6 | 7 | - briq contract: ERC1155-like. Handles briq tokens. briqs have a material, and fungible & NFT briqs may have the same material. The interface is material-based instead of token-id based in an ERC1155. 8 | - set NFT contract: ERC721-like. Handles sets. Essentially a regular ERC721, but handles assembly/disassembly. When assembling, it becomes the owner of the underlying briq tokens, and vice-versa. 9 | - box NFT contract: ERC1155 for Genesis boxes. Can be unboxed, granting some briqs and a booklet. 10 | - booklet NFT contract: ERC1155 for booklets. Acts as an attribute, refers to shapes. 11 | - Attribute registry: handles additional on-chain metadata for sets 12 | - Auction contract: for the Genesis Sale, sells boxes. 13 | - Shape contracts: define 3D shapes for set NFTs. 14 | 15 | See [docs/](docs/) for more information. 16 | 17 | ### Repo structure 18 | 19 | Contracts that actually get compiled are under `contracts/` directly. They have their own subfolder for their specific logic. 20 | `contracts/library_erc721` is the generic 721 implementation I use, which is felt-based. 21 | `contracts/library_erc1155` is the generic 1155 implementation I use, which is felt-based. 22 | `contracts/mocks` is used for testing convenience. 23 | 24 | `generators` contains some Python utility to generate shape data & auction data (and some older dead code). 25 | `tests` contain the tests. 26 | 27 | ## Setup 28 | #### Install python environment (via venv) 29 | ```sh 30 | python3 -m venv venv 31 | source venv/bin/activate 32 | pip3 install -r requirements.txt 33 | pip3 install -e . # For the generators utilities. 34 | ``` 35 | 36 | Compile the target contracts: 37 | ```sh 38 | scripts/compile.sh 39 | ``` 40 | ## Tests 41 | 42 | Note that at the moment proxy_test.py is failing (expectedly, the tests are outdated). 43 | ```sh 44 | pytest 45 | ``` 46 | 47 | ## Deployment 48 | 49 | Deployment is done manually but 95% there to being automatic. 50 | Just configure a wallet using `scripts/setup_testnet_env.sh` (adapt as needed), then run `scripts/setup_contracts.sh`. 51 | You may need to wait for pending blocks occasionally, and beware of 429 too many requests in testnet. 52 | 53 | ## Current mainnet addresses: 54 | 55 | ``` 56 | briq token (ERC1155): 0x00247444a11a98ee7896f9dec18020808249e8aad21662f2fa00402933dce402 57 | Set NFTs (ERC721): 0x01435498bf393da86b4733b9264a86b58a42b31f8d8b8ba309593e5c17847672 58 | Boxes (ERC1155): 0x01e1f972637ad02e0eed03b69304344c4253804e528e1a5dd5c26bb2f23a8139 59 | Booklets (ERC1155): 0x05faa82e2aec811d3a3b14c1f32e9bbb6c9b4fd0cd6b29a823c98c7360019aa4 60 | Attributes Registry: 0x008d4f5b0830bd49a88730133a538ccaca3048ccf2b557114b1076feeff13c11 61 | 62 | Auction: 0x01712e3e3f133b26d65a3c5aaae78e7405dfca0a3cfe725dd57c4941d9474620 (used for Genesis Boxes sale) 63 | Shape attribute: 0x04848d0dd8a296352ba4fe100fed9d6f44cbd0a8d360b7d551d986732a14791a (used to hash booklet shapes) 64 | Onchain Auction: 0x00b9bb7650a88f7e375ae8d31d48b4d4f13c6c34344839837d7dae7ffcdd3df0 (used for Ducks) 65 | ``` 66 | -------------------------------------------------------------------------------- /Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "briq_protocol" 3 | version = "2.0.0" 4 | cairo-version = "2.2.0" 5 | 6 | [cairo] 7 | sierra-replace-ids = true 8 | 9 | [dependencies] 10 | presets = { path = "../origami/presets/" } 11 | 12 | [tool.dojo.world] 13 | name = "briq" 14 | description = "Collect, build and play with briqs, the building blocks of the metaverse." 15 | icon_uri = "file://docs/squarebriq.jpg" 16 | cover_uri = "/Volumes/Samsung_T5/Programming/briq-protocol/docs/banner.png" 17 | website = "https://briq.construction/" 18 | socials.x = "https://twitter.com/briqNFT" 19 | 20 | [[target.dojo]] 21 | build-external-contracts = [ 22 | 'presets::erc1155::erc1155::components::erc_1155_balance', 23 | 'presets::erc1155::erc1155::components::operator_approval', 24 | 'presets::erc721::erc721::components::erc_721_balance', 25 | 'presets::erc721::erc721::components::erc_721_owner', 26 | 'presets::erc721::erc721::components::erc_721_token_approval' 27 | ] 28 | 29 | #[tool.dojo.env] 30 | # rpc_url = "http://localhost:5050/" 31 | # Default account for katana with seed = 0 32 | # account_address = "0x03ee9e18edc71a6df30ac3aca2e0b02a198fbce19b7480a63a0d71cbd76652e0" 33 | # private_key = "0x0300001800000000300000180000000000030000000000003006001800006600" 34 | 35 | # keystore_password = "password" 36 | # keystore_path = "../keystore.json" 37 | -------------------------------------------------------------------------------- /artifacts/abis/proxy.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "data": [ 4 | { 5 | "name": "implementation", 6 | "type": "felt" 7 | } 8 | ], 9 | "keys": [], 10 | "name": "Upgraded", 11 | "type": "event" 12 | }, 13 | { 14 | "data": [ 15 | { 16 | "name": "previousAdmin", 17 | "type": "felt" 18 | }, 19 | { 20 | "name": "newAdmin", 21 | "type": "felt" 22 | } 23 | ], 24 | "keys": [], 25 | "name": "AdminChanged", 26 | "type": "event" 27 | }, 28 | { 29 | "inputs": [ 30 | { 31 | "name": "admin", 32 | "type": "felt" 33 | }, 34 | { 35 | "name": "implentation_hash", 36 | "type": "felt" 37 | } 38 | ], 39 | "name": "constructor", 40 | "outputs": [], 41 | "type": "constructor" 42 | }, 43 | { 44 | "inputs": [ 45 | { 46 | "name": "selector", 47 | "type": "felt" 48 | }, 49 | { 50 | "name": "calldata_size", 51 | "type": "felt" 52 | }, 53 | { 54 | "name": "calldata", 55 | "type": "felt*" 56 | } 57 | ], 58 | "name": "__default__", 59 | "outputs": [ 60 | { 61 | "name": "retdata_size", 62 | "type": "felt" 63 | }, 64 | { 65 | "name": "retdata", 66 | "type": "felt*" 67 | } 68 | ], 69 | "type": "function" 70 | }, 71 | { 72 | "inputs": [ 73 | { 74 | "name": "selector", 75 | "type": "felt" 76 | }, 77 | { 78 | "name": "calldata_size", 79 | "type": "felt" 80 | }, 81 | { 82 | "name": "calldata", 83 | "type": "felt*" 84 | } 85 | ], 86 | "name": "__l1_default__", 87 | "outputs": [], 88 | "type": "l1_handler" 89 | } 90 | ] 91 | -------------------------------------------------------------------------------- /briq_protocol/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briqNFT/briq-protocol/5db5f812812b3039e94ecce96506fe8dd94a1c69/briq_protocol/__init__.py -------------------------------------------------------------------------------- /briq_protocol/__main__.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | from .generate_auction import generate_auction 4 | from .generate_box import generate_box 5 | from .generate_interface import generate 6 | 7 | parser = argparse.ArgumentParser(description='Generate contracts.') 8 | parser.add_argument('--box', help='Generate the box contract', action="store_true") 9 | parser.add_argument('--auction', help='Generate the auction contract', action="store_true") 10 | parser.add_argument('--source', help='The name of the source contract.') 11 | args = parser.parse_args() 12 | 13 | if args.box: 14 | with open('contracts/box_nft/data.cairo', 'w') as f: 15 | f.write(generate_box()) 16 | 17 | if args.auction: 18 | with open('contracts/auction/data.cairo', 'w') as f: 19 | f.write(generate_auction()) 20 | 21 | if args.source: 22 | generate(args.source, f"contracts/{args.source}_interface.cairo") 23 | -------------------------------------------------------------------------------- /briq_protocol/binomial_ifs.py: -------------------------------------------------------------------------------- 1 | from briq_protocol.gen_shape_check import ShapeItem, generate_shape_check 2 | 3 | HEADER = """ 4 | // This contract is only declared, and call via LibraryDispatcher & class_hash 5 | #[starknet::contract] 6 | mod shapes_verifier { 7 | use array::{SpanTrait, ArrayTrait}; 8 | use option::OptionTrait; 9 | 10 | // Copied from briq_protocol to keep this simple. 11 | #[derive(Copy, Drop, Serde,)] 12 | struct FTSpec { 13 | token_id: felt252, 14 | qty: u128, 15 | } 16 | 17 | #[derive(Copy, Drop, Serde, Store)] 18 | struct PackedShapeItem { 19 | color_material: felt252, 20 | x_y_z: felt252, 21 | } 22 | 23 | #[storage] 24 | struct Storage {} 25 | 26 | """ 27 | 28 | 29 | def generate_binary_search_function(nft_ids, check_generator): 30 | """ 31 | :param nft_ids: List of NFT IDs, sorted. 32 | :param data_checks: List of code snippets for each NFT. Must be the same length as nft_ids. 33 | :return: Contract function string. 34 | """ 35 | def recursive_search(low, high): 36 | if low > high: 37 | return "" # or some other default action 38 | 39 | mid = (low + high) // 2 40 | 41 | if low == high: 42 | return f"if attribute_id == {nft_ids[mid]} {{\n {check_generator(nft_ids[mid])}\n return;\n}}" 43 | 44 | return f""" 45 | if attribute_id == {nft_ids[mid]} {{ 46 | {check_generator(nft_ids[mid])} 47 | }} else if attribute_id < {nft_ids[mid]} {{ 48 | {recursive_search(low, mid - 1)} 49 | }} else {{ 50 | {recursive_search(mid + 1, high)} 51 | }} 52 | """ 53 | 54 | return f""" 55 | #[external(v0)] 56 | fn verify_shape( 57 | self: @ContractState, attribute_id: u64, mut shape: Span, mut fts: Span 58 | ) {{ 59 | {recursive_search(0, len(nft_ids) - 1)} 60 | assert(false, 'bad attribute ID'); 61 | }} 62 | """ 63 | 64 | 65 | def shape_check(index): 66 | return generate_shape_check([ 67 | ShapeItem(0, 0, 0, "#ffaaff", 1), 68 | ShapeItem(0, 1, 0, "#ffaaff", 0), 69 | ]) 70 | 71 | # print(HEADER + generate_binary_search_function([1, 2, 3, 10, 200], shape_check) + "\n}") 72 | -------------------------------------------------------------------------------- /briq_protocol/data/artifacts: -------------------------------------------------------------------------------- 1 | ../../artifacts -------------------------------------------------------------------------------- /briq_protocol/data/contracts: -------------------------------------------------------------------------------- 1 | ../../contracts -------------------------------------------------------------------------------- /briq_protocol/gen_shape_check.py: -------------------------------------------------------------------------------- 1 | 2 | from dataclasses import dataclass 3 | from typing import Dict 4 | 5 | ANY_MATERIAL_ANY_COLOR = 0 6 | 7 | def generate_shape_check(shape): 8 | materials = {} 9 | for shapeItem in shape: 10 | if shapeItem.material == ANY_MATERIAL_ANY_COLOR: 11 | continue 12 | if shapeItem.material not in materials: 13 | materials[shapeItem.material] = 0 14 | materials[shapeItem.material] += 1 15 | return f""" 16 | assert(shape.len() == {len(shape)}, 'bad shape length'); 17 | let mut ftsum = 0; 18 | loop {{ 19 | if fts.len() == 0 {{ 20 | break; 21 | }} 22 | ftsum = ftsum + *(fts.pop_front().unwrap().qty); 23 | }}; 24 | assert(ftsum == shape.len().into(), 'bad fts spec'); 25 | """ + '\n'.join([item_check(item) for item in shape]) 26 | 27 | 28 | def item_check(item): 29 | out = [ 30 | " let shapeItem = shape.pop_front().unwrap();", 31 | ] 32 | if item.material != ANY_MATERIAL_ANY_COLOR: 33 | out.append(f"assert(shapeItem.color_material == @{item.color_material}, 'bad shape item');") 34 | out.append(f"assert(shapeItem.x_y_z == @{item.x_y_z}, 'bad shape item');") 35 | return "\n ".join(out) 36 | 37 | 38 | @dataclass 39 | class ShapeItem: 40 | x: int 41 | y: int 42 | z: int 43 | color: str 44 | material: int 45 | 46 | @property 47 | def color_material(self): 48 | return int.from_bytes(self.color.encode(), 'big') * 2**64 + self.material 49 | 50 | @property 51 | def x_y_z(self): 52 | return (self.x + 2**31) * 2**64 + (self.y + 2**31) * 2**32 + (self.z + 2**31) 53 | -------------------------------------------------------------------------------- /briq_protocol/generate_auction.py: -------------------------------------------------------------------------------- 1 | auction_data = { 2 | 1: { 3 | "box_token_id": 0xcafe, 4 | "quantity": 10, 5 | "auction_start": 198, 6 | "auction_duration": 24 * 60 * 60, 7 | "initial_price": 2, 8 | }, 9 | 2: { 10 | "box_token_id": 0xfade, 11 | "quantity": 20, 12 | "auction_start": 198, 13 | "auction_duration": 24 * 60 * 60, 14 | "initial_price": 2, 15 | } 16 | } 17 | 18 | box_address = 0xcafe 19 | erc20_address = 0xfade 20 | 21 | def generate_auction(box_address=box_address, erc20_address=box_address, auction_data=auction_data): 22 | lines = [] 23 | 24 | lines.append(f"const box_address = {box_address};") 25 | lines.append(f"const erc20_address = {erc20_address};") 26 | 27 | lines.append("auction_data_start:") 28 | i = 1 29 | for key in auction_data: 30 | if key != i: 31 | print("Bad auction_data") 32 | raise 33 | lines.append(f'dw {auction_data[key]["box_token_id"]};') 34 | lines.append(f'dw {auction_data[key]["quantity"]};') 35 | lines.append(f'dw {auction_data[key]["auction_start"]};') 36 | lines.append(f'dw {auction_data[key]["auction_duration"]};') 37 | lines.append(f'dw {auction_data[key]["initial_price"]};') 38 | i += 1 39 | lines.append("auction_data_end:") 40 | 41 | return '%lang starknet\n' + '\n'.join(lines) + '\n' 42 | -------------------------------------------------------------------------------- /briq_protocol/generate_box.py: -------------------------------------------------------------------------------- 1 | 2 | briq_data = { 3 | 1: { 4 | 0x1: 54 5 | }, 6 | 2: { 7 | 0x1: 20, 8 | 0x3: 10 9 | } 10 | } 11 | 12 | shape_data = { 13 | 0x1: "0xcafe", 14 | 0x2: "0xdead", 15 | } 16 | 17 | booklet_address = 0xcafe 18 | briq_address = 0xdead 19 | collection_id = 0xfafa 20 | 21 | def generate_box( 22 | briq_data=briq_data, 23 | shape_data=shape_data): 24 | lines = [] 25 | 26 | lines.append("briq_data_start:") 27 | i = 1 28 | for key in briq_data: 29 | if key != i: 30 | print("Bad briq_data") 31 | raise 32 | lines.append(f'dw {briq_data[key][0x1] if 0x1 in briq_data[key] else 0}; // Box #{i}') 33 | lines.append(f'dw {briq_data[key][0x3] if 0x3 in briq_data[key] else 0};') 34 | lines.append(f'dw {briq_data[key][0x4] if 0x4 in briq_data[key] else 0};') 35 | lines.append(f'dw {briq_data[key][0x5] if 0x5 in briq_data[key] else 0};') 36 | lines.append(f'dw {briq_data[key][0x6] if 0x6 in briq_data[key] else 0};') 37 | i += 1 38 | lines.append("briq_data_end:") 39 | lines.append("shape_data_start:") 40 | j = 1 41 | for key in shape_data: 42 | if key != j: 43 | print("Bad shape_data") 44 | raise 45 | lines.append(f'dw {shape_data[key]}; // Box #{i}') 46 | j += 1 47 | if j != i: 48 | print("Bad shape/briq data match") 49 | raise 50 | lines.append("shape_data_end:") 51 | 52 | return '%lang starknet\n' + '\n'.join(lines) + '\n' 53 | -------------------------------------------------------------------------------- /briq_protocol/generate_interface.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | def make_func(name, inputs, outputs, mutability): 4 | # For now, pass all hints, and I'll manually drop those that aren't needed. 5 | header = f""" 6 | @{mutability} 7 | func {name[0:-1]}{{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, bitwise_ptr: BitwiseBuiltin*, range_check_ptr 8 | }} ({', '.join([f"{inp['name']}: {inp['type']}" for inp in inputs])}) -> ({', '.join([f"{outp['name']}: {outp['type']}" for outp in outputs])}):""" 9 | # There are outputs: store them. 10 | if len(outputs) > 0: 11 | return header + f""" 12 | let ({', '.join([outp['name'] for outp in outputs])}) = {name}({', '.join([inp['name'] for inp in inputs])}) 13 | return ({', '.join([outp['name'] for outp in outputs])}) 14 | end""" 15 | else: 16 | return header + f""" 17 | {name}({', '.join([inp['name'] for inp in inputs])}) 18 | return () 19 | end""" 20 | 21 | 22 | def generate(input_contract, output_path): 23 | abi_path = f"artifacts/abis/{input_contract}.json" 24 | abi = json.load(open(abi_path, "r")) 25 | 26 | codeparts = [] 27 | 28 | imports = [] 29 | 30 | structs = [] 31 | for part in abi: 32 | if part["type"] == "struct" and part["name"] != 'Uint256': 33 | structs.append(part["name"]) 34 | 35 | if part["type"] != "function": 36 | continue 37 | if "stateMutability" not in part: 38 | codeparts.append(make_func(part["name"], part["inputs"], part["outputs"], "external")) 39 | else: 40 | codeparts.append(make_func(part["name"], part["inputs"], part["outputs"], part["stateMutability"])) 41 | imports.append(part["name"]) 42 | 43 | with open(output_path, "w") as f: 44 | 45 | f.write(""" 46 | %lang starknet 47 | 48 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin, BitwiseBuiltin 49 | """) 50 | f.write(f"from contracts.{input_contract} import (\n\t" + ',\n\t'.join(imports) + '\n)\n') 51 | 52 | f.write("from contracts.types import (\n\t" + ',\n\t'.join(structs) + '\n)\n') 53 | 54 | for part in codeparts: 55 | f.write(part) 56 | f.write("\n") 57 | print("Wrote to ", output_path) 58 | -------------------------------------------------------------------------------- /briq_protocol/generate_shape.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple 2 | from .shape_utils import to_shape_data 3 | 4 | # Index_start is usually 1 because the token 0 generally doesn't exist and this makes things neater. 5 | # Warning: shapes isn't sorted (because tests pass broken inputs on purpose) 6 | def generate_shape_code(shapes: list[Tuple[list, list]], index_start: int = 1): 7 | newline = '\n' 8 | shape_offsets = ["dw 0;"] 9 | nft_offsets = ["dw 0;"] 10 | data_shapes = [] 11 | data_nfts = [] 12 | cum_shape = 0 13 | cum_nft = 0 14 | for shape in shapes: 15 | cum_shape += len(shape[0]) 16 | cum_nft += len(shape[1]) 17 | shape_offsets.append(f"dw {cum_shape};") 18 | nft_offsets.append(f"dw {cum_nft};") 19 | for shape_data in shape[0]: 20 | data_shapes.append(to_shape_data(*shape_data)) 21 | for nft_data in shape[1]: 22 | data_nfts.append(f"dw {hex(nft_data)};") 23 | 24 | return f""" 25 | %lang starknet 26 | 27 | const INDEX_START = {index_start}; 28 | 29 | shape_offset_cumulative: 30 | {newline.join(shape_offsets)} 31 | shape_offset_cumulative_end: 32 | 33 | shape_data: 34 | {newline.join(data_shapes)} 35 | shape_data_end: 36 | 37 | nft_offset_cumulative: 38 | {newline.join(nft_offsets)} 39 | nft_offset_cumulative_end: 40 | 41 | nft_data: 42 | {newline.join(data_nfts)} 43 | nft_data_end: 44 | """ 45 | -------------------------------------------------------------------------------- /briq_protocol/shape_utils.py: -------------------------------------------------------------------------------- 1 | from starkware.cairo.lang.compiler.test_utils import short_string_to_felt 2 | 3 | def compress_shape_item(color: str, material: int, x: int, y: int, z: int, has_token_id: bool = False): 4 | if material < 0 or material >= 2 ** 64: 5 | raise Exception("Material must be between 0 and 2^64") 6 | 7 | if color == 'any_color_any_material': 8 | color_nft_material = 0 9 | else: 10 | if len(color) != 7: 11 | raise Exception("Color must be formatted like '#001122'") 12 | color_nft_material = short_string_to_felt(color) * (2 ** 136) + (has_token_id) * (2 ** 128) + material 13 | 14 | if x <= -2**63 or x >= 2**63 or y <= -2**63 or y >= 2**63 or z <= -2**63 or z >= 2**63: 15 | raise Exception("The shape contract currently cannot support positions beyond 2^63 in any direction") 16 | 17 | x_y_z = to_storage_form(x) * 2 ** 128 + to_storage_form(y) * 2 ** 64 + to_storage_form(z) 18 | return (color_nft_material, x_y_z) 19 | 20 | # I want to preserve ordering in felt, so just add 2**63. 0 becomes -2**63 + 1, 2**64 - 1 becomes 2**63 - 1 21 | def to_storage_form(v): 22 | return v + 0x8000000000000000 23 | 24 | def from_storage_form(v): 25 | return v - 0x8000000000000000 26 | 27 | # NB -> This can't actually tell what the NFT is, since that depends on other metadata 28 | def uncompress_shape_item(col_nft_mat: int, x_y_z: int): 29 | color: int = col_nft_mat // 2 ** 136 30 | has_token_id: bool = bool(col_nft_mat & (2**128)) 31 | mat: int = col_nft_mat & 0xffffffffffffffff 32 | x: int = from_storage_form(x_y_z // 2 ** 128) 33 | y: int = from_storage_form((x_y_z // 2 ** 64) & 0xffffffffffffffff) 34 | z: int = from_storage_form(x_y_z & 0xffffffffffffffff) 35 | return color.to_bytes(7, 'big').decode('ascii'), mat, x, y, z, has_token_id 36 | 37 | def to_shape_data(color: str, material: int, x: int, y: int, z: int, has_nft: bool = False): 38 | col_nft_mat, xyz = compress_shape_item(color, material, x, y, z, has_nft) 39 | return f"""dw {col_nft_mat}; // {color} {material} {has_nft} 40 | dw {xyz}; // {x} {y} {z}""" 41 | -------------------------------------------------------------------------------- /contracts/attributes_registry.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin, BitwiseBuiltin 4 | 5 | from contracts.upgrades.upgradable_mixin import ( 6 | getAdmin_, 7 | getImplementation_, 8 | upgradeImplementation_, 9 | setRootAdmin_, 10 | ) 11 | 12 | from contracts.ecosystem.to_set import ( 13 | getSetAddress_, 14 | setSetAddress_, 15 | ) 16 | 17 | from contracts.attributes_registry.collections import ( 18 | create_collection_, 19 | increase_attribute_balance_, 20 | ) 21 | 22 | from contracts.attributes_registry.attributes import ( 23 | assign_attribute, 24 | remove_attribute, 25 | assign_attributes, 26 | remove_attributes, 27 | has_attribute, 28 | total_balance, 29 | token_uri, 30 | ) 31 | -------------------------------------------------------------------------------- /contracts/auction.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from contracts.upgrades.upgradable_mixin import ( 4 | getAdmin_, 5 | getImplementation_, 6 | upgradeImplementation_, 7 | setRootAdmin_, 8 | ) 9 | 10 | from contracts.auction.auction_lib import ( 11 | make_bid, 12 | close_auction, 13 | get_auction_data, 14 | transfer_funds, 15 | get_price, 16 | ) 17 | 18 | 19 | from contracts.ecosystem.to_box import ( 20 | getBoxAddress_, 21 | setBoxAddress_, 22 | ) 23 | 24 | -------------------------------------------------------------------------------- /contracts/auction/data.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | const box_address = 0x0; 3 | const erc20_address = 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7; 4 | auction_data_start: 5 | dw 1; 6 | dw 150; 7 | dw 1669114800; 8 | dw 1728000; 9 | dw 50000000000000000; 10 | dw 2; 11 | dw 50; 12 | dw 1669114800; 13 | dw 1728000; 14 | dw 200000000000000000; 15 | dw 3; 16 | dw 5; 17 | dw 1669114800; 18 | dw 1728000; 19 | dw 500000000000000000; 20 | dw 4; 21 | dw 150; 22 | dw 1669287600; 23 | dw 1728000; 24 | dw 50000000000000000; 25 | dw 5; 26 | dw 50; 27 | dw 1669287600; 28 | dw 1728000; 29 | dw 200000000000000000; 30 | dw 6; 31 | dw 5; 32 | dw 1669287600; 33 | dw 1728000; 34 | dw 500000000000000000; 35 | dw 7; 36 | dw 150; 37 | dw 1669460400; 38 | dw 1728000; 39 | dw 50000000000000000; 40 | dw 8; 41 | dw 50; 42 | dw 1669460400; 43 | dw 1728000; 44 | dw 200000000000000000; 45 | dw 9; 46 | dw 5; 47 | dw 1669460400; 48 | dw 1728000; 49 | dw 500000000000000000; 50 | auction_data_end: 51 | -------------------------------------------------------------------------------- /contracts/auction/raffle.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | // Need: 4 | // - Balance of token per user. 5 | // - Way to buy N tokens 6 | // - A reveal function 7 | 8 | 9 | @storage_var 10 | func _balance(owner: felt) -> (balance: felt) { 11 | } 12 | 13 | 14 | func _increaseBalance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 15 | owner: felt, number: felt, 16 | ) { 17 | let (balance) = _balance.read(owner); 18 | with_attr error_message("Mint would overflow balance") { 19 | assert_lt_felt(balance, balance + number); 20 | } 21 | _balance.write(owner, balance + number); 22 | return (); 23 | } 24 | 25 | func _decreaseBalance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 26 | owner: felt, number: felt, 27 | ) { 28 | let (balance) = _balance.read(owner); 29 | with_attr error_message("Insufficient balance") { 30 | assert_lt_felt(balance - number, balance); 31 | } 32 | _balance.write(owner, balance - number); 33 | return (); 34 | } 35 | 36 | 37 | 38 | @external 39 | func buy_tokens{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 40 | owner: felt, 41 | number: felt, 42 | ) { 43 | // Take token from contract address to player. 44 | } 45 | 46 | 47 | 48 | @external 49 | func reveal_raffle{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 50 | owners_len: felt, 51 | owners: felt*, 52 | ) { 53 | onlyAdmin_(); 54 | // Algo: generate a random-ish value, modulo it LIST_LEN, then transfer the corresponding set. 55 | // If the set is not owned by this contract, then it has already been raffled. 56 | // In that case, try the next one, then the next one, wrapping around at the start if needed. 57 | } 58 | -------------------------------------------------------------------------------- /contracts/auction_onchain.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from contracts.upgrades.upgradable_mixin import ( 4 | getAdmin_, 5 | getImplementation_, 6 | upgradeImplementation_, 7 | setRootAdmin_, 8 | ) 9 | 10 | from contracts.auction_onchain.payment_token import ( 11 | getPaymentAddress_, 12 | setPaymentAddress_, 13 | ) 14 | 15 | from contracts.auction_onchain.data_link import ( 16 | getDataHash_, 17 | setDataHash_, 18 | ) 19 | 20 | from contracts.auction_onchain.bid import ( 21 | get_auction_data, 22 | make_bids, 23 | make_bid, 24 | settle_auctions, 25 | transfer_funds, 26 | ) 27 | 28 | from contracts.ecosystem.to_set import ( 29 | getSetAddress_, 30 | setSetAddress_, 31 | ) 32 | -------------------------------------------------------------------------------- /contracts/auction_onchain/allowlist_testnet.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin 4 | 5 | from starkware.cairo.common.find_element import search_sorted 6 | from starkware.cairo.common.registers import get_label_location 7 | 8 | from starkware.starknet.common.syscalls import ( 9 | get_caller_address, 10 | ) 11 | 12 | from contracts.utilities.authorization import _onlyAdmin 13 | 14 | list_start: 15 | dw 0x00439d7ed01976d4633667dce210aa880aeadc85e6d3d621eb7b87659df54984; 16 | list_end: 17 | 18 | func _onlyAllowed{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 19 | ) { 20 | alloc_locals; 21 | let (start) = get_label_location(list_start); 22 | let (end) = get_label_location(list_end); 23 | let (caller) = get_caller_address(); 24 | let (elm_ptr: felt*, success: felt) = search_sorted(cast(start, felt*), 1, end - start, caller); 25 | 26 | with_attr error_message("Caller is not in the allow list") { 27 | if (success == 1) { 28 | return (); 29 | } 30 | // Allow admins so they can settle the auction. 31 | _onlyAdmin(); 32 | } 33 | 34 | return (); 35 | } 36 | -------------------------------------------------------------------------------- /contracts/auction_onchain/data_link.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin 4 | 5 | from contracts.utilities.authorization import _onlyAdmin 6 | 7 | struct AuctionData { 8 | token_id: felt, // The actual token ID being bid on 9 | minimum_bid: felt, // Minimum bid in WEI 10 | bid_growth_factor: felt, // per mil minimum increase over current bid 11 | auction_start_date: felt, // timestamp in seconds 12 | auction_duration: felt, // in seconds 13 | } 14 | 15 | @contract_interface 16 | namespace IDataContract { 17 | func get_auction_data( 18 | auction_id: felt 19 | ) -> ( 20 | data: AuctionData, 21 | ){ 22 | } 23 | } 24 | 25 | @storage_var 26 | func _data_hash() -> (hash: felt) { 27 | } 28 | 29 | @view 30 | func getDataHash_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( 31 | hash: felt 32 | ) { 33 | let (value) = _data_hash.read(); 34 | return (value,); 35 | } 36 | 37 | @external 38 | func setDataHash_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 39 | hash: felt 40 | ) { 41 | _onlyAdmin(); 42 | _data_hash.write(hash); 43 | return (); 44 | } 45 | -------------------------------------------------------------------------------- /contracts/auction_onchain/data_test.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin 4 | 5 | from contracts.auction_onchain.data_link import AuctionData 6 | 7 | from contracts.auction_onchain.allowlist_test import _onlyAllowed 8 | 9 | @view 10 | func get_auction_data{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 11 | auction_id: felt, 12 | ) -> ( 13 | data: AuctionData, 14 | ){ 15 | // For convenience, the whitelist check is done here. 16 | // This makes it easier to change on a per-network basis, and it's called as part of making bids anyways. 17 | _onlyAllowed(); 18 | 19 | // Special case for testing 20 | if (auction_id == 20) { 21 | let data = AuctionData( 22 | token_id=0, 23 | minimum_bid=0, 24 | bid_growth_factor=0, 25 | auction_start_date=0, 26 | auction_duration=0, 27 | ); 28 | return (data,); 29 | } 30 | 31 | let data = AuctionData( 32 | token_id=auction_id, 33 | minimum_bid=1210, 34 | bid_growth_factor=10, 35 | auction_start_date=100, 36 | auction_duration=100, 37 | ); 38 | return (data,); 39 | } 40 | 41 | -------------------------------------------------------------------------------- /contracts/auction_onchain/payment_token.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin 4 | 5 | from contracts.utilities.authorization import _onlyAdmin 6 | 7 | @storage_var 8 | func _payment_address() -> (address: felt) { 9 | } 10 | 11 | @view 12 | func getPaymentAddress_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( 13 | address: felt 14 | ) { 15 | let (value) = _payment_address.read(); 16 | return (value,); 17 | } 18 | 19 | @external 20 | func setPaymentAddress_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 21 | address: felt 22 | ) { 23 | _onlyAdmin(); 24 | _payment_address.write(address); 25 | return (); 26 | } 27 | -------------------------------------------------------------------------------- /contracts/booklet_nft.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin, BitwiseBuiltin 4 | 5 | from contracts.upgrades.upgradable_mixin import ( 6 | getAdmin_, 7 | getImplementation_, 8 | upgradeImplementation_, 9 | setRootAdmin_, 10 | ) 11 | 12 | from contracts.ecosystem.to_attributes_registry import ( 13 | getAttributesRegistryAddress_, 14 | setAttributesRegistryAddress_, 15 | ) 16 | 17 | from contracts.ecosystem.to_box import ( 18 | getBoxAddress_, 19 | setBoxAddress_, 20 | ) 21 | 22 | from contracts.booklet_nft.minting import ( 23 | mint_ 24 | ) 25 | 26 | from contracts.booklet_nft.token_uri import ( 27 | get_shape_contract_, 28 | get_shape_, 29 | tokenURI_, 30 | ) 31 | 32 | from contracts.booklet_nft.attribute import ( 33 | assign_attribute, 34 | remove_attribute, 35 | ) 36 | 37 | from contracts.library_erc1155.IERC1155 import ( 38 | setApprovalForAll_, 39 | isApprovedForAll_, 40 | balanceOf_, 41 | balanceOfBatch_, 42 | safeTransferFrom_, 43 | supportsInterface, 44 | ) 45 | 46 | from contracts.library_erc1155.IERC1155_OZ import ( 47 | setApprovalForAll, 48 | isApprovedForAll, 49 | balanceOf, 50 | balanceOfBatch, 51 | safeTransferFrom, 52 | ) 53 | 54 | from starkware.cairo.common.uint256 import Uint256 55 | from contracts.utilities.Uint256_felt_conv import _uint_to_felt, _felt_to_uint 56 | 57 | // URI is custom 58 | // Not quite OZ compliant -> I return a list of felt. 59 | @view 60 | func uri{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, bitwise_ptr: BitwiseBuiltin*, range_check_ptr}( 61 | id: Uint256 62 | ) -> (uri_len: felt, uri: felt*) { 63 | let (tid) = _uint_to_felt(id); 64 | let (ul, u) = tokenURI_(token_id=tid); 65 | return (ul, u); 66 | } 67 | -------------------------------------------------------------------------------- /contracts/booklet_nft/minting.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin 4 | from starkware.cairo.common.math import assert_lt_felt 5 | from starkware.starknet.common.syscalls import get_caller_address 6 | from starkware.cairo.common.math_cmp import is_le_felt 7 | 8 | from contracts.library_erc1155.transferability import ERC1155_transferability 9 | from contracts.library_erc1155.balance import ERC1155_balance 10 | 11 | from contracts.booklet_nft.token_uri import _shape_contract 12 | 13 | from contracts.ecosystem.to_box import _box_address 14 | from contracts.ecosystem.genesis_collection import DUCKS_COLLECTION 15 | from contracts.utilities.authorization import _onlyAdminAnd 16 | 17 | @external 18 | func mint_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 19 | owner: felt, token_id: felt, shape_contract: felt 20 | ) { 21 | alloc_locals; 22 | ERC1155_balance._increaseBalance(owner, token_id, 1); 23 | 24 | _shape_contract.write(token_id, shape_contract); 25 | 26 | // Can only be minted by the box contract or an admin of the contract. 27 | let (caller) = get_caller_address(); 28 | if (caller == 0x02ef9325a17d3ef302369fd049474bc30bfeb60f59cca149daa0a0b7bcc278f8) { 29 | // Allow OutSmth to mint ducks. 30 | let tid = (token_id - DUCKS_COLLECTION) / 2**192; 31 | // Check this is below an arbitrary low number to make sure the range is correct 32 | assert_lt_felt(tid, 10000); 33 | 34 | ERC1155_transferability._onTransfer(caller, 0, owner, token_id, 1); 35 | return (); 36 | } else { 37 | let (box_addr) = _box_address.read(); 38 | _onlyAdminAnd(box_addr); 39 | 40 | ERC1155_transferability._onTransfer(caller, 0, owner, token_id, 1); 41 | return (); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /contracts/booklet_nft/token_uri.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin, BitwiseBuiltin 4 | 5 | from contracts.types import ShapeItem 6 | 7 | from contracts.ecosystem.genesis_collection import GENESIS_COLLECTION 8 | 9 | from contracts.utilities.token_uri import TokenURIHelpers 10 | 11 | @contract_interface 12 | namespace IShapeContract { 13 | func shape_(global_index: felt) -> (shape_len: felt, shape: ShapeItem*, nfts_len: felt, nfts: felt*) { 14 | } 15 | } 16 | 17 | @storage_var 18 | func _shape_contract(token_id: felt) -> (contract_address: felt) { 19 | } 20 | 21 | @view 22 | func get_shape_contract_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 23 | token_id: felt 24 | ) -> (address: felt) { 25 | let (addr) = _shape_contract.read(token_id); 26 | return (addr,); 27 | } 28 | 29 | @view 30 | func get_shape_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 31 | token_id: felt 32 | ) -> (shape_len: felt, shape: ShapeItem*, nfts_len: felt, nfts: felt*) { 33 | let (addr) = _shape_contract.read(token_id); 34 | let (a, b, c, d) = IShapeContract.library_call_shape_(addr, (token_id - GENESIS_COLLECTION) / 2**192); 35 | return (a, b, c, d); 36 | } 37 | 38 | data_uri_start: 39 | dw 'https://api.briq.construction'; 40 | dw '/v1/uri/booklet/'; 41 | dw 'starknet-mainnet/'; 42 | dw '.json'; 43 | 44 | @view 45 | func tokenURI_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 46 | token_id: felt 47 | ) -> (uri_len: felt, uri: felt*) { 48 | return TokenURIHelpers._getUrl(token_id, data_uri_start); 49 | } 50 | -------------------------------------------------------------------------------- /contracts/box_nft.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin 3 | 4 | from contracts.upgrades.upgradable_mixin import ( 5 | getAdmin_, 6 | getImplementation_, 7 | upgradeImplementation_, 8 | setRootAdmin_, 9 | ) 10 | 11 | from contracts.ecosystem.to_briq import ( 12 | getBriqAddress_, 13 | setBriqAddress_, 14 | ) 15 | 16 | from contracts.ecosystem.to_booklet import ( 17 | getBookletAddress_, 18 | setBookletAddress_, 19 | ) 20 | 21 | from contracts.box_nft.minting import ( 22 | mint_, 23 | ) 24 | from contracts.box_nft.unboxing import ( 25 | unbox_, 26 | ) 27 | from contracts.box_nft.token_uri import ( 28 | get_box_data, 29 | get_box_nb, 30 | tokenURI_, 31 | ) 32 | 33 | from contracts.library_erc1155.IERC1155 import ( 34 | setApprovalForAll_, 35 | isApprovedForAll_, 36 | balanceOf_, 37 | balanceOfBatch_, 38 | safeTransferFrom_, 39 | supportsInterface, 40 | ) 41 | 42 | from contracts.library_erc1155.IERC1155_OZ import ( 43 | setApprovalForAll, 44 | isApprovedForAll, 45 | balanceOf, 46 | balanceOfBatch, 47 | safeTransferFrom, 48 | ) 49 | 50 | 51 | from starkware.cairo.common.uint256 import Uint256 52 | from contracts.utilities.Uint256_felt_conv import _uint_to_felt, _felt_to_uint 53 | 54 | // URI is custom 55 | // Not quite OZ compliant -> I return a list of felt. 56 | @view 57 | func uri{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 58 | id: Uint256 59 | ) -> (uri_len: felt, uri: felt*) { 60 | let (tid) = _uint_to_felt(id); 61 | let (ul, u) = tokenURI_(token_id=tid); 62 | return (ul, u); 63 | } 64 | -------------------------------------------------------------------------------- /contracts/box_nft/data.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | briq_data_start: 3 | dw 434; // Box #1 4 | dw 0; 5 | dw 0; 6 | dw 0; 7 | dw 0; 8 | dw 1252; // Box #2 9 | dw 0; 10 | dw 0; 11 | dw 0; 12 | dw 0; 13 | dw 2636; // Box #3 14 | dw 0; 15 | dw 0; 16 | dw 0; 17 | dw 0; 18 | dw 431; // Box #4 19 | dw 0; 20 | dw 0; 21 | dw 0; 22 | dw 0; 23 | dw 1246; // Box #5 24 | dw 0; 25 | dw 0; 26 | dw 0; 27 | dw 0; 28 | dw 2287; // Box #6 29 | dw 0; 30 | dw 0; 31 | dw 0; 32 | dw 0; 33 | dw 431; // Box #7 34 | dw 0; 35 | dw 0; 36 | dw 0; 37 | dw 0; 38 | dw 1286; // Box #8 39 | dw 0; 40 | dw 0; 41 | dw 0; 42 | dw 0; 43 | dw 2392; // Box #9 44 | dw 0; 45 | dw 0; 46 | dw 0; 47 | dw 0; 48 | dw 60; // Box #10 49 | dw 0; 50 | dw 0; 51 | dw 0; 52 | dw 0; 53 | briq_data_end: 54 | shape_data_start: 55 | dw 0x3666f9ef3c2848b484726482c5c18d4c95dab997a9b31b10b194337f9922682; // Box #11 56 | dw 0x3666f9ef3c2848b484726482c5c18d4c95dab997a9b31b10b194337f9922682; // Box #11 57 | dw 0x3666f9ef3c2848b484726482c5c18d4c95dab997a9b31b10b194337f9922682; // Box #11 58 | dw 0x3666f9ef3c2848b484726482c5c18d4c95dab997a9b31b10b194337f9922682; // Box #11 59 | dw 0x3666f9ef3c2848b484726482c5c18d4c95dab997a9b31b10b194337f9922682; // Box #11 60 | dw 0x3666f9ef3c2848b484726482c5c18d4c95dab997a9b31b10b194337f9922682; // Box #11 61 | dw 0x3666f9ef3c2848b484726482c5c18d4c95dab997a9b31b10b194337f9922682; // Box #11 62 | dw 0x3666f9ef3c2848b484726482c5c18d4c95dab997a9b31b10b194337f9922682; // Box #11 63 | dw 0x3666f9ef3c2848b484726482c5c18d4c95dab997a9b31b10b194337f9922682; // Box #11 64 | dw 0x3666f9ef3c2848b484726482c5c18d4c95dab997a9b31b10b194337f9922682; // Box #11 65 | shape_data_end: 66 | -------------------------------------------------------------------------------- /contracts/box_nft/minting.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin 4 | from starkware.cairo.common.math import assert_lt_felt, assert_le_felt 5 | from starkware.starknet.common.syscalls import get_caller_address 6 | 7 | from starkware.cairo.common.registers import get_label_location 8 | 9 | from contracts.library_erc1155.transferability import ERC1155_transferability 10 | from contracts.library_erc1155.balance import ERC1155_balance 11 | 12 | from contracts.utilities.authorization import _onlyAdmin 13 | 14 | from contracts.box_nft.data import shape_data_start, shape_data_end 15 | 16 | @external 17 | func mint_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 18 | owner: felt, token_id: felt, number: felt 19 | ) { 20 | _onlyAdmin(); 21 | 22 | ERC1155_balance._increaseBalance(owner, token_id, number); 23 | 24 | let (caller) = get_caller_address(); 25 | ERC1155_transferability._onTransfer(caller, 0, owner, token_id, number); 26 | 27 | // Make sure we have data for that token ID 28 | let (_shape_data_start) = get_label_location(shape_data_start); 29 | let (_shape_data_end) = get_label_location(shape_data_end); 30 | assert_lt_felt(0, token_id); 31 | assert_le_felt(token_id, _shape_data_end - _shape_data_start); 32 | 33 | return (); 34 | } 35 | -------------------------------------------------------------------------------- /contracts/box_nft/token_uri.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.alloc import alloc 4 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin, BitwiseBuiltin 5 | from starkware.cairo.common.registers import get_label_location 6 | from starkware.cairo.common.registers import get_fp_and_pc 7 | from starkware.cairo.common.memcpy import memcpy 8 | from starkware.cairo.common.uint256 import Uint256 9 | from starkware.cairo.common.math import assert_lt_felt 10 | 11 | from contracts.utilities.token_uri import TokenURIHelpers 12 | 13 | from contracts.utilities.Uint256_felt_conv import _uint_to_felt, _felt_to_uint 14 | 15 | from contracts.box_nft.data import briq_data_start, briq_data_end, shape_data_start, shape_data_end 16 | 17 | struct BoxData { 18 | briq_1: felt, // nb of briqs of material 0x1 19 | briq_3: felt, // nb of briqs of material 0x3 20 | briq_4: felt, // nb of briqs of material 0x4 21 | briq_5: felt, // nb of briqs of material 0x5 22 | briq_6: felt, // nb of briqs of material 0x6 23 | shape_class_hash: felt, // Class hash of the matching shape contract 24 | } 25 | 26 | @view 27 | func get_box_data{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 28 | token_id: felt 29 | ) -> (data: BoxData) { 30 | alloc_locals; 31 | let box: BoxData* = alloc(); 32 | let (briq_data) = get_label_location(briq_data_start); 33 | memcpy(box, briq_data + 5 * (token_id - 1), 5); 34 | let (shape_data) = get_label_location(shape_data_start); 35 | memcpy(box + 5, shape_data + (token_id - 1), 1); 36 | return (box[0],); 37 | } 38 | 39 | @view 40 | func get_box_nb{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( 41 | nb: felt 42 | ) { 43 | let (_start) = get_label_location(shape_data_start); 44 | let (_end) = get_label_location(shape_data_end); 45 | let res = _end - _start; 46 | return (res,); 47 | } 48 | 49 | 50 | data_uri_start: 51 | dw 'https://api.briq.construction'; 52 | dw '/v1/uri/box/'; 53 | dw 'starknet-mainnet/'; 54 | dw '.json'; 55 | 56 | @view 57 | func tokenURI_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 58 | token_id: felt 59 | ) -> (uri_len: felt, uri: felt*) { 60 | return TokenURIHelpers._getUrl(token_id, data_uri_start); 61 | } 62 | -------------------------------------------------------------------------------- /contracts/ecosystem/genesis_collection.cairo: -------------------------------------------------------------------------------- 1 | 2 | const GENESIS_COLLECTION = 0x1; 3 | const DUCKS_COLLECTION = 0x3; 4 | const ZENDUCKS_COLLECTION = 0x4; -------------------------------------------------------------------------------- /contracts/ecosystem/to_attributes_registry.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin 4 | from starkware.starknet.common.syscalls import get_caller_address 5 | from contracts.utilities.authorization import _onlyAdmin 6 | 7 | @storage_var 8 | func _attributes_registry_address() -> (address: felt) { 9 | } 10 | 11 | @view 12 | func getAttributesRegistryAddress_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( 13 | address: felt 14 | ) { 15 | let (value) = _attributes_registry_address.read(); 16 | return (value,); 17 | } 18 | 19 | @external 20 | func setAttributesRegistryAddress_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 21 | address: felt 22 | ) { 23 | _onlyAdmin(); 24 | _attributes_registry_address.write(address); 25 | return (); 26 | } 27 | 28 | func _onlyAttributesRegistry{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { 29 | let (caller) = get_caller_address(); 30 | let (attr_addr) = _attributes_registry_address.read(); 31 | with_attr error_message("Only the attributes registry may call this function.") { 32 | assert caller = attr_addr; 33 | } 34 | return (); 35 | } -------------------------------------------------------------------------------- /contracts/ecosystem/to_booklet.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin 4 | 5 | from contracts.utilities.authorization import _onlyAdmin 6 | 7 | @storage_var 8 | func _booklet_address() -> (address: felt) { 9 | } 10 | 11 | @view 12 | func getBookletAddress_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( 13 | address: felt 14 | ) { 15 | let (value) = _booklet_address.read(); 16 | return (value,); 17 | } 18 | 19 | @external 20 | func setBookletAddress_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 21 | address: felt 22 | ) { 23 | _onlyAdmin(); 24 | _booklet_address.write(address); 25 | return (); 26 | } 27 | -------------------------------------------------------------------------------- /contracts/ecosystem/to_box.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin 4 | 5 | from contracts.utilities.authorization import _onlyAdmin 6 | 7 | @storage_var 8 | func _box_address() -> (address: felt) { 9 | } 10 | 11 | @view 12 | func getBoxAddress_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( 13 | address: felt 14 | ) { 15 | let (value) = _box_address.read(); 16 | return (value,); 17 | } 18 | 19 | @external 20 | func setBoxAddress_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 21 | address: felt 22 | ) { 23 | _onlyAdmin(); 24 | _box_address.write(address); 25 | return (); 26 | } 27 | -------------------------------------------------------------------------------- /contracts/ecosystem/to_briq.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin 4 | 5 | from contracts.utilities.authorization import _onlyAdmin 6 | 7 | @storage_var 8 | func _briq_address() -> (address: felt) { 9 | } 10 | 11 | @view 12 | func getBriqAddress_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( 13 | address: felt 14 | ) { 15 | let (value) = _briq_address.read(); 16 | return (value,); 17 | } 18 | 19 | @external 20 | func setBriqAddress_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 21 | address: felt 22 | ) { 23 | _onlyAdmin(); 24 | _briq_address.write(address); 25 | return (); 26 | } 27 | -------------------------------------------------------------------------------- /contracts/ecosystem/to_factory.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin 4 | 5 | from contracts.utilities.authorization import _onlyAdmin 6 | 7 | @storage_var 8 | func _factory_address() -> (address: felt) { 9 | } 10 | 11 | @view 12 | func getFactoryAddress_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( 13 | address: felt 14 | ) { 15 | let (value) = _factory_address.read(); 16 | return (value,); 17 | } 18 | 19 | @external 20 | func setFactoryAddress_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 21 | address: felt 22 | ) { 23 | _onlyAdmin(); 24 | _factory_address.write(address); 25 | return (); 26 | } 27 | -------------------------------------------------------------------------------- /contracts/ecosystem/to_migration.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin 4 | 5 | from contracts.utilities.authorization import _onlyAdmin 6 | 7 | @storage_var 8 | func _migration_address() -> (address: felt) { 9 | } 10 | 11 | @view 12 | func getMigrationAddress_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( 13 | address: felt 14 | ) { 15 | let (value) = _migration_address.read(); 16 | return (value,); 17 | } 18 | 19 | @external 20 | func setMigrationAddress_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 21 | address: felt 22 | ) { 23 | _onlyAdmin(); 24 | _migration_address.write(address); 25 | return (); 26 | } 27 | -------------------------------------------------------------------------------- /contracts/ecosystem/to_set.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin 4 | 5 | from contracts.utilities.authorization import _onlyAdmin 6 | 7 | @storage_var 8 | func _set_address() -> (address: felt) { 9 | } 10 | 11 | @view 12 | func getSetAddress_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( 13 | address: felt 14 | ) { 15 | let (value) = _set_address.read(); 16 | return (value,); 17 | } 18 | 19 | @external 20 | func setSetAddress_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 21 | address: felt 22 | ) { 23 | _onlyAdmin(); 24 | _set_address.write(address); 25 | return (); 26 | } 27 | -------------------------------------------------------------------------------- /contracts/library_erc1155/IERC1155.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin, BitwiseBuiltin 4 | from starkware.cairo.common.uint256 import Uint256 5 | 6 | from contracts.library_erc1155.approvals import ERC1155_approvals 7 | from contracts.library_erc1155.balance import ERC1155_balance 8 | from contracts.library_erc1155.token_uri import ERC1155_token_uri 9 | from contracts.library_erc1155.transferability import ERC1155_transferability 10 | 11 | from contracts.utilities.IERC165 import (TRUE, FALSE, IERC165_ID, IERC1155_ID, IERC1155_METADATA_ID) 12 | 13 | //@external 14 | //func approve_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 15 | // to: felt, token_id: felt, value: felt 16 | //) { 17 | // return ERC1155_approvals.approve_(to, token_id, value); 18 | //} 19 | 20 | @external 21 | func setApprovalForAll_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 22 | approved_address: felt, is_approved: felt 23 | ) { 24 | return ERC1155_approvals.setApprovalForAll_(approved_address, is_approved); 25 | } 26 | 27 | //@view 28 | //func getApproved_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 29 | // on_behalf_of: felt, token_id: felt, address: felt 30 | //) -> (approved_value: felt) { 31 | // return ERC1155_approvals.getApproved_(on_behalf_of, token_id, address); 32 | //} 33 | 34 | @view 35 | func isApprovedForAll_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 36 | on_behalf_of: felt, address: felt 37 | ) -> (is_approved: felt) { 38 | return ERC1155_approvals.isApprovedForAll_(on_behalf_of, address); 39 | } 40 | 41 | @view 42 | func balanceOf_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 43 | owner: felt, token_id: felt 44 | ) -> (balance: felt) { 45 | return ERC1155_balance.balanceOf_(owner, token_id); 46 | } 47 | 48 | @view 49 | func balanceOfBatch_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 50 | owners_len: felt, owners: felt*, token_ids_len: felt, token_ids: felt* 51 | ) -> (balances_len: felt, balances: felt*) { 52 | return ERC1155_balance.balanceOfBatch_(owners_len, owners, token_ids_len, token_ids); 53 | } 54 | 55 | @view 56 | func uri_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, bitwise_ptr: BitwiseBuiltin*, range_check_ptr}( 57 | token_id: felt 58 | ) -> (uri_len: felt, uri: felt*) { 59 | return ERC1155_token_uri.uri_(token_id); 60 | } 61 | 62 | @external 63 | func safeTransferFrom_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 64 | sender: felt, recipient: felt, token_id: felt, value: felt, data_len: felt, data: felt* 65 | ) { 66 | return ERC1155_transferability.safeTransferFrom_(sender, recipient, token_id, value, data_len, data); 67 | } 68 | 69 | @view 70 | func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 71 | interfaceId: felt 72 | ) -> (success: felt) { 73 | if (interfaceId == IERC165_ID) { 74 | return (success=TRUE); 75 | } 76 | if (interfaceId == IERC1155_ID) { 77 | return (success=TRUE); 78 | } 79 | if (interfaceId == IERC1155_METADATA_ID) { 80 | return (success=TRUE); 81 | } 82 | return (success=FALSE); 83 | } 84 | -------------------------------------------------------------------------------- /contracts/library_erc1155/IERC1155_OZ.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin, BitwiseBuiltin 4 | from starkware.cairo.common.uint256 import Uint256 5 | from contracts.utilities.Uint256_felt_conv import _uint_to_felt, _felt_to_uint, _uints_to_felts, _felts_to_uints 6 | 7 | from contracts.library_erc1155.IERC1155 import ( 8 | balanceOf_, 9 | balanceOfBatch_, 10 | setApprovalForAll_, 11 | isApprovedForAll_, 12 | safeTransferFrom_, 13 | uri_ 14 | ) 15 | 16 | @view 17 | func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 18 | account: felt, id: Uint256 19 | ) -> (balance: Uint256) { 20 | let (tid) = _uint_to_felt(id); 21 | let (b) = balanceOf_(owner=account, token_id=tid); 22 | let (uintb) = _felt_to_uint(b); 23 | return (uintb,); 24 | } 25 | 26 | @view 27 | func balanceOfBatch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 28 | accounts_len: felt, accounts: felt*, ids_len: felt, ids: Uint256* 29 | ) -> (balances_len: felt, balances: Uint256*) { 30 | let (tid_l, tid) = _uints_to_felts(ids_len, ids); 31 | let (bs_l, bs) = balanceOfBatch_(owners_len=accounts_len, owners=accounts, token_ids_len=tid_l, token_ids=tid); 32 | let (ubs_l, ubs) = _felts_to_uints(bs_l, bs); 33 | return (ubs_l, ubs); 34 | } 35 | 36 | @external 37 | func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 38 | operator: felt, approved: felt 39 | ) { 40 | return setApprovalForAll_(approved_address=operator, is_approved=approved); 41 | } 42 | 43 | @view 44 | func isApprovedForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 45 | account: felt, operator: felt 46 | ) -> (isApproved: felt) { 47 | let (r) = isApprovedForAll_(on_behalf_of=account, address=operator); 48 | return (r,); 49 | } 50 | 51 | @external 52 | func safeTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 53 | from_: felt, to: felt, id: Uint256, amount: Uint256, data_len: felt, data: felt* 54 | ) { 55 | let (tid) = _uint_to_felt(id); 56 | let (tam) = _uint_to_felt(amount); 57 | return safeTransferFrom_(sender=from_, recipient=to, token_id=tid, value=tam, data_len=data_len, data=data); 58 | } 59 | 60 | // Not quite OZ compliant -> I return a list of felt. 61 | @view 62 | func uri{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, bitwise_ptr: BitwiseBuiltin*, range_check_ptr}( 63 | id: Uint256 64 | ) -> (uri_len: felt, uri: felt*) { 65 | let (tid) = _uint_to_felt(id); 66 | return uri_(token_id=tid); 67 | } 68 | -------------------------------------------------------------------------------- /contracts/library_erc1155/approvals.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin 4 | from starkware.starknet.common.syscalls import get_caller_address 5 | from starkware.cairo.common.math import assert_not_zero, assert_not_equal, assert_le_felt 6 | 7 | from starkware.cairo.common.uint256 import Uint256 8 | from contracts.utilities.Uint256_felt_conv import _felt_to_uint 9 | 10 | from contracts.library_erc1155.balance import _balance 11 | 12 | from contracts.utilities.authorization import _only 13 | 14 | @event 15 | func ApprovalForAll(_owner: felt, _operator: felt, _approved: felt) { 16 | } 17 | 18 | // # approved_address is 'operator' in the spec, but I find that name rather unclear. 19 | @storage_var 20 | func _approval_all(on_behalf_of: felt, approved_address: felt) -> (is_approved: felt) { 21 | } 22 | 23 | namespace ERC1155_approvals { 24 | @external 25 | func setApprovalForAll_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 26 | approved_address: felt, is_approved: felt 27 | ) { 28 | let (caller) = get_caller_address(); 29 | _setExplicitApprovalForAll( 30 | on_behalf_of=caller, approved_address=approved_address, is_approved=is_approved 31 | ); 32 | return (); 33 | } 34 | 35 | func _setExplicitApprovalForAll{ 36 | syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr 37 | }(on_behalf_of: felt, approved_address: felt, is_approved: felt) { 38 | // Neither of these can be 0. 39 | with_attr error_message("ERC721: either the caller or operator is the zero address") { 40 | assert_not_zero(on_behalf_of * approved_address); 41 | } 42 | 43 | // Cannot approve yourself. 44 | with_attr error_message("ERC721: approve to caller") { 45 | assert_not_equal(on_behalf_of, approved_address); 46 | } 47 | 48 | // Make sure `is_approved` is a boolean (0 or 1) 49 | with_attr error_message("ERC721: approved is not a Cairo boolean") { 50 | assert is_approved * (1 - is_approved) = 0; 51 | } 52 | 53 | _approval_all.write(on_behalf_of, approved_address, is_approved); 54 | ApprovalForAll.emit(on_behalf_of, approved_address, is_approved); 55 | return (); 56 | } 57 | 58 | @view 59 | func isApprovedForAll_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 60 | on_behalf_of: felt, address: felt 61 | ) -> (is_approved: felt) { 62 | let (allowed) = _approval_all.read(on_behalf_of, address); 63 | return (allowed,); 64 | } 65 | 66 | // ## Auth 67 | 68 | func _onlyApprovedAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 69 | on_behalf_of: felt 70 | ) { 71 | let (caller) = get_caller_address(); 72 | // You can always approve on behalf of yourself. 73 | if (caller == on_behalf_of) { 74 | return (); 75 | } 76 | let (isOperator) = isApprovedForAll_(on_behalf_of, caller); 77 | assert isOperator = 1; 78 | return (); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /contracts/library_erc1155/approvals_detailed.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin 4 | from starkware.starknet.common.syscalls import get_caller_address 5 | from starkware.cairo.common.math import assert_not_zero, assert_not_equal, assert_le_felt 6 | 7 | from starkware.cairo.common.uint256 import Uint256 8 | from contracts.utilities.Uint256_felt_conv import _felt_to_uint 9 | 10 | from contracts.library_erc1155.balance import _balance 11 | 12 | from contracts.utilities.authorization import _only 13 | 14 | @event 15 | func ApprovalFor(_token_id: Uint256, _owner: felt, _approved_address: felt, _value: felt) { 16 | } 17 | 18 | @storage_var 19 | func _approval_n(token_id: felt, owner: felt, approved_address: felt) -> (value: felt) { 20 | } 21 | 22 | namespace ERC1155_extension_approvals { 23 | func approve_nocheck_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 24 | caller: felt, to: felt, token_id: felt, value: felt 25 | ) { 26 | _approval_n.write(token_id, caller, to, value); 27 | let (tk) = _felt_to_uint(token_id); 28 | ApprovalFor.emit(tk, caller, to, value); 29 | return (); 30 | } 31 | 32 | @external 33 | func approve_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 34 | to: felt, token_id: felt, value: felt 35 | ) { 36 | alloc_locals; 37 | let (caller) = get_caller_address(); 38 | 39 | with_attr error_message("Cannot approve from the zero address") { 40 | assert_not_zero(caller); 41 | } 42 | 43 | with_attr error_message("Approval to current caller") { 44 | assert_not_equal(caller, to); 45 | } 46 | 47 | // Checks that either approving for yourself or 48 | // caller isApprovedForAll on behalf of caller 49 | _onlyApprovedAll(on_behalf_of=caller); 50 | 51 | approve_nocheck_(caller, to, token_id, value); 52 | return (); 53 | } 54 | 55 | @view 56 | func getApproved_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 57 | on_behalf_of: felt, token_id: felt, address: felt 58 | ) -> (approved_value: felt) { 59 | let (value) = _approval_n.read(token_id, on_behalf_of, address); 60 | return (value,); 61 | } 62 | 63 | // ## Auth 64 | 65 | func _onlyApproved{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 66 | on_behalf_of: felt, token_id: felt, value: felt 67 | ) { 68 | let (caller) = get_caller_address(); 69 | // You can always approve on behalf of yourself. 70 | if (on_behalf_of == caller) { 71 | return (); 72 | } 73 | let (isOperator) = isApprovedForAll_(on_behalf_of, caller); 74 | if (isOperator == 1) { 75 | return (); 76 | } 77 | let (approved_value) = getApproved_(on_behalf_of, token_id, caller); 78 | with_attr error_message("Insufficient approval balance") { 79 | assert_le_felt(value, approved_value); 80 | } 81 | return (); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /contracts/library_erc1155/balance.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin 4 | from starkware.starknet.common.syscalls import get_caller_address 5 | from starkware.cairo.common.math import ( 6 | assert_nn_le, 7 | assert_lt, 8 | assert_le, 9 | assert_not_zero, 10 | assert_lt_felt, 11 | unsigned_div_rem, 12 | assert_not_equal, 13 | ) 14 | from starkware.cairo.common.registers import get_fp_and_pc 15 | from starkware.cairo.common.alloc import alloc 16 | 17 | //########### 18 | //########### 19 | //########### 20 | // Storage variables. 21 | 22 | @storage_var 23 | func _balance(owner: felt, token_id: felt) -> (balance: felt) { 24 | } 25 | 26 | namespace ERC1155_balance { 27 | 28 | func _increaseBalance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 29 | owner: felt, token_id: felt, number: felt, 30 | ) { 31 | let (balance) = _balance.read(owner, token_id); 32 | with_attr error_message("Mint would overflow balance") { 33 | assert_lt_felt(balance, balance + number); 34 | } 35 | _balance.write(owner, token_id, balance + number); 36 | return (); 37 | } 38 | 39 | func _decreaseBalance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 40 | owner: felt, token_id: felt, number: felt, 41 | ) { 42 | let (balance) = _balance.read(owner, token_id); 43 | with_attr error_message("Insufficient balance") { 44 | assert_lt_felt(balance - number, balance); 45 | } 46 | _balance.write(owner, token_id, balance - number); 47 | return (); 48 | } 49 | 50 | // @view 51 | func balanceOf_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 52 | owner: felt, token_id: felt 53 | ) -> (balance: felt) { 54 | let (balance) = _balance.read(owner, token_id); 55 | return (balance,); 56 | } 57 | 58 | // @view 59 | func balanceOfBatch_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 60 | owners_len: felt, owners: felt*, token_ids_len: felt, token_ids: felt* 61 | ) -> (balances_len: felt, balances: felt*) { 62 | alloc_locals; 63 | assert owners_len = token_ids_len; 64 | 65 | let (balances: felt*) = alloc(); 66 | let (b_end) = _balanceOfBatch(owners_len, owners, token_ids, balances); 67 | return (b_end - balances, balances); 68 | } 69 | 70 | func _balanceOfBatch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 71 | owners_len: felt, owners: felt*, token_ids: felt*, balances: felt* 72 | ) -> (balance: felt*) { 73 | if (owners_len == 0) { 74 | return (balances,); 75 | } 76 | let (balance) = _balance.read(owners[0], token_ids[0]); 77 | balances[0] = balance; 78 | return _balanceOfBatch(owners_len - 1, owners + 1, token_ids + 1, balances + 1); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /contracts/library_erc721/IERC721.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin, BitwiseBuiltin 4 | from starkware.cairo.common.uint256 import Uint256 5 | 6 | from contracts.library_erc721.approvals import ERC721_approvals 7 | from contracts.library_erc721.balance import ERC721 8 | from contracts.library_erc721.enumerability import ERC721_enumerability 9 | from contracts.library_erc721.transferability import ERC721_transferability 10 | 11 | from contracts.utilities.IERC165 import (TRUE, FALSE, IERC165_ID, IERC721_ID, IERC721_METADATA_ID) 12 | 13 | @external 14 | func approve_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 15 | to: felt, token_id: felt 16 | ) { 17 | return ERC721_approvals.approve_(to, token_id); 18 | } 19 | 20 | @external 21 | func setApprovalForAll_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 22 | approved_address: felt, is_approved: felt 23 | ) { 24 | return ERC721_approvals.setApprovalForAll_(approved_address, is_approved); 25 | } 26 | 27 | @view 28 | func getApproved_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 29 | token_id: felt 30 | ) -> (approved: felt) { 31 | return ERC721_approvals.getApproved_(token_id); 32 | } 33 | 34 | @view 35 | func isApprovedForAll_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 36 | on_behalf_of: felt, address: felt 37 | ) -> (is_approved: felt) { 38 | return ERC721_approvals.isApprovedForAll_(on_behalf_of, address); 39 | } 40 | 41 | @view 42 | func ownerOf_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 43 | token_id: felt 44 | ) -> (owner: felt) { 45 | return ERC721.ownerOf_(token_id); 46 | } 47 | 48 | @view 49 | func balanceOf_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 50 | owner: felt 51 | ) -> (balance: felt) { 52 | return ERC721.balanceOf_(owner); 53 | } 54 | 55 | @view 56 | func balanceDetailsOf_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 57 | owner: felt 58 | ) -> (token_ids_len: felt, token_ids: felt*) { 59 | return ERC721_enumerability.balanceDetailsOf_(owner); 60 | } 61 | 62 | @view 63 | func tokenOfOwnerByIndex_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 64 | owner: felt, index: felt 65 | ) -> (token_id: felt) { 66 | return ERC721_enumerability.tokenOfOwnerByIndex_(owner, index); 67 | } 68 | 69 | @external 70 | func transferFrom_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 71 | sender: felt, recipient: felt, token_id: felt 72 | ) { 73 | return ERC721_transferability.transferFrom_(sender, recipient, token_id); 74 | } 75 | 76 | @view 77 | func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 78 | interfaceId: felt 79 | ) -> (success: felt) { 80 | if (interfaceId == IERC165_ID) { 81 | return (success=TRUE); 82 | } 83 | if (interfaceId == IERC721_ID) { 84 | return (success=TRUE); 85 | } 86 | if (interfaceId == IERC721_METADATA_ID) { 87 | return (success=TRUE); 88 | } 89 | return (success=FALSE); 90 | } 91 | -------------------------------------------------------------------------------- /contracts/library_erc721/IERC721_enumerable.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin, BitwiseBuiltin 4 | from starkware.cairo.common.uint256 import Uint256 5 | 6 | from contracts.library_erc721.transferability_enum import transferFrom_ as tf 7 | 8 | 9 | @external 10 | func transferFrom_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 11 | sender: felt, recipient: felt, token_id: felt 12 | ) { 13 | return tf(sender, recipient, token_id); 14 | } 15 | -------------------------------------------------------------------------------- /contracts/library_erc721/balance.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin 4 | from starkware.starknet.common.syscalls import get_caller_address 5 | from starkware.cairo.common.math import ( 6 | assert_nn_le, 7 | assert_lt, 8 | assert_le, 9 | assert_not_zero, 10 | assert_lt_felt, 11 | unsigned_div_rem, 12 | assert_not_equal, 13 | ) 14 | from starkware.cairo.common.registers import get_fp_and_pc 15 | from starkware.cairo.common.alloc import alloc 16 | 17 | //########### 18 | //########### 19 | //########### 20 | // Storage variables. 21 | 22 | @storage_var 23 | func _balance(owner: felt) -> (balance: felt) { 24 | } 25 | 26 | @storage_var 27 | func _owner(token_id: felt) -> (owner: felt) { 28 | } 29 | 30 | namespace ERC721 { 31 | // @view 32 | func ownerOf_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 33 | token_id: felt 34 | ) -> (owner: felt) { 35 | let (res) = _owner.read(token_id); 36 | // OZ ∆: don't fail on res == 0 37 | return (res,); 38 | } 39 | 40 | // @view 41 | func balanceOf_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 42 | owner: felt 43 | ) -> (balance: felt) { 44 | // OZ ∆: No 0 check, I don't see the point. 45 | let (balance) = _balance.read(owner); 46 | return (balance,); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /contracts/library_erc721/transferability_enum.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin 4 | 5 | from contracts.library_erc721.approvals import ERC721_approvals 6 | from contracts.library_erc721.transferability import ERC721_transferability 7 | 8 | from contracts.library_erc721.enumerability import ERC721_enumerability 9 | 10 | // Not namespaced for convenience 11 | @external 12 | func transferFrom_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 13 | sender: felt, recipient: felt, token_id: felt 14 | ) { 15 | // TEMP - deactivated for the briq dojo migration 16 | // ERC721_approvals._onlyApproved(sender, token_id); 17 | 18 | ERC721_transferability._transfer(sender, recipient, token_id); 19 | 20 | // Unset before setting, so that self-transfers work. 21 | ERC721_enumerability._unsetTokenByOwner(sender, token_id); 22 | ERC721_enumerability._setTokenByOwner(recipient, token_id, 0); 23 | 24 | return (); 25 | } 26 | -------------------------------------------------------------------------------- /contracts/mocks/attributes_registry_mock.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from contracts.types import FTSpec, ShapeItem 4 | 5 | @view 6 | func total_balance(owner: felt) -> (balance: felt) { 7 | return (0,); 8 | } 9 | 10 | @external 11 | func assign_attributes( 12 | owner: felt, 13 | set_token_id: felt, 14 | attributes_len: felt, attributes: felt*, 15 | shape_len: felt, shape: ShapeItem*, 16 | fts_len: felt, fts: FTSpec*, 17 | nfts_len: felt, nfts: felt* 18 | ) { 19 | return (); 20 | } 21 | 22 | @external 23 | func remove_attributes( 24 | owner: felt, 25 | set_token_id: felt, 26 | attributes_len: felt, attributes: felt*, 27 | ) { 28 | return (); 29 | } -------------------------------------------------------------------------------- /contracts/mocks/briq_mock.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | @external 4 | func transferFT_(sender: felt, recipient: felt, material: felt, qty: felt) { 5 | return (); 6 | } 7 | 8 | @external 9 | func transferOneNFT_(sender: felt, recipient: felt, material: felt, briq_token_id: felt) { 10 | return (); 11 | } 12 | 13 | @external 14 | func balanceOf_(owner: felt, material: felt) -> (balance: felt) { 15 | return (0,); 16 | } 17 | -------------------------------------------------------------------------------- /contracts/mocks/set_mock.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin 4 | from contracts.types import FTSpec, ShapeItem 5 | 6 | @external 7 | func assemble_( 8 | owner: felt, 9 | token_id_hint: felt, 10 | name_len: felt, name: felt*, 11 | description_len: felt, description: felt*, 12 | fts_len: felt, fts: FTSpec*, 13 | nfts_len: felt, nfts: felt*, 14 | shape_len: felt, shape: ShapeItem*, 15 | attributes_len: felt, attributes: felt*, 16 | ) { 17 | return (); 18 | } 19 | 20 | // Very basic implem 21 | 22 | @storage_var 23 | func __balance(token_id: felt) -> (owner: felt) { 24 | } 25 | 26 | @external 27 | func transferFrom_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 28 | sender: felt, 29 | receiver: felt, 30 | token_id: felt, 31 | ) { 32 | alloc_locals; 33 | 34 | let (owner) = __balance.read(token_id); 35 | assert sender = owner; 36 | __balance.write(token_id, receiver); 37 | return (); 38 | } 39 | 40 | @view 41 | func ownerOf_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 42 | token_id: felt 43 | ) -> (owner: felt) { 44 | let (owner) = __balance.read(token_id); 45 | return (owner,); 46 | } 47 | -------------------------------------------------------------------------------- /contracts/mocks/shape_mock.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from contracts.types import ShapeItem, FTSpec 4 | 5 | @external 6 | func check_shape_numbers_( 7 | index: felt, shape_len: felt, shape: ShapeItem*, fts_len: felt, fts: FTSpec*, nfts_len: felt, nfts: felt* 8 | ) { 9 | return (); 10 | } 11 | -------------------------------------------------------------------------------- /contracts/mocks/uint256.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, BitwiseBuiltin 4 | from starkware.cairo.common.uint256 import Uint256 5 | 6 | from contracts.utilities.Uint256_felt_conv import _uint_to_felt, _felt_to_uint, _uints_to_felts, _felts_to_uints 7 | 8 | @external 9 | func uint_to_felt{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 10 | value: Uint256 11 | ) -> (value: felt) { 12 | return _uint_to_felt(value); 13 | } 14 | 15 | @external 16 | func felt_to_uint{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 17 | value: felt 18 | ) -> (value: Uint256) { 19 | return _felt_to_uint(value); 20 | } 21 | 22 | @external 23 | func uints_to_felts{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 24 | value_len: felt, value: Uint256* 25 | ) -> (value_len: felt, value: felt*) { 26 | return _uints_to_felts(value_len, value); 27 | } 28 | 29 | @external 30 | func felts_to_uints{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 31 | value_len: felt, value: felt* 32 | ) -> (value_len: felt, value: Uint256*) { 33 | return _felts_to_uints(value_len, value); 34 | } 35 | -------------------------------------------------------------------------------- /contracts/set_nft/token_uri.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin, BitwiseBuiltin 4 | 5 | from contracts.utilities.token_uri import TokenURIHelpers 6 | 7 | data_uri_start: 8 | dw 'https://api.briq.construction'; 9 | dw '/v1/uri/set/'; 10 | dw 'starknet-mainnet/'; 11 | dw '.json'; 12 | 13 | @view 14 | func tokenURI_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 15 | token_id: felt 16 | ) -> (uri_len: felt, uri: felt*) { 17 | return TokenURIHelpers._getUrl(token_id, data_uri_start); 18 | } 19 | -------------------------------------------------------------------------------- /contracts/shape_attribute.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from contracts.upgrades.upgradable_mixin import ( 4 | getAdmin_, 5 | getImplementation_, 6 | upgradeImplementation_, 7 | setRootAdmin_, 8 | ) 9 | 10 | from contracts.shape.attribute import ( 11 | assign_attribute, 12 | remove_attribute, 13 | balanceOf_, 14 | getShapeHash_, 15 | checkShape_, 16 | ) 17 | 18 | from contracts.ecosystem.to_attributes_registry import ( 19 | getAttributesRegistryAddress_, 20 | setAttributesRegistryAddress_, 21 | ) 22 | 23 | -------------------------------------------------------------------------------- /contracts/types.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | struct FTSpec { 4 | token_id: felt, 5 | qty: felt, 6 | } 7 | 8 | struct NFTSpec { 9 | material: felt, 10 | token_id: felt, 11 | } 12 | 13 | struct BalanceSpec { 14 | material: felt, 15 | balance: felt, 16 | } 17 | 18 | struct ShapeItem { 19 | // Material is 64 bit so this is COLOR as short string shifted 136 bits left, and material. 20 | // The 128th bit indicates 'This is an NFT', at which point you need to refer to the list of NFTs. 21 | // (I'm shifting colors by 7 more bits so that the corresponding hex is easily readable and I don't need more). 22 | color_nft_material: felt, 23 | // Stored as reversed two's completement, shifted by 64 bits. 24 | // (reversed az in -> the presence of the 64th bit indicates positive number) 25 | // (This is done so that sorting works) 26 | x_y_z: felt, 27 | } 28 | -------------------------------------------------------------------------------- /contracts/upgrades/proxy.cairo: -------------------------------------------------------------------------------- 1 | // Slightly different constructor from OZ - I just initialize with an admin directly. 2 | // (This would be imported, but I cannot have two constructors...) 3 | 4 | %lang starknet 5 | 6 | from starkware.cairo.common.cairo_builtins import HashBuiltin 7 | from starkware.cairo.common.math import assert_not_zero 8 | from starkware.starknet.common.syscalls import library_call_l1_handler, library_call 9 | 10 | from contracts.vendor.openzeppelin.upgrades.library import Proxy 11 | 12 | // 13 | // Constructor 14 | // 15 | 16 | @constructor 17 | func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 18 | admin: felt, implentation_hash: felt 19 | ) { 20 | assert_not_zero(admin); 21 | assert_not_zero(implentation_hash); 22 | 23 | Proxy.initializer(admin); 24 | Proxy._set_implementation_hash(implentation_hash); 25 | return (); 26 | } 27 | 28 | // 29 | // Fallback functions 30 | // 31 | 32 | @external 33 | @raw_input 34 | @raw_output 35 | func __default__{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 36 | selector: felt, calldata_size: felt, calldata: felt* 37 | ) -> (retdata_size: felt, retdata: felt*) { 38 | let (class_hash) = Proxy.get_implementation_hash(); 39 | 40 | let (retdata_size: felt, retdata: felt*) = library_call( 41 | class_hash=class_hash, 42 | function_selector=selector, 43 | calldata_size=calldata_size, 44 | calldata=calldata, 45 | ); 46 | return (retdata_size, retdata); 47 | } 48 | 49 | @l1_handler 50 | @raw_input 51 | func __l1_default__{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 52 | selector: felt, calldata_size: felt, calldata: felt* 53 | ) { 54 | let (class_hash) = Proxy.get_implementation_hash(); 55 | 56 | library_call_l1_handler( 57 | class_hash=class_hash, 58 | function_selector=selector, 59 | calldata_size=calldata_size, 60 | calldata=calldata, 61 | ); 62 | return (); 63 | } 64 | -------------------------------------------------------------------------------- /contracts/upgrades/upgradable_mixin.cairo: -------------------------------------------------------------------------------- 1 | // Import this to make a contract upgradable, but only for admins. 2 | // To turn off upgradability, simply remove the mixin from the contract and upgrade one last time. 3 | 4 | %lang starknet 5 | 6 | from starkware.cairo.common.cairo_builtins import HashBuiltin 7 | from starkware.cairo.common.math import assert_not_zero 8 | 9 | from contracts.vendor.openzeppelin.upgrades.library import Proxy 10 | from contracts.utilities.authorization import _onlyAdmin 11 | 12 | @view 13 | func getAdmin_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (admin: felt) { 14 | let (admin) = Proxy.get_admin(); 15 | return (admin,); 16 | } 17 | 18 | @view 19 | func getImplementation_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( 20 | implementation: felt 21 | ) { 22 | let (implementation) = Proxy.get_implementation_hash(); 23 | return (implementation,); 24 | } 25 | 26 | // Upgrade 27 | 28 | @external 29 | func upgradeImplementation_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 30 | new_implementation: felt 31 | ) { 32 | _onlyAdmin(); 33 | assert_not_zero(new_implementation); 34 | Proxy._set_implementation_hash(new_implementation); 35 | return (); 36 | } 37 | 38 | @external 39 | func setRootAdmin_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 40 | new_admin: felt 41 | ) { 42 | _onlyAdmin(); 43 | assert_not_zero(new_admin); 44 | Proxy._set_admin(new_admin); 45 | return (); 46 | } 47 | -------------------------------------------------------------------------------- /contracts/utilities/IERC165.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | from starkware.cairo.common.bool import TRUE, FALSE 3 | 4 | const IERC165_ID = 0x01ffc9a7; 5 | 6 | const IERC721_ID = 0x80ac58cd; 7 | const IERC721_METADATA_ID = 0x5b5e139f; 8 | 9 | const IERC1155_ID = 0xd9b67a26; 10 | const IERC1155_METADATA_ID = 0x0e89341c; 11 | -------------------------------------------------------------------------------- /contracts/utilities/Uint256_felt_conv.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, BitwiseBuiltin 4 | from starkware.cairo.common.math import ( 5 | assert_nn_le, 6 | assert_lt, 7 | assert_le, 8 | assert_not_zero, 9 | assert_lt_felt, 10 | unsigned_div_rem, 11 | ) 12 | from starkware.cairo.common.math import split_felt 13 | from starkware.cairo.common.math_cmp import is_le_felt 14 | from starkware.cairo.common.uint256 import Uint256, uint256_check 15 | from starkware.cairo.common.alloc import alloc 16 | 17 | const high_bit_max = 0x8000000000000110000000000000000; 18 | 19 | func _check_uint_fits_felt{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 20 | value: Uint256 21 | ) { 22 | let high_clear = is_le_felt(value.high, high_bit_max - 1); 23 | // Only one possible value otherwise, the actual PRIME - 1; 24 | if (high_clear == 0) { 25 | assert value.high = high_bit_max; 26 | assert value.low = 0; 27 | } 28 | return (); 29 | } 30 | 31 | 32 | func _uint_to_felt{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 33 | value: Uint256 34 | ) -> (value: felt) { 35 | uint256_check(value); 36 | _check_uint_fits_felt(value); 37 | return (value.high * (2 ** 128) + value.low,); 38 | } 39 | 40 | func _felt_to_uint{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 41 | value: felt 42 | ) -> (value: Uint256) { 43 | let (high, low) = split_felt(value); 44 | tempvar res: Uint256; 45 | res.high = high; 46 | res.low = low; 47 | return (res,); 48 | } 49 | 50 | 51 | func _uints_to_felts{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 52 | value_len: felt, value: Uint256* 53 | ) -> (value_len: felt, value: felt*) { 54 | alloc_locals; 55 | let (out: felt*) = alloc(); 56 | _uints_to_felts_inner(value_len, value, out); 57 | return (value_len, out); 58 | } 59 | 60 | 61 | func _uints_to_felts_inner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 62 | value_len: felt, value: Uint256*, out: felt* 63 | ) { 64 | if (value_len == 0) { 65 | return (); 66 | } 67 | let v = value[0]; 68 | uint256_check(v); 69 | _check_uint_fits_felt(v); 70 | assert out[0] = v.high * (2 ** 128) + v.low; 71 | return _uints_to_felts_inner(value_len - 1, value + 2, out + 1); 72 | } 73 | 74 | 75 | func _felts_to_uints{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 76 | value_len: felt, value: felt* 77 | ) -> (value_len: felt, value: Uint256*) { 78 | alloc_locals; 79 | let (out: Uint256*) = alloc(); 80 | _felts_to_uints_inner(value_len, value, out); 81 | return (value_len, out); 82 | } 83 | 84 | func _felts_to_uints_inner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 85 | value_len: felt, value: felt*, out: Uint256* 86 | ) { 87 | if (value_len == 0) { 88 | return (); 89 | } 90 | let v = value[0]; 91 | let (high, low) = split_felt(v); 92 | assert out[0].high = high; 93 | assert out[0].low = low; 94 | return _felts_to_uints_inner(value_len - 1, value + 1, out + 2); 95 | } 96 | -------------------------------------------------------------------------------- /contracts/utilities/authorization.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin 4 | from starkware.starknet.common.syscalls import get_caller_address 5 | from contracts.vendor.openzeppelin.upgrades.library import Proxy 6 | 7 | //################### 8 | //################### 9 | //################### 10 | // Authorization patterns 11 | 12 | func _only{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(address: felt) { 13 | let (caller) = get_caller_address(); 14 | if ((caller - address) == 0) { 15 | return (); 16 | } 17 | // Failure 18 | with_attr error_message("You are not authorized to call this function") { 19 | assert 0 = 1; 20 | } 21 | return (); 22 | } 23 | 24 | func _onlyAdmin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { 25 | let (caller) = get_caller_address(); 26 | // Hardcoded briq team addresses. 27 | if ((caller - 0x03eF5b02BCc5D30f3f0D35d55F365E6388fE9501eca216Cb1596940bf41083E2) * (caller - 0x044Fb5366f2a8f9f8F24c4511fE86c15F39C220dcfecC730C6Ea51A335BC99CB) == 0) { 28 | return (); 29 | } 30 | // Older address from Sylve, temp 31 | if ((caller - 0x059dF66aF2E0E350842b11EA6B5a903b94640C4ff0418b04CceDcC320F531A08) == 0) { 32 | return (); 33 | } 34 | // Fallback to the proxy admin. 35 | Proxy.assert_only_admin(); 36 | return (); 37 | } 38 | 39 | func _onlyAdminAnd{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(address: felt) { 40 | let (caller) = get_caller_address(); 41 | if ((caller - address) == 0) { 42 | return (); 43 | } 44 | _onlyAdmin(); 45 | return (); 46 | } 47 | -------------------------------------------------------------------------------- /contracts/utilities/token_uri.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin, BitwiseBuiltin 4 | from starkware.cairo.common.alloc import alloc 5 | from starkware.cairo.common.registers import get_label_location 6 | from starkware.cairo.common.math import split_int 7 | from starkware.cairo.common.memcpy import memcpy 8 | 9 | from contracts.vendor.cairopen.string.ASCII import StringCodec 10 | 11 | from starkware.cairo.common.uint256 import Uint256 12 | from starkware.cairo.common.bitwise import bitwise_and 13 | 14 | from contracts.utilities.Uint256_felt_conv import _uint_to_felt, _felt_to_uint 15 | 16 | 17 | @event 18 | func URI(_value_len: felt, _value: felt*, _id: Uint256) { 19 | } 20 | 21 | namespace TokenURIHelpers { 22 | 23 | func _onUriChange{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 24 | token_id: felt, new_uri_len: felt, new_uri: felt* 25 | ) { 26 | alloc_locals; 27 | let (tk) = _felt_to_uint(token_id); 28 | URI.emit(new_uri_len, new_uri, tk); 29 | return (); 30 | } 31 | 32 | func _getUrl{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 33 | token_id: felt, label: codeoffset 34 | ) -> (uri_len: felt, uri: felt*) { 35 | alloc_locals; 36 | 37 | let (data_address) = get_label_location(label); 38 | let (local uri_out: felt*) = alloc(); 39 | assert uri_out[0] = data_address[0]; 40 | assert uri_out[1] = data_address[1]; 41 | // TODO: change on a per-network basis? 42 | assert uri_out[2] = data_address[2]; 43 | 44 | // Parse the token ID as a string. 45 | let (outout: felt*) = alloc(); 46 | split_int(token_id, 4, 10**20, 2**80, outout); 47 | let (sum) = add_number(4, 3, outout, uri_out, 1); 48 | assert uri_out[sum] = data_address[3]; 49 | 50 | return (sum + 1, uri_out); 51 | } 52 | 53 | func padl{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 54 | n: felt, data: felt* 55 | ) -> felt { 56 | if (n == 0) { 57 | return (20); 58 | } 59 | assert data[0] = '0'; 60 | return padl(n - 1, data + 1); 61 | } 62 | 63 | 64 | func add_number{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 65 | i: felt, sum: felt, data_in: felt*, data_out: felt*, leading_zero: felt, 66 | ) -> (sum: felt) { 67 | alloc_locals; 68 | if (i == 0) { 69 | return (sum,); 70 | } 71 | let (token_id_ascii) = StringCodec.felt_to_string(data_in[i - 1]); 72 | if (data_in[i - 1] == 0 and leading_zero == 1) { 73 | return add_number(i - 1, sum, data_in, data_out, 1); 74 | } 75 | let toto = (20 - token_id_ascii.len) * (1 - leading_zero); 76 | padl(toto, data_out + sum); 77 | memcpy(data_out + sum + toto, token_id_ascii.data, token_id_ascii.len); 78 | return add_number(i - 1, sum + toto + token_id_ascii.len, data_in, data_out, 0); 79 | } 80 | } -------------------------------------------------------------------------------- /contracts/vendor/cairopen/binary/README.md: -------------------------------------------------------------------------------- 1 | # Bits manipulation 2 | 3 | This allows to represent long lists of bits and to perform common operations on them. This list will be represented by a list of words (felts) each containing up to 32 bits, and a felt containing the total number of bits. 4 | 5 | ## Bits 6 | 7 | Import 8 | 9 | ```cairo 10 | from cairopen.binary.bits import Bits 11 | ``` 12 | 13 | ### ``Bits.extract`` 14 | Write len bits from input to output, starting at start. 15 | 16 | Arguments 17 | 18 | - `input (felt*)`: The input bits as 32-bit integers 19 | - `start (felt)`: The starting bit index (included) 20 | - `len (felt)`: The amount of bits to write 21 | - `output (felt*)`: Where to write the output 22 | 23 | Implicit arguments 24 | 25 | - `range_check_ptr (felt)` 26 | 27 | Usage example 28 | 29 | ```cairo 30 | @view 31 | func test_extract{range_check_ptr}(): 32 | alloc_locals 33 | let (input) = alloc() 34 | # 01001000011001010110110001101100 35 | assert input[0] = 1214606444 36 | # 01101111001000000111011101101111 37 | assert input[1] = 1864398703 38 | # 01110010011011000110010000000000 39 | assert input[2] = 1919706112 40 | 41 | # two words, no shift, len = two words 42 | let (output) = alloc() 43 | Bits.extract(input, 0, 64, output) 44 | # 01001000011001010110110001101100 45 | assert output[0] = 1214606444 46 | # 01101111001000000111011101101111 47 | assert output[1] = 1864398703 48 | 49 | return () 50 | end 51 | ``` 52 | 53 | --- 54 | 55 | ### ``Bits.merge`` 56 | Allows to merge two lists of bits into one. 57 | 58 | Arguments 59 | 60 | - `a (felt*)`: The first bits list 61 | - `a_nb_bits (felt)`: The bit length of a 62 | - `b (felt*)`: The first bits list 63 | - `b_nb_bits (felt)`: The bit length of b 64 | 65 | Implicit arguments 66 | 67 | - `range_check_ptr (felt)` 68 | 69 | Returns 70 | 71 | - `merged (felt*)`: The merge bit list a::b 72 | - `merged_nb_bits (felt)`: The bit length of merged 73 | 74 | Usage example 75 | 76 | ```cairo 77 | @view 78 | func test_merge{range_check_ptr}(): 79 | alloc_locals 80 | let (a) = alloc() 81 | # 01101111001000000111011101101111 82 | assert a[0] = 1864398703 83 | # 01110010011011000110010000000000 84 | assert a[1] = 1919706112 85 | # 32+22=54 86 | let a_nb_bits = 54 87 | 88 | let (b) = alloc() 89 | # 01101111001000000111011101101110 90 | assert b[0] = 1864398702 91 | # 31 (last 0 doesn't count) 92 | let b_nb_bits = 31 93 | 94 | let (c, c_bits) = Bits.merge(a, a_nb_bits, b, b_nb_bits) 95 | 96 | assert c[0] = 1864398703 97 | assert c[1] = 1919706556 98 | assert c[2] = 2178791424 99 | 100 | return () 101 | end 102 | ``` 103 | 104 | --- 105 | 106 | ### ``Bits.rightshift`` 107 | Allows you to apply a binary rightshift to a word. 108 | 109 | --- 110 | 111 | ### ``Bits.leftshift`` 112 | Allows you to apply a binary leftship to a word. 113 | 114 | --- 115 | 116 | ### ``Bits.rightrotate`` 117 | Allows you to shift the bits to the right and return by the left to a word. 118 | 119 | --- 120 | 121 | ### ``Bits.negate`` 122 | Returns the binary negation of a word. 123 | -------------------------------------------------------------------------------- /contracts/vendor/cairopen/hash/README.md: -------------------------------------------------------------------------------- 1 | # Sha256 computation 2 | 3 | This lib depends on bits.cairo from ``cairopen/binary/``. 4 | 5 | ## sha256 6 | 7 | ### ``sha256`` 8 | 9 | This allows to calculate the sha256 hash of an input of any bit length. 10 | 11 | Arguments 12 | 13 | - `input (felt*)`: bits array of 32-bit words 14 | - `n_bits (felt)`: The bit length of input 15 | 16 | Implicit arguments 17 | 18 | - `bitwise_ptr (BitwiseBuiltin*)` 19 | - `range_check_ptr (felt)` 20 | 21 | Returns 22 | 23 | - `output (felt*)`: Hashed input as an array of 8 32-bit words (big endian) 24 | 25 | Import 26 | 27 | ```cairo 28 | from cairopen.math.array import concat_arr 29 | ``` 30 | 31 | Usage example 32 | 33 | ``sha256("hey guys")`` = ``"be83351937c9a13e0d0e16ae97ee46915e790cf9a5d55fa317014539009f2101"`` 34 | Which if broken down into 32-bit words, gives : 35 | - 3196269849 36 | - 935960894 37 | - 219027118 38 | - 2548975249 39 | - 1584991481 40 | - 2782224291 41 | - 385959225 42 | - 10428673 43 | 44 | ```cairo 45 | @view 46 | func test_sha256{bitwise_ptr : BitwiseBuiltin*, range_check_ptr}(): 47 | 48 | # let's hash "hey guys" 49 | let (hash) = sha256(new ('hey ', 'guys'), 64) 50 | let a = hash[0] 51 | assert a = 3196269849 52 | let b = hash[1] 53 | assert b = 935960894 54 | let c = hash[2] 55 | assert c = 219027118 56 | let d = hash[3] 57 | assert d = 2548975249 58 | let e = hash[4] 59 | assert e = 1584991481 60 | let f = hash[5] 61 | assert f = 2782224291 62 | let g = hash[6] 63 | assert g = 385959225 64 | let h = hash[7] 65 | assert h = 10428673 66 | 67 | return () 68 | end 69 | ``` 70 | -------------------------------------------------------------------------------- /contracts/vendor/cairopen/string/constants.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | // @dev The maximum character length of a short string 4 | const SHORT_STRING_MAX_LEN = 31; 5 | 6 | // @dev The maximum value for a short string of 31 characters (= 0b11...11 = 0xff...ff) 7 | const SHORT_STRING_MAX_VALUE = 2 ** 248 - 1; 8 | 9 | // @dev The maximum index for felt* in one direction given str[i] for i in [-2**15, 2**15) 10 | const STRING_MAX_LEN = 2 ** 15; 11 | -------------------------------------------------------------------------------- /contracts/vendor/cairopen/string/libs/manipulation.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.cairo.common.cairo_builtins import BitwiseBuiltin 4 | from starkware.cairo.common.alloc import alloc 5 | 6 | from cairopen.math.array import concat_felt_arr 7 | from cairopen.string.string import String 8 | 9 | // concat 10 | func manipulation_concat{range_check_ptr}(str1: String, str2: String) -> (str: String) { 11 | let (concat_len, concat) = concat_felt_arr(str1.len, str1.data, str2.len, str2.data); 12 | return (String(concat_len, concat),); 13 | } 14 | 15 | // append character 16 | func manipulation_append_char{range_check_ptr}(base: String, char: felt) -> (str: String) { 17 | assert base.data[base.len] = char; 18 | 19 | return (String(base.len + 1, base.data),); 20 | } 21 | 22 | // path join 23 | func manipulation_path_join{range_check_ptr}(path1: String, path2: String) -> (path: String) { 24 | if (path1.data[path1.len - 1] == '/') { 25 | let (path) = manipulation_concat(path1, path2); 26 | return (path,); 27 | } 28 | 29 | let (slash_base) = manipulation_append_char(path1, '/'); 30 | let (path) = manipulation_concat(slash_base, path2); 31 | return (path,); 32 | } 33 | -------------------------------------------------------------------------------- /contracts/vendor/cairopen/string/string.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | // @dev A struct to represent strings 4 | // @member len (felt): The length of the string 5 | // @member data (felt*): The string as a char array 6 | struct String { 7 | len: felt, 8 | data: felt*, 9 | } 10 | -------------------------------------------------------------------------------- /contracts/vendor/cairopen/string/utils.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from cairopen.string.string import String 4 | from cairopen.string.libs.manipulation import ( 5 | manipulation_concat, 6 | manipulation_append_char, 7 | manipulation_path_join, 8 | ) 9 | 10 | namespace StringUtil { 11 | // @dev Concatenates two Strings together 12 | // @implicit range_check_ptr (felt) 13 | // @param str1 (String): The first String 14 | // @param str2 (String): The second String 15 | // @return str (String): The appended String 16 | func concat{range_check_ptr}(str1: String, str2: String) -> (str: String) { 17 | return manipulation_concat(str1, str2); 18 | } 19 | 20 | // @dev Appends a **single** char as a short string to a String 21 | // @implicit range_check_ptr (felt) 22 | // @param base (String): The base String 23 | // @param char (felt): The character to append 24 | // @return str (String): The appended String 25 | func append_char{range_check_ptr}(base: String, char: felt) -> (str: String) { 26 | return manipulation_append_char(base, char); 27 | } 28 | 29 | // @dev Joins to Strings together and adding a '/' in between if needed 30 | // @implicit range_check_ptr (felt) 31 | // @param path1 (String): The path start 32 | // @param path2 (String): The path end 33 | // @return path (String): The full path 34 | func path_join{range_check_ptr}(path1: String, path2: String) -> (path: String) { 35 | return manipulation_path_join(path1, path2); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /contracts/vendor/caistring/array.cairo: -------------------------------------------------------------------------------- 1 | // https://github.com/marcellobardus/starknet-l2-storage-verifier/blob/master/contracts/starknet/lib/concat_arr.cairo 2 | // https://github.com/sekai-studio/starknet-libs/blob/main/cairo_string/Array.cairo 3 | 4 | from starkware.cairo.common.memcpy import memcpy 5 | from starkware.cairo.common.alloc import alloc 6 | 7 | func array_concat{range_check_ptr}(arr1_len: felt, arr1: felt*, arr2_len: felt, arr2: felt*) -> ( 8 | res_len: felt, res: felt* 9 | ) { 10 | alloc_locals; 11 | 12 | let (local res: felt*) = alloc(); 13 | memcpy(res, arr1, arr1_len); 14 | memcpy(res + arr1_len, arr2, arr2_len); 15 | 16 | return (arr1_len + arr2_len, res); 17 | } 18 | -------------------------------------------------------------------------------- /contracts/vendor/openzeppelin/access/ownable/library.cairo: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts for Cairo v0.4.0b (access/ownable/library.cairo) 3 | 4 | %lang starknet 5 | 6 | from starkware.starknet.common.syscalls import get_caller_address 7 | from starkware.cairo.common.cairo_builtins import HashBuiltin 8 | from starkware.cairo.common.math import assert_not_zero 9 | 10 | // 11 | // Events 12 | // 13 | 14 | @event 15 | func OwnershipTransferred(previousOwner: felt, newOwner: felt) { 16 | } 17 | 18 | // 19 | // Storage 20 | // 21 | 22 | @storage_var 23 | func Ownable_owner() -> (owner: felt) { 24 | } 25 | 26 | namespace Ownable { 27 | // 28 | // Initializer 29 | // 30 | 31 | func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(owner: felt) { 32 | _transfer_ownership(owner); 33 | return (); 34 | } 35 | 36 | // 37 | // Guards 38 | // 39 | 40 | func assert_only_owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { 41 | let (owner) = Ownable.owner(); 42 | let (caller) = get_caller_address(); 43 | with_attr error_message("Ownable: caller is the zero address") { 44 | assert_not_zero(caller); 45 | } 46 | with_attr error_message("Ownable: caller is not the owner") { 47 | assert owner = caller; 48 | } 49 | return (); 50 | } 51 | 52 | // 53 | // Public 54 | // 55 | 56 | func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) { 57 | return Ownable_owner.read(); 58 | } 59 | 60 | func transfer_ownership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 61 | new_owner: felt 62 | ) { 63 | with_attr error_message("Ownable: new owner is the zero address") { 64 | assert_not_zero(new_owner); 65 | } 66 | assert_only_owner(); 67 | _transfer_ownership(new_owner); 68 | return (); 69 | } 70 | 71 | func renounce_ownership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { 72 | assert_only_owner(); 73 | _transfer_ownership(0); 74 | return (); 75 | } 76 | 77 | // 78 | // Internal 79 | // 80 | 81 | func _transfer_ownership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 82 | new_owner: felt 83 | ) { 84 | let (previous_owner: felt) = Ownable.owner(); 85 | Ownable_owner.write(new_owner); 86 | OwnershipTransferred.emit(previous_owner, new_owner); 87 | return (); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /contracts/vendor/openzeppelin/token/erc20/IERC20.cairo: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts for Cairo v0.4.0b (token/erc20/IERC20.cairo) 3 | 4 | %lang starknet 5 | 6 | from starkware.cairo.common.uint256 import Uint256 7 | 8 | @contract_interface 9 | namespace IERC20 { 10 | func name() -> (name: felt) { 11 | } 12 | 13 | func symbol() -> (symbol: felt) { 14 | } 15 | 16 | func decimals() -> (decimals: felt) { 17 | } 18 | 19 | func totalSupply() -> (totalSupply: Uint256) { 20 | } 21 | 22 | func balanceOf(account: felt) -> (balance: Uint256) { 23 | } 24 | 25 | func allowance(owner: felt, spender: felt) -> (remaining: Uint256) { 26 | } 27 | 28 | func transfer(recipient: felt, amount: Uint256) -> (success: felt) { 29 | } 30 | 31 | func transferFrom(sender: felt, recipient: felt, amount: Uint256) -> (success: felt) { 32 | } 33 | 34 | func approve(spender: felt, amount: Uint256) -> (success: felt) { 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /contracts/vendor/openzeppelin/token/erc20/presets/ERC20.cairo: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts for Cairo v0.4.0b (token/erc20/presets/ERC20.cairo) 3 | 4 | %lang starknet 5 | 6 | from starkware.cairo.common.cairo_builtins import HashBuiltin 7 | from starkware.cairo.common.uint256 import Uint256 8 | 9 | from openzeppelin.token.erc20.library import ERC20 10 | 11 | @constructor 12 | func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 13 | name: felt, symbol: felt, decimals: felt, initial_supply: Uint256, recipient: felt 14 | ) { 15 | ERC20.initializer(name, symbol, decimals); 16 | ERC20._mint(recipient, initial_supply); 17 | return (); 18 | } 19 | 20 | // 21 | // Getters 22 | // 23 | 24 | @view 25 | func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) { 26 | return ERC20.name(); 27 | } 28 | 29 | @view 30 | func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) { 31 | return ERC20.symbol(); 32 | } 33 | 34 | @view 35 | func totalSupply{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( 36 | totalSupply: Uint256 37 | ) { 38 | let (totalSupply: Uint256) = ERC20.total_supply(); 39 | return (totalSupply=totalSupply); 40 | } 41 | 42 | @view 43 | func decimals{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( 44 | decimals: felt 45 | ) { 46 | return ERC20.decimals(); 47 | } 48 | 49 | @view 50 | func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(account: felt) -> ( 51 | balance: Uint256 52 | ) { 53 | return ERC20.balance_of(account); 54 | } 55 | 56 | @view 57 | func allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 58 | owner: felt, spender: felt 59 | ) -> (remaining: Uint256) { 60 | return ERC20.allowance(owner, spender); 61 | } 62 | 63 | // 64 | // Externals 65 | // 66 | 67 | @external 68 | func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 69 | recipient: felt, amount: Uint256 70 | ) -> (success: felt) { 71 | return ERC20.transfer(recipient, amount); 72 | } 73 | 74 | @external 75 | func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 76 | sender: felt, recipient: felt, amount: Uint256 77 | ) -> (success: felt) { 78 | return ERC20.transfer_from(sender, recipient, amount); 79 | } 80 | 81 | @external 82 | func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 83 | spender: felt, amount: Uint256 84 | ) -> (success: felt) { 85 | return ERC20.approve(spender, amount); 86 | } 87 | 88 | @external 89 | func increaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 90 | spender: felt, added_value: Uint256 91 | ) -> (success: felt) { 92 | return ERC20.increase_allowance(spender, added_value); 93 | } 94 | 95 | @external 96 | func decreaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 97 | spender: felt, subtracted_value: Uint256 98 | ) -> (success: felt) { 99 | return ERC20.decrease_allowance(spender, subtracted_value); 100 | } 101 | -------------------------------------------------------------------------------- /contracts/vendor/openzeppelin/token/erc721/IERC721.cairo: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts for Cairo v0.4.0b (token/erc721/IERC721.cairo) 3 | 4 | %lang starknet 5 | 6 | from starkware.cairo.common.uint256 import Uint256 7 | 8 | @contract_interface 9 | namespace IERC721 { 10 | func balanceOf(owner: felt) -> (balance: Uint256) { 11 | } 12 | 13 | func ownerOf(tokenId: Uint256) -> (owner: felt) { 14 | } 15 | 16 | func safeTransferFrom(from_: felt, to: felt, tokenId: Uint256, data_len: felt, data: felt*) { 17 | } 18 | 19 | func transferFrom(from_: felt, to: felt, tokenId: Uint256) { 20 | } 21 | 22 | func approve(approved: felt, tokenId: Uint256) { 23 | } 24 | 25 | func setApprovalForAll(operator: felt, approved: felt) { 26 | } 27 | 28 | func getApproved(tokenId: Uint256) -> (approved: felt) { 29 | } 30 | 31 | func isApprovedForAll(owner: felt, operator: felt) -> (isApproved: felt) { 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /contracts/vendor/openzeppelin/token/erc721/IERC721Metadata.cairo: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts for Cairo v0.4.0b (token/erc721/IERC721Metadata.cairo) 3 | 4 | %lang starknet 5 | 6 | from starkware.cairo.common.uint256 import Uint256 7 | 8 | @contract_interface 9 | namespace IERC721Metadata { 10 | func name() -> (name: felt) { 11 | } 12 | 13 | func symbol() -> (symbol: felt) { 14 | } 15 | 16 | func tokenURI(tokenId: Uint256) -> (tokenURI: felt) { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /contracts/vendor/openzeppelin/token/erc721/IERC721Receiver.cairo: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts for Cairo v0.4.0b (token/erc721/IERC721Receiver.cairo) 3 | 4 | %lang starknet 5 | 6 | from starkware.cairo.common.uint256 import Uint256 7 | 8 | @contract_interface 9 | namespace IERC721Receiver { 10 | func onERC721Received( 11 | operator: felt, from_: felt, tokenId: Uint256, data_len: felt, data: felt* 12 | ) -> (selector: felt) { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /contracts/vendor/openzeppelin/token/erc721/enumerable/IERC721Enumerable.cairo: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts for Cairo v0.4.0b (token/erc721/enumerable/IERC721Enumerable.cairo) 3 | 4 | %lang starknet 5 | 6 | from starkware.cairo.common.uint256 import Uint256 7 | 8 | @contract_interface 9 | namespace IERC721Enumerable { 10 | func totalSupply() -> (totalSupply: Uint256) { 11 | } 12 | 13 | func tokenByIndex(index: Uint256) -> (tokenId: Uint256) { 14 | } 15 | 16 | func tokenOfOwnerByIndex(owner: felt, index: Uint256) -> (tokenId: Uint256) { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /contracts/vendor/openzeppelin/token/erc721/enumerable/presets/utils/ERC721Holder.cairo: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts for Cairo v0.4.0b (token/erc721/presets/utils/ERC721Holder.cairo) 3 | 4 | %lang starknet 5 | 6 | from starkware.cairo.common.cairo_builtins import HashBuiltin 7 | from starkware.cairo.common.uint256 import Uint256 8 | 9 | from openzeppelin.utils.constants.library import IERC721_RECEIVER_ID 10 | 11 | from openzeppelin.introspection.erc165.library import ERC165 12 | 13 | @view 14 | func onERC721Received( 15 | operator: felt, from_: felt, tokenId: Uint256, data_len: felt, data: felt* 16 | ) -> (selector: felt) { 17 | return (selector=IERC721_RECEIVER_ID); 18 | } 19 | 20 | @view 21 | func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 22 | interfaceId: felt 23 | ) -> (success: felt) { 24 | return ERC165.supports_interface(interfaceId); 25 | } 26 | 27 | @constructor 28 | func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { 29 | ERC165.register_interface(IERC721_RECEIVER_ID); 30 | return (); 31 | } 32 | -------------------------------------------------------------------------------- /contracts/vendor/openzeppelin/upgrades/presets/Proxy.cairo: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts for Cairo v0.4.0b (upgrades/presets/Proxy.cairo) 3 | 4 | %lang starknet 5 | 6 | from starkware.cairo.common.cairo_builtins import HashBuiltin 7 | from starkware.starknet.common.syscalls import library_call, library_call_l1_handler 8 | from openzeppelin.upgrades.library import Proxy 9 | 10 | // 11 | // Constructor 12 | // 13 | 14 | @constructor 15 | func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 16 | implementation_hash: felt 17 | ) { 18 | Proxy._set_implementation_hash(implementation_hash); 19 | return (); 20 | } 21 | 22 | // 23 | // Fallback functions 24 | // 25 | 26 | @external 27 | @raw_input 28 | @raw_output 29 | func __default__{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 30 | selector: felt, calldata_size: felt, calldata: felt* 31 | ) -> (retdata_size: felt, retdata: felt*) { 32 | let (class_hash) = Proxy.get_implementation_hash(); 33 | 34 | let (retdata_size: felt, retdata: felt*) = library_call( 35 | class_hash=class_hash, 36 | function_selector=selector, 37 | calldata_size=calldata_size, 38 | calldata=calldata, 39 | ); 40 | return (retdata_size, retdata); 41 | } 42 | 43 | @l1_handler 44 | @raw_input 45 | func __l1_default__{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 46 | selector: felt, calldata_size: felt, calldata: felt* 47 | ) { 48 | let (class_hash) = Proxy.get_implementation_hash(); 49 | 50 | library_call_l1_handler( 51 | class_hash=class_hash, 52 | function_selector=selector, 53 | calldata_size=calldata_size, 54 | calldata=calldata, 55 | ); 56 | return (); 57 | } 58 | -------------------------------------------------------------------------------- /contracts/vendor/openzeppelin/utils/constants/library.cairo: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts for Cairo v0.4.0b (utils/constants/library.cairo) 3 | 4 | %lang starknet 5 | 6 | // 7 | // Numbers 8 | // 9 | 10 | const UINT8_MAX = 255; 11 | 12 | // 13 | // Interface Ids 14 | // 15 | 16 | // ERC165 17 | const IERC165_ID = 0x01ffc9a7; 18 | const INVALID_ID = 0xffffffff; 19 | 20 | // Account 21 | const IACCOUNT_ID = 0xa66bd575; 22 | 23 | // ERC721 24 | const IERC721_ID = 0x80ac58cd; 25 | const IERC721_RECEIVER_ID = 0x150b7a02; 26 | const IERC721_METADATA_ID = 0x5b5e139f; 27 | const IERC721_ENUMERABLE_ID = 0x780e9d63; 28 | 29 | // AccessControl 30 | const IACCESSCONTROL_ID = 0x7965db0b; 31 | 32 | // 33 | // Roles 34 | // 35 | 36 | const DEFAULT_ADMIN_ROLE = 0; 37 | -------------------------------------------------------------------------------- /docs/GenesisSale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briqNFT/briq-protocol/5db5f812812b3039e94ecce96506fe8dd94a1c69/docs/GenesisSale.png -------------------------------------------------------------------------------- /docs/Readme.md: -------------------------------------------------------------------------------- 1 | # briq protocol 2 | 3 | ## High-level overview 4 | 5 | The briq protocol is a token-backed, customisable NFT system. 6 | 7 | It has three core components: 8 | - `briqs`, or `briq tokens`, are the fundamental unit. They are the underlying token of the ecosystem, and can be either fungible or non-fungible. They have one on-chain characteristic: their _material_ (an arbitrary identifier). 9 | - `sets` are the NFTs of the briq ecosystem. They are made of `briqs`, and can be minted, transferred, burned at-will by their owner. Sets are defined by a 3D matrix of briqs, and contain data such as briq position, briq colors, etc. The full shape is not stored on-chain, but a hash is. 10 | - the `Attribute Registry` is a contract handling additional metadata for sets, such as 'This set is an official Genesis Collection set'. 11 | 12 | Additional components include: 13 | - `Booklets`, which are an 1155 NFT that can be 'wrapped' inside a `set` to mark it an official Genesis Collection set. This process is handled by the attributes registry and is transparent on the frontend. 14 | - The `box` contract, which are 1155 NFTs sold by briq for the Genesis sale. Boxes are regular NFTs that can be unboxed, i.e. burned in exchanged for a booklet NFT and briq tokens. 15 | - The `Auction` contract handles the Genesis auction sale. 16 | - `Shape` contracts deal with 3D shapes and are used by booklets to verify that the user is constructing the correct shapes. 17 | 18 | ![Genesis Sale Overview](./GenesisSale.png) 19 | 20 | ### Detailed contracts 21 | 22 | Contracts are upgradable for the foreseeable future. See [`contracts/upgrades/proxy.cairo`](../contracts/upgrades/proxy.cairo) for the core proxy contract. 23 | 24 | - [briq contract](briq/): somewhat ERC1155-like. Handles `briq tokens`. `briqs` have a material. The interface is material-based instead of token_id based as that is a more natural usage. 25 | - [set contract](set/): ERC721-like. Handles `sets`. Essentially a regular ERC721, but handles assembly/disassembly. When assembling, it becomes the owner of the underlying briq tokens, and vice-versa. This vastly reduces the gas costs when transferring sets. 26 | -------------------------------------------------------------------------------- /docs/attributes/AttributesRegistry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briqNFT/briq-protocol/5db5f812812b3039e94ecce96506fe8dd94a1c69/docs/attributes/AttributesRegistry.png -------------------------------------------------------------------------------- /docs/attributes/Readme.md: -------------------------------------------------------------------------------- 1 | # Attributes 2 | 3 | The briq ecosystem is intended to be flexible and extensible. This extensibility takes several form: 4 | - At the briq level, custom `materials` can be created. 5 | - At the set level, different on-chain `attributes` can be granted to sets. 6 | 7 | Examples of possible attributes include: 8 | - Official Genesis Collection sets 9 | - 'red' sets 10 | - a shortlist of set by a custom curator (for e.g. beauty) 11 | - Sets that can be used in a gicen Metaverse. 12 | 13 | Attributes can be granted to sets based on set properties, such as their 3D shape. They can also be dependent on external conditions, such as ownership of another NFT. 14 | 15 | The `Attributes Registry` contract does the bookeeping of set attributes. 16 | 17 | ![Attributes Registry Overview](./AttributesRegistry.png) 18 | 19 | ## Collections 20 | 21 | Attributes are grouped under 'collection', to make it possible to have different qualities as part of a single, recognizable group. 22 | An example of a collection is the `Genesis Collection` by briq, which groups all sets made from Genesis Booklets. 23 | 24 | Collections can be managed by third-parties, and can have limited supply and arbitrary rules. 25 | 26 | ## Attribute ID 27 | 28 | To make grouping in collections easier, the Attribute token ID contains the collection ID, defined as so: 29 | `Attribute ID` = `actual attribute identifier` * 2^192 + `collection ID` 30 | 31 | ## Different kinds of attributes 32 | 33 | Attributes come in 3 main categories: 34 | - 'Functional' attributes are just functions of a set's data, and can be verified on-chain at any given moment. These could include attributes based on the set shape or the set color. 35 | - 'Autonomous' attributes are handled directly by the `attributes registry`. Sets need to claim these attributes, and there can be a limited quantity of them available. Such attributes are managed by a third-party, the 'admin' of the collection. 36 | - Contract-backed attributes are delegated to an external contract. The `booklet` contract, an ERC1155, is an example of such a contract in the native briq ecosystem. These contracts can be arbitrarily complex and extensible themselves. 37 | 38 | For security reasons, contract-backed attributes must be whitelisted. 39 | For practical reasons, creating collections is also permissioned at the moment. 40 | Finally, functional attributes may only work on data present on-chain, and thus are currently limited. 41 | 42 | ### Abilities for collection 43 | 44 | TODO: expand on this. 45 | -------------------------------------------------------------------------------- /docs/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briqNFT/briq-protocol/5db5f812812b3039e94ecce96506fe8dd94a1c69/docs/banner.png -------------------------------------------------------------------------------- /docs/set/Readme.md: -------------------------------------------------------------------------------- 1 | # set contract 2 | 3 | The set contract can be found at [`contracts/set_nft.cairo`](../../contracts/set_nft.cairo). 4 | Implementation is found in the [`contracts/set_nft/`](../../contracts/set_nft/) folder. 5 | 6 | ## High-level overview 7 | 8 | The set contract is an augmented ERC 721. 9 | Unlike most ERC 721 collections, sets can be minted, burned and modified. The process of minting/burning is called assembling/disassembling. 10 | 11 | ## Assembling/Disassembling a set 12 | 13 | When minting a set, you pass a list of `briqs` to the assembly function. The `set` becomes the new owner of these tokens (essentially locking the tokens in the set). Inversely, when a set is disassembled/burned, the `briqs` are transferred back to the `set` owner. 14 | 15 | Note that for gas-efficiency, the full list of briqs is expected during assembly _and_ disassembly. 16 | 17 | ## Set token ID & token URI 18 | 19 | ### Token ID 20 | 21 | The set *token_id* is crafted very specifically. The requirements are: 22 | - The *token_id* should not conflict with any wallet address, or the set would become owner of the wallet's briqs (and vice-versa). 23 | - The *token_id* should be computable before the minting transaction is complete (for convenience). 24 | 25 | To enforce these contraints, the set token_id must not be **chosable**, but nonetheless **predictable**. 26 | The approach taken consists of on-chain hashing the wallet address & a 'hint' into the token_id. The top 192 bits are kept. 27 | Assuming pedersen_hashing is cryptographically secure, we avoid attackers creating `sets` with the same token_id as a wallet (a fortiori a specific wallet), while remaining predictable. 28 | 29 | ### token URI 30 | 31 | The set token URI is generally stored in its own storage_var. However, if the token URI is small enough (less than 310 bits total), we store part of it in the token ID, using the bits left free (see above, the token_id only uses 192 bits). The token ID is then frozen in place (further changes to the token URI will ignore the bit in the token ID). 32 | 33 | Instead of storing the total length, the token URI uses the top bits of a felt (248/249/250) to store whether the token URI continues. 34 | 35 | ## API 36 | 37 | In general, there are two versions of each view/external functions: 38 | - One taking `felt` arguments, that end with an extra `_`. 39 | - One taking `Uint256` arguments, that don't. 40 | 41 | The default interface takes `Uint256` for compatibility with the OpenZeppelin contracts, which are expected to become standard on Cairo. However, since sets are Cairo-native, the set contract uses `felt` internally for efficiency. 42 | 43 | ### Balance 44 | 45 | The usual ERC 721 functions are provided: `balanceOf`, `ownerOf`, along with `tokenOfOwnerByIndex` & `balanceDetailsOf` to get the full list. 46 | 47 | ### Transfer 48 | 49 | Only `transferFrom` is currently implemented. 50 | -------------------------------------------------------------------------------- /docs/squarebriq.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briqNFT/briq-protocol/5db5f812812b3039e94ecce96506fe8dd94a1c69/docs/squarebriq.jpg -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "briq-protocol" 7 | description = "briq NFT protocol contracts & python helpers" 8 | version = "0.1.0" 9 | readme = "README.md" 10 | requires-python = "~=3.9.0" 11 | license = "MIT" 12 | keywords = [] 13 | authors = [ 14 | { name = "briq", email = "founders@sltech.company" }, 15 | ] 16 | classifiers = [ 17 | "Development Status :: 4 - Beta", 18 | "Programming Language :: Python", 19 | "Programming Language :: Python :: 3.9", 20 | "Programming Language :: Python :: Implementation :: CPython", 21 | "Programming Language :: Python :: Implementation :: PyPy", 22 | ] 23 | dependencies = [ 24 | "cairo-lang", 25 | ] 26 | 27 | [project.urls] 28 | Documentation = "https://github.com/briqNFT/briq-protocol#readme" 29 | Issues = "https://github.com/briqNFT/briq-protocol/issues" 30 | Source = "https://github.com/briqNFT/briq-protocol" 31 | 32 | [tool.hatch.build] 33 | only-include = ["briq_protocol", "docs", "tests"] 34 | [tool.hatch.build.force-include] 35 | "artifacts" = "briq_protocol/artifacts" 36 | "contracts" = "briq_protocol/contracts" 37 | 38 | [tool.hatch.envs.default] 39 | python = "3.9" 40 | dependencies = [ 41 | "pytest>=7", 42 | "pytest-asyncio>=0.21", 43 | "flake8>=6.0", 44 | #"cairo-nile>=0.9", 45 | #"starknet-devnet@git+ssh://git@github.com/Shard-Labs/starknet-devnet.git", 46 | "starknet-py>=0.15.2", 47 | ] 48 | 49 | [tool.hatch.envs.default.scripts] 50 | test = "pytest" 51 | 52 | [tool.hatch.envs.devnet] 53 | python = "3.9" 54 | dependencies = [ 55 | "starknet-devnet@git+ssh://git@github.com/Shard-Labs/starknet-devnet.git", 56 | ] 57 | 58 | [tool.hatch.envs.devnet.scripts] 59 | devnet = "starknet-devnet" 60 | deploy = "starknet declare --contract ./target/dev/briq_protocol_AttributesRegistry.sierra.json --wallet=starkware.starknet.wallets.open_zeppelin.OpenZeppelinAccount --compiler_dir ./target/debug" 61 | -------------------------------------------------------------------------------- /scripts-old/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | compile () { 4 | starknet-compile --no_debug_info --cairo_path contracts/vendor/ --output artifacts/$1.json contracts/$2.cairo 5 | jq '.abi' --indent 4 artifacts/$1.json > artifacts/abis/$1.json 6 | } 7 | 8 | #nile compile --directory contracts/vendor/ \ 9 | # contracts/set_nft.cairo \ 10 | # contracts/briq.cairo \ 11 | # contracts/box_nft.cairo \ 12 | # contracts/booklet_nft.cairo \ 13 | # contracts/attributes_registry.cairo \ 14 | # contracts/auction.cairo \ 15 | # contracts/auction_onchain.cairo \ 16 | # contracts/shape_attribute.cairo \ 17 | # contracts/shape/shape_store.cairo \ 18 | # contracts/upgrades/proxy.cairo 19 | 20 | 21 | compile briq briq 22 | compile set_nft set_nft 23 | compile box_nft box_nft 24 | compile booklet_nft booklet_nft 25 | compile briq_factory briq_factory 26 | 27 | compile attributes_registry attributes_registry 28 | 29 | 30 | compile shape_store_ducks shape/shape_store_ducks 31 | compile shape_store_zenducks shape/shape_store_zenducks 32 | compile auction_onchain_data_goerli auction_onchain/data_testnet 33 | compile auction_onchain_data_mainnet auction_onchain/data_mainnet 34 | -------------------------------------------------------------------------------- /scripts-old/deploy.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from nile.nre import NileRuntimeEnvironment 4 | 5 | 6 | def run(nre: NileRuntimeEnvironment): 7 | if not os.getenv("ADMIN"): 8 | print("ADMIN env variable must be set to the address of the admin wallet") 9 | 10 | #os.environ["toto"] = "123456" 11 | #account = nre.get_or_deploy_account("toto") 12 | #print(f"Deployed deploy account to {account.address}") 13 | 14 | briq_addr, abi = nre.deploy("briq", arguments=[], alias="briq") 15 | set_addr, abi = nre.deploy("set", arguments=[], alias="set") 16 | 17 | briq_address, abi = nre.deploy("proxy", arguments=[os.getenv("ADMIN"), briq_addr], alias="briq_proxy") 18 | set_address, abi = nre.deploy("proxy", arguments=[os.getenv("ADMIN"), set_addr], alias="set_proxy") 19 | 20 | print(f"Deployed briq to {briq_address}") 21 | print(f"Deployed set to {set_address}") 22 | 23 | #account.send(briq_address, "setSetAddress_", [int(set_address, 16)]) 24 | #account.send(set_address, "setBriqAddress_", [int(briq_address, 16)]) 25 | -------------------------------------------------------------------------------- /scripts-old/deploy_briq_factory.sh: -------------------------------------------------------------------------------- 1 | source "$STARKNET_NETWORK_ID.test_node.txt" 2 | 3 | starknet_declare () { 4 | addr="$(starknet declare --contract $1 --nonce $nonce --max_fee 993215999380800)" 5 | echo $addr 6 | comm=$(echo "$addr" | grep 'Contract class hash' | awk '{gsub("Contract class hash: ", "",$0); print $0}') 7 | printf -v $2 $comm 8 | echo "$2=$comm" 9 | echo "$2=$comm" >> "$STARKNET_NETWORK_ID.test_node.txt" 10 | ((nonce=$nonce+1)) 11 | } 12 | 13 | deploy_proxy() { 14 | addr="$(starknet deploy --class_hash $proxy_hash --inputs $WALLET_ADDRESS $1 --nonce $nonce --max_fee 2220277007180367)" 15 | echo $addr 16 | comm=$(echo "$addr" | grep "Contract address: " | awk '{gsub("Contract address: ", "",$0); print $0}') 17 | printf -v $2 $comm 18 | echo "$2=$comm" 19 | echo "$2=$comm" >> "$STARKNET_NETWORK_ID.test_node.txt" 20 | ((nonce=$nonce+1)) 21 | } 22 | 23 | invoke () { 24 | tx=$(starknet invoke --address $1 --abi artifacts/abis/$2.json --function $3 --inputs $4 $5 $6 $7 --nonce $nonce --max_fee 12618293576158800) 25 | export tx_hash=$(echo $tx | grep "Transaction hash:" | awk '{gsub("Transaction hash: ", "",$0); print $0}') 26 | echo "$2 $3" 27 | echo "starknet get_transaction --hash $tx_hash" 28 | ((nonce=$nonce+1)) 29 | } 30 | 31 | 32 | call () { 33 | tx=$(starknet call --address $1 --abi artifacts/abis/$2.json --function $3 --inputs $4 $5 $6 $7) 34 | echo $tx 35 | } 36 | 37 | nonce=$(starknet get_nonce --contract_address $WALLET_ADDRESS) 38 | 39 | starknet_declare artifacts/briq.json briq_hash 40 | starknet_declare artifacts/briq_factory.json briq_factory_hash 41 | 42 | deploy_proxy $briq_factory_hash briq_factory_addr 43 | 44 | # Start at 300000 45 | invoke $briq_factory_addr briq_factory initialise 300000000000000000000000 0 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 46 | invoke $briq_factory_addr briq_factory setBriqAddress_ $briq_addr 47 | 48 | call $briq_factory_addr briq_factory get_price 30 49 | 50 | invoke $briq_addr box_nft upgradeImplementation_ $briq_hash 51 | invoke $briq_addr briq setFactoryAddress_ $briq_factory_addr 52 | 53 | # Upgrade 54 | nonce=$(starknet get_nonce --contract_address $WALLET_ADDRESS) 55 | echo $nonce 56 | 57 | starknet_declare artifacts/briq_factory.json briq_factory_hash 58 | invoke $briq_factory_addr briq_factory upgradeImplementation_ $briq_factory_hash 59 | -------------------------------------------------------------------------------- /scripts-old/node.sh: -------------------------------------------------------------------------------- 1 | # Run the starknet node and save/load it durably. 2 | # Run without load-path to reinit: 3 | # starknet-devnet --dump-path devnetstate --accounts 1 --dump-on transaction 4 | starknet-devnet --dump-path devnetstate --accounts 0 --dump-on transaction --load-path devnetstate 5 | -------------------------------------------------------------------------------- /scripts-old/setup_devnet_env.sh: -------------------------------------------------------------------------------- 1 | # NB: for now, run the starknet devnet node manually using 2 | # starknet-devnet --dump-path devnetstate --dump-on exit 3 | # then 4 | # starknet-devnet --dump-path devnetstate --dump-on exit --load-path devnetstate 5 | 6 | export STARKNET_GATEWAY_URL="http://127.0.0.1:5050" 7 | export STARKNET_FEEDER_GATEWAY_URL="http://127.0.0.1:5050" 8 | export STARKNET_NETWORK_ID="localhost" 9 | 10 | # export STARKNET_NETWORK="alpha-goerli" 11 | export STARKNET_CHAIN_ID=SN_GOERLI 12 | export STARKNET_WALLET=starkware.starknet.wallets.open_zeppelin.OpenZeppelinAccount 13 | 14 | # NB: may need to be done manually changing 15 | # rm ~/.starknet_accounts/starknet_open_zeppelin_accounts.json 16 | export ACCOUNT="test" 17 | # info stored in ~/.starknet_accounts/starknet_open_zeppelin_accounts.json 18 | comm=$(starknet deploy_account --account $ACCOUNT) 19 | comm=$(echo "$comm" | grep 'Contract address' | awk '{gsub("Contract address: ", "",$0); print $0}') 20 | export WALLET_ADDRESS=$comm 21 | # Mint some tokens to be able to deploy stuff 22 | curl -H "Content-Type: application/json" -d "{ \"address\": \"$WALLET_ADDRESS\", \"amount\": 5000000000000000000, \"lite\": 1 }" -X POST localhost:5050/mint 23 | -------------------------------------------------------------------------------- /scripts-old/setup_mainnet_env.sh: -------------------------------------------------------------------------------- 1 | export STARKNET_WALLET=starkware.starknet.wallets.open_zeppelin.OpenZeppelinAccount 2 | export STARKNET_NETWORK_ID="mainnet" 3 | export STARKNET_NETWORK="alpha-mainnet" 4 | export WALLET_ADDRESS="0x75341b8090a4257f22dafffe3a4cb882006bd26302720d6a80a1fde154a3430" 5 | PROMPT='%F{blue}MAINNET%f %1~ %# ' 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | # NB: for now, run the starknet devnet node manually using 15 | # starknet-devnet --dump-path devnetstate --accounts 0 --lite-mode --dump-on exit 16 | # then 17 | # starknet-devnet --dump-path devnetstate --accounts 0 --lite-mode --dump-on exit --load-path devnetstate 18 | 19 | export STARKNET_NETWORK_ID="mainnet" 20 | export STARKNET_NETWORK="alpha-mainnet" 21 | export STARKNET_WALLET=starkware.starknet.wallets.open_zeppelin.OpenZeppelinAccount 22 | 23 | export ACCOUNT="mainnet_deployer_1" 24 | # info stored in ~/.starknet_accounts/starknet_open_zeppelin_accounts.json 25 | comm=$(starknet deploy_account --account $ACCOUNT) 26 | comm=$(echo "$comm" | grep 'Contract address' | awk '{gsub("Contract address: ", "",$0); print $0}') 27 | export WALLET_ADDRESS=$comm 28 | 29 | ## Already deployed 30 | export ACCOUNT="mainnet_deployer_1" 31 | export WALLET_ADDRESS="0x75341b8090a4257f22dafffe3a4cb882006bd26302720d6a80a1fde154a3430" 32 | export TOKEN="0x269144bcf78891dbf22d757cbbe8e77c6a2a8868c379dc7d82fb94a04aa4b65" 33 | -------------------------------------------------------------------------------- /scripts-old/setup_testnet_env.sh: -------------------------------------------------------------------------------- 1 | export STARKNET_WALLET=starkware.starknet.wallets.open_zeppelin.OpenZeppelinAccount 2 | export STARKNET_NETWORK_ID="goerli" 3 | export STARKNET_NETWORK="alpha-goerli" 4 | export WALLET_ADDRESS="0x22030445da671e4f5bdab7802a061ca0c55754d9703c5390266fd8b814de880" 5 | PROMPT='%F{green}TESTNET%f %1~ %# ' 6 | 7 | 8 | 9 | 10 | # Below is old 11 | 12 | 13 | ##### 14 | 15 | # NB: for now, run the starknet devnet node manually using 16 | # starknet-devnet --dump-path devnetstate --accounts 0 --lite-mode --dump-on exit 17 | # then 18 | # starknet-devnet --dump-path devnetstate --accounts 0 --lite-mode --dump-on exit --load-path devnetstate 19 | 20 | export STARKNET_NETWORK_ID="goerli-2" 21 | export STARKNET_NETWORK="alpha-goerli2" 22 | export STARKNET_WALLET=starkware.starknet.wallets.open_zeppelin.OpenZeppelinAccount 23 | export GATEWAY_URL="https://alpha4-2.starknet.io" 24 | export FEEDER_GATEWAY_URL="https://alpha4-2.starknet.io" 25 | export TOKEN='0' 26 | 27 | export ACCOUNT="testnet_2_deployer_1" 28 | # info stored in ~/.starknet_accounts/starknet_open_zeppelin_accounts.json 29 | comm=$(starknet deploy_account --account $ACCOUNT) 30 | comm=$(echo "$comm" | grep 'Contract address' | awk '{gsub("Contract address: ", "",$0); print $0}') 31 | export WALLET_ADDRESS=$comm 32 | 33 | ## Already deployed 34 | export ACCOUNT="testnet_2_deployer_1" 35 | export WALLET_ADDRESS="0x0624aa94dd5121e18cfcef94f724706caa4dda69cb621c8b7a9b57fc2efc94ae" 36 | 37 | 38 | starknet new_account 39 | Account address: 0x033d820bae2318e0e0f93e6d68e36de7ba70de850a6c367eca64f3a9aa74e4f4 40 | Public key: 0x059dc5e6ebbb737014bcf7a5e4abad254e158b88d201ef90c81d5337c3db895b 41 | Move the appropriate amount of funds to the account, and then deploy the account 42 | by invoking the 'starknet deploy_account' command. 43 | 44 | NOTE: This is a modified version of the OpenZeppelin account contract. The signature is computed 45 | differently. 46 | 47 | export STARKNET_NETWORK_ID="goerli-2" 48 | export STARKNET_WALLET=starkware.starknet.wallets.open_zeppelin.OpenZeppelinAccount 49 | export STARKNET_NETWORK="alpha-goerli2" 50 | export WALLET_ADDRESS="0x33d820bae2318e0e0f93e6d68e36de7ba70de850a6c367eca64f3a9aa74e4f4" 51 | source goerli-2.test_node.txt -------------------------------------------------------------------------------- /scripts-old/wave2.sh: -------------------------------------------------------------------------------- 1 | 2 | starknet_declare () { 3 | addr="$(starknet declare --contract $1 --account $ACCOUNT --nonce $nonce --max_fee 993215999380800 --token $TOKEN)" 4 | echo $addr 5 | comm=$(echo "$addr" | grep 'Contract class hash' | awk '{gsub("Contract class hash: ", "",$0); print $0}') 6 | printf -v $2 $comm 7 | echo "$2=$comm" 8 | echo "$2=$comm" >> "$STARKNET_NETWORK_ID.test_node.txt" 9 | ((nonce=$nonce+1)) 10 | } 11 | 12 | invoke () { 13 | tx=$(starknet invoke --address $1 --abi artifacts/abis/$2.json --function $3 --inputs $4 $5 $6 $7 --account $ACCOUNT --nonce $nonce --max_fee 12618293576158800) 14 | export tx_hash=$(echo $tx | grep "Transaction hash:" | awk '{gsub("Transaction hash: ", "",$0); print $0}') 15 | echo "$2 $3" 16 | echo "starknet get_transaction --hash $tx_hash" 17 | ((nonce=$nonce+1)) 18 | } 19 | 20 | 21 | call () { 22 | tx=$(starknet call --address $1 --abi artifacts/abis/$2.json --function $3 --inputs $4 $5 $6 $7 --account $ACCOUNT) 23 | echo $tx 24 | } 25 | 26 | 27 | nonce=$(starknet get_nonce --contract_address $WALLET_ADDRESS) 28 | starknet_declare artifacts/box_nft_wave2.json box_nft_wave2_hash 29 | starknet_declare artifacts/shape_store_wave2.json shape_store_wave2 30 | 31 | echo $box_nft_wave2_hash 32 | 33 | call $box_addr box_nft getImplementation_ 34 | 35 | invoke $box_addr box_nft upgradeImplementation_ $box_nft_wave2_hash 36 | 37 | invoke $box_addr box_nft mint_ $auction_addr 4 150 38 | invoke $box_addr box_nft mint_ $auction_addr 5 50 39 | invoke $box_addr box_nft mint_ $auction_addr 6 5 40 | 41 | call $box_addr box_nft get_box_data 4 42 | 43 | 44 | nonce=$(starknet get_nonce --contract_address $WALLET_ADDRESS) 45 | starknet_declare artifacts/box_nft_wave3.json box_nft_wave3_hash 46 | 47 | echo $box_nft_wave3_hash 48 | 49 | call $box_addr box_nft getImplementation_ 50 | 51 | invoke $box_addr box_nft upgradeImplementation_ $box_nft_wave3_hash 52 | 53 | call $auction_addr auction get_auction_data 54 | 55 | invoke $box_addr box_nft mint_ $auction_addr 7 150 56 | invoke $box_addr box_nft mint_ $auction_addr 8 50 57 | invoke $box_addr box_nft mint_ $auction_addr 9 5 58 | 59 | call $box_addr box_nft get_box_data 4 60 | 61 | starknet_declare artifacts/shape_store_wave3.json shape_store_wave3 62 | -------------------------------------------------------------------------------- /scripts-old/wave_briqmas.sh: -------------------------------------------------------------------------------- 1 | 2 | starknet_declare () { 3 | addr="$(starknet declare --contract $1 --nonce $nonce --max_fee 993215999380800)" 4 | echo $addr 5 | comm=$(echo "$addr" | grep 'Contract class hash' | awk '{gsub("Contract class hash: ", "",$0); print $0}') 6 | printf -v $2 $comm 7 | echo "$2=$comm" 8 | echo "$2=$comm" >> "$STARKNET_NETWORK_ID.test_node.txt" 9 | ((nonce=$nonce+1)) 10 | } 11 | 12 | invoke () { 13 | tx=$(starknet invoke --address $1 --abi artifacts/abis/$2.json --function $3 --inputs $4 $5 $6 $7 --nonce $nonce --max_fee 12618293576158800) 14 | export tx_hash=$(echo $tx | grep "Transaction hash:" | awk '{gsub("Transaction hash: ", "",$0); print $0}') 15 | echo "$2 $3" 16 | echo "starknet get_transaction --hash $tx_hash" 17 | ((nonce=$nonce+1)) 18 | } 19 | 20 | 21 | call () { 22 | tx=$(starknet call --address $1 --abi artifacts/abis/$2.json --function $3 --inputs $4 $5 $6 $7 ) 23 | echo $tx 24 | } 25 | 26 | 27 | nonce=$(starknet get_nonce --contract_address $WALLET_ADDRESS) 28 | 29 | starknet_declare artifacts/box_nft_briqmas.json box_nft_briqmas 30 | starknet_declare artifacts/shape_store_briqmas.json shape_store_briqmas 31 | 32 | echo $box_nft_briqmas 33 | 34 | call $box_addr box_nft getImplementation_ 35 | 36 | invoke $box_addr box_nft upgradeImplementation_ $box_nft_briqmas 37 | invoke $box_addr box_nft upgradeImplementation_ 0x33ab4545de0f0e6b1796369bd9db5e517d5701e39612fa1a5cbc6659424adef 38 | 39 | call $box_addr box_nft get_box_data 10 40 | 41 | #invoke $box_addr box_nft mint_ $auction_addr 4 150 42 | #invoke $box_addr box_nft mint_ $auction_addr 5 50 43 | #invoke $box_addr box_nft mint_ $auction_addr 6 5 44 | -------------------------------------------------------------------------------- /scripts/katana_account.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "variant": { 4 | "type": "open_zeppelin", 5 | "version": 1, 6 | "public_key": "0x02b191c2f3ecf685a91af7cf72a43e7b90e2e41220175de5c4f7498981b10053" 7 | }, 8 | "deployment": { 9 | "status": "deployed", 10 | "class_hash": "0x04d07e40e93398ed3c76981e72dd1fd22557a78ce36c0515f679e27f0bb5bc5f", 11 | "address": "0x517ececd29116499f4a1b64b094da79ba08dfd54a3edaa316134c41f8160973" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /scripts/katana_signer.json: -------------------------------------------------------------------------------- 1 | {"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"8b479330a29124faa645d5592262cdf1"},"ciphertext":"cf0b648f8d9534b538050ea4c1419a9ded59488de296e92fbeae0a5eaba38673","kdf":"scrypt","kdfparams":{"dklen":32,"n":8192,"p":1,"r":8,"salt":"1f45d6f997fedcaaf58aaa9c0b4fd802c7cd9d0b46eb3dfcf564529b3034d6a7"},"mac":"3e5055ad520eda34bd2eb5db51a20a36c5fad3cc45360e32c268b06861c7c30c"},"id":"f6ff3e06-ae48-4251-9c88-ad24f29efe09","version":3} -------------------------------------------------------------------------------- /scripts/setup_katana.sh: -------------------------------------------------------------------------------- 1 | # Run katana 2 | 3 | # Configure for starkli 4 | export STARKNET_RPC="http://localhost:5050/" 5 | export STARKNET_RPC_URL="$STARKNET_RPC" 6 | 7 | # password is katana 8 | export STARKNET_KEYSTORE="scripts/katana_signer.json" 9 | export KEYSTORE_PWD="katana" 10 | 11 | export STARKNET_ACCOUNT="scripts/katana_account.json" 12 | export ACCOUNT_ADDRESS=$(jq .deployment.address $STARKNET_ACCOUNT -r) 13 | 14 | export TREASURY_ADDRESS=$ACCOUNT_ADDRESS 15 | 16 | # https://github.com/dojoengine/dojo/blob/main/crates/katana/core/src/constants.rs 17 | # katana predeployed fee_token_address (uses transferFrom..) 18 | export FEE_TOKEN_ADDR="0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7" 19 | 20 | export DOJO_ACCOUNT_ADDRESS="$ACCOUNT_ADDRESS" 21 | -------------------------------------------------------------------------------- /src-old/booklet_nft/attribute.cairo: -------------------------------------------------------------------------------- 1 | 2 | #[abi] 3 | trait IShapeContract { 4 | fn _shape(i: felt252) -> (Array::, Array::); 5 | fn check_shape_numbers_(global_index: felt252, shape: Array, fts: Array, nfts: Array); 6 | } 7 | 8 | use starknet::class_hash; 9 | use traits::TryInto; 10 | use option::OptionTrait; 11 | 12 | use briq_protocol::utils; 13 | use briq_protocol::booklet_nft::token_uri::toShapeContract::get_shape_contract_; 14 | use briq_protocol::types::ShapeItem; 15 | use briq_protocol::types::FTSpec; 16 | use briq_protocol::ecosystem::to_attributes_registry::toAttributesRegistry::_onlyAttributesRegistry; 17 | use briq_protocol::constants; 18 | 19 | use briq_protocol::library_erc1155::transferability::Transferability; 20 | use briq_protocol::ecosystem::genesis_collection::GENESIS_COLLECTION; 21 | //########### 22 | //########### 23 | 24 | fn _check_shape( 25 | attribute_id: felt252, 26 | shape: Array, 27 | fts: Array, 28 | nfts: Array, 29 | ) { 30 | // Check that the shape matches the passed data 31 | let addr = get_shape_contract_(attribute_id); 32 | 33 | // TEMP HACK because my original code hardcoded GENESIS_COLLECTION here. 34 | // Need to update the shape contracts that the booklets point to. 35 | let coll = (attribute_id - GENESIS_COLLECTION) / constants::c2_192; 36 | //let is_genesis = is_le_felt252(coll, 2**63); 37 | let is_genesis = coll & constants::c2_64_mask; 38 | if (is_genesis == GENESIS_COLLECTION) { 39 | IShapeContractLibraryDispatcher { class_hash: addr.try_into().unwrap() }.check_shape_numbers_( 40 | coll, shape, fts, nfts 41 | ); 42 | } else { 43 | IShapeContractLibraryDispatcher { class_hash: addr.try_into().unwrap() }.check_shape_numbers_( 44 | attribute_id, shape, fts, nfts 45 | ); 46 | } 47 | return (); 48 | } 49 | 50 | //@external 51 | fn assign_attribute( 52 | owner: felt252, 53 | set_token_id: felt252, 54 | attribute_id: felt252, 55 | shape: Array, 56 | fts: Array, 57 | nfts: Array, 58 | ) { 59 | _onlyAttributesRegistry(); 60 | 61 | _check_shape(attribute_id, shape, fts, nfts); 62 | 63 | // Transfer the booklet to the set. 64 | // The owner of the set must also be the owner of the booklet. 65 | Transferability::_transfer(owner, set_token_id, attribute_id, 1); 66 | } 67 | 68 | //@external 69 | fn remove_attribute( 70 | owner: felt252, 71 | set_token_id: felt252, 72 | attribute_id: felt252, 73 | ) { 74 | _onlyAttributesRegistry(); 75 | 76 | // Give the booklet back to the original set owner. 77 | Transferability::_transfer(set_token_id, owner, attribute_id, 1); 78 | } 79 | -------------------------------------------------------------------------------- /src-old/booklet_nft/minting.cairo: -------------------------------------------------------------------------------- 1 | use traits::Into; 2 | use traits::TryInto; 3 | use option::OptionTrait; 4 | 5 | use starknet::contract_address; 6 | 7 | use briq_protocol::library_erc1155; 8 | use briq_protocol::library_erc1155::transferability::Transferability; 9 | use briq_protocol::booklet_nft::token_uri; 10 | use briq_protocol::utils::GetCallerAddress; 11 | use briq_protocol::utils; 12 | use briq_protocol::constants; 13 | 14 | use briq_protocol::ecosystem::genesis_collection::DUCKS_COLLECTION; 15 | use briq_protocol::ecosystem::to_box::toBox; 16 | use briq_protocol::utilities::authorization::Auth::_onlyAdminAnd; 17 | 18 | use briq_protocol::booklet_nft::token_uri::toShapeContract; 19 | 20 | //@external 21 | fn mint_(owner: felt252, token_id: felt252, shape_contract: felt252) { 22 | library_erc1155::balance::Balance::_increaseBalance(owner, token_id, 1); 23 | 24 | toShapeContract::_shape_contract::write(token_id, shape_contract); 25 | 26 | let caller = GetCallerAddress(); 27 | // Can only be minted by the box contract or an admin of the contract. 28 | if (caller == 0x02ef9325a17d3ef302369fd049474bc30bfeb60f59cca149daa0a0b7bcc278f8) { 29 | // Allow OutSmth to mint ducks. 30 | let tid = (token_id - DUCKS_COLLECTION) / constants::c2_192; 31 | // Check this is below an arbitrary low number to make sure the range is correct 32 | assert(tid < 10000, 'Invalid token id'); 33 | 34 | Transferability::TransferSingle(caller, 0, owner, token_id.into(), 1.into()); 35 | return (); 36 | } else { 37 | _onlyAdminAnd(toBox::get().try_into().unwrap()); 38 | 39 | Transferability::TransferSingle(caller, 0, owner, token_id.into(), 1.into()); 40 | return (); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src-old/booklet_nft/token_uri.cairo: -------------------------------------------------------------------------------- 1 | use traits::TryInto; 2 | use option::OptionTrait; 3 | 4 | use starknet::class_hash; 5 | 6 | use briq_protocol::utils; 7 | 8 | use briq_protocol::types::ShapeItem; 9 | use briq_protocol::types::FTSpec; 10 | use briq_protocol::constants; 11 | 12 | use briq_protocol::ecosystem::genesis_collection::GENESIS_COLLECTION; 13 | 14 | #[abi] 15 | trait IShapeContract { 16 | fn shape_(i: felt252) -> (Array::, Array::); 17 | } 18 | 19 | #[contract] 20 | mod toShapeContract { 21 | struct Storage { 22 | _shape_contract: LegacyMap, 23 | } 24 | 25 | //#[view] 26 | fn get_shape_contract_(token_id: felt252) -> felt252 { //(address: felt252) { 27 | return _shape_contract::read(token_id); 28 | } 29 | } 30 | 31 | //@view 32 | fn get_shape_(token_id: felt252) -> (Array::, Array::) { 33 | let addr = toShapeContract::_shape_contract::read(token_id); 34 | return IShapeContractLibraryDispatcher { class_hash: addr.try_into().unwrap() }.shape_((token_id - GENESIS_COLLECTION) / constants::c2_192);//2**192); 35 | } 36 | 37 | // @view 38 | fn tokenURI_(token_id: felt252) -> Array { 39 | briq_protocol::utilities::token_uri::_getUrl( 40 | token_id, 41 | 'https://api.briq.construction', 42 | '/v1/uri/booklet/', 43 | 'starknet-mainnet/', 44 | '.json', 45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /src-old/box_nft/minting.cairo: -------------------------------------------------------------------------------- 1 | use traits::Into; 2 | 3 | use briq_protocol::utilities::authorization::Auth::_onlyAdmin; 4 | use briq_protocol::library_erc1155; 5 | 6 | use briq_protocol::utils; 7 | use briq_protocol::utils::GetCallerAddress; 8 | 9 | //@external 10 | fn mint_(owner: felt252, token_id: felt252, number: felt252) { 11 | _onlyAdmin(); 12 | 13 | library_erc1155::balance::Balance::_increaseBalance(owner, token_id, number); 14 | 15 | library_erc1155::transferability::Transferability::TransferSingle(GetCallerAddress(), 0, owner, token_id.into(), number.into()); 16 | 17 | // Make sure we have data for that token ID 18 | //let (_shape_data_start) = get_label_location(shape_data_start); 19 | //let (_shape_data_end) = get_label_location(shape_data_end); 20 | //assert_lt_felt(0, token_id); 21 | //assert_le_felt(token_id, _shape_data_end - _shape_data_start); 22 | assert(false, 'TODO'); 23 | 24 | return (); 25 | } 26 | -------------------------------------------------------------------------------- /src-old/box_nft/token_uri.cairo: -------------------------------------------------------------------------------- 1 | use array::ArrayTrait; 2 | 3 | #[derive(Drop, Copy, Serde)] 4 | struct BoxData { 5 | briq_1: felt252, // nb of briqs of material 0x1 6 | briq_3: felt252, // nb of briqs of material 0x3 7 | briq_4: felt252, // nb of briqs of material 0x4 8 | briq_5: felt252, // nb of briqs of material 0x5 9 | briq_6: felt252, // nb of briqs of material 0x6 10 | shape_class_hash: felt252, // Class hash of the matching shape contract 11 | } 12 | 13 | //@view 14 | fn get_box_data(token_id: felt252) -> BoxData { //(data: BoxData) { 15 | //let box: BoxData* = alloc(); 16 | //let (briq_data) = get_label_location(briq_data_start); 17 | //memcpy(box, briq_data + 5 * (token_id - 1), 5); 18 | //let (shape_data) = get_label_location(shape_data_start); 19 | //memcpy(box + 5, shape_data + (token_id - 1), 1); 20 | assert(false, 'TODO'); 21 | BoxData { briq_1: 0, briq_3: 0, briq_4: 0, briq_5: 0, briq_6: 0, shape_class_hash: 0 } 22 | } 23 | 24 | //@view 25 | fn get_box_nb(nb: felt252) -> felt252 { 26 | //let (_start) = get_label_location(shape_data_start); 27 | //let (_end) = get_label_location(shape_data_end); 28 | //let res = _end - _start; 29 | //return (res,); 30 | assert(false, 'TODO'); 31 | 0 32 | } 33 | 34 | // @view 35 | fn tokenURI_(token_id: felt252) -> Array { 36 | briq_protocol::utilities::token_uri::_getUrl( 37 | token_id, 38 | 'https://api.briq.construction', 39 | '/v1/uri/box/', 40 | 'starknet-mainnet/', 41 | '.json', 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /src-old/box_nft/unboxing.cairo: -------------------------------------------------------------------------------- 1 | use traits::Into; 2 | use traits::TryInto; 3 | use option::OptionTrait; 4 | 5 | use array::ArrayTrait; 6 | 7 | use starknet::contract_address; 8 | 9 | use briq_protocol::library_erc1155::transferability::Transferability; 10 | use briq_protocol::library_erc1155::balance::Balance; 11 | use briq_protocol::utils::GetCallerAddress; 12 | use briq_protocol::ecosystem::to_booklet::toBooklet; 13 | use briq_protocol::ecosystem::to_briq::toBriq; 14 | use briq_protocol::ecosystem::genesis_collection::GENESIS_COLLECTION; 15 | use briq_protocol::constants; 16 | 17 | use briq_protocol::box_nft::token_uri::BoxData; 18 | 19 | #[abi] 20 | trait IBookletContract { 21 | fn mint_(owner: felt252, token_id: felt252, shape_contract: felt252); 22 | } 23 | 24 | #[abi] 25 | trait IBriqContract { 26 | fn mintFT_(owner: felt252, material: felt252, qty: felt252); 27 | } 28 | 29 | // Unbox burns the box NFT, and mints briqs & attributes_registry corresponding to the token URI. 30 | //@external 31 | fn unbox_(owner: felt252, token_id: felt252) { 32 | Balance::_decreaseBalance(owner, token_id, 1); 33 | // At this point token_id cannot be 0 any more 34 | 35 | let caller = GetCallerAddress(); 36 | // Only the owner may unbox their box. 37 | assert(owner == caller, 'Not owner'); 38 | Transferability::TransferSingle(caller, owner, 0, token_id.into(), 1.into()); 39 | 40 | _unbox_mint(owner, token_id); 41 | } 42 | 43 | fn _unbox_mint(owner: felt252, token_id: felt252) { 44 | //let (_shape_data_start) = get_label_location(shape_data_start); 45 | //let shape_contract = [cast(_shape_data_start, felt252*) + token_id - 1]; 46 | assert(false, 'TODO'); 47 | let shape_contract = 0; 48 | IBookletContractDispatcher { contract_address: toBooklet::get().try_into().unwrap() } .mint_(owner, token_id * constants::c2_192 + GENESIS_COLLECTION, shape_contract); 49 | 50 | //let (_briq_data_start) = get_label_location(briq_data_start); 51 | //let (briq_addr) = _briq_address.read(); 52 | assert(false, 'TODO'); 53 | let briq_addr = toBriq::get(); 54 | _maybe_mint_briq(owner, briq_addr, ArrayTrait::::new(), token_id, 1, 0); 55 | //_maybe_mint_briq(owner, briq_addr, cast(_briq_data_start, felt252*), token_id, 3, 1); 56 | //_maybe_mint_briq(owner, briq_addr, cast(_briq_data_start, felt252*), token_id, 4, 2); 57 | //_maybe_mint_briq(owner, briq_addr, cast(_briq_data_start, felt252*), token_id, 5, 3); 58 | // TODO -> NFT briqs 59 | // let (amnt) = [cast(_briq_data_start, felt252*) + 1] 60 | // IBriqContract.mintFT_(briq_addr, owner, 0x1, amnt) 61 | 62 | return (); 63 | } 64 | 65 | // token_id acts as the global data offset, where offset is the inner-data offset. 66 | fn _maybe_mint_briq(owner: felt252, briq_addr: felt252, briq_data: Array, token_id: felt252, material: felt252, offset: felt252, 67 | ) { 68 | // sizeof 69 | let amnt = *briq_data[(token_id * 6 + offset).try_into().unwrap()]; 70 | if amnt != 0 { 71 | IBriqContractDispatcher { contract_address: briq_addr.try_into().unwrap() }.mintFT_(owner, material, amnt); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src-old/briq/minting.cairo: -------------------------------------------------------------------------------- 1 | use traits::Into; 2 | use traits::TryInto; 3 | use option::OptionTrait; 4 | use starknet::contract_address; 5 | 6 | use briq_protocol::ecosystem::to_box::toBox; 7 | use briq_protocol::library_erc1155::balance::Balance; 8 | use briq_protocol::library_erc1155::transferability::Transferability; 9 | use briq_protocol::briq::balance_enumerability::BalanceEnum::_setMaterialByOwner; 10 | use briq_protocol::utilities::authorization::Auth::_onlyAdminAnd; 11 | 12 | use briq_protocol::utils::GetCallerAddress; 13 | use briq_protocol::utils; 14 | 15 | 16 | fn _onlyAdminAndBoxContract() { 17 | _onlyAdminAnd(toBox::get().try_into().unwrap()); 18 | } 19 | 20 | #[contract] 21 | mod TotalSupply { 22 | struct Storage { 23 | // Delta with cairo 0 - this was "material -> supply", it is now "token ID -> supply" 24 | _total_supply: LegacyMap, 25 | } 26 | 27 | //#[view] 28 | fn totalSupplyOfMaterial(material: felt252) -> felt252 { //(supply: felt252) { 29 | _total_supply::read(material) 30 | } 31 | 32 | } 33 | 34 | //@external 35 | fn mintFT_(owner: felt252, material: felt252, qty: felt252) { 36 | _onlyAdminAndBoxContract(); 37 | 38 | assert(owner != 0, 'Invalid owner'); 39 | assert(material != 0, 'Invalid material'); 40 | assert(qty != 0, 'Invalid quantity'); 41 | 42 | // Update total supply. 43 | let res = TotalSupply::_total_supply::read(material); 44 | //with_attr error_message("Overflow in total supply") { 45 | assert(res < res + qty, 'Overflow total supply'); 46 | TotalSupply::_total_supply::write(material, res + qty); 47 | 48 | // Update balance 49 | let balance = Balance::_increaseBalance(owner, material, qty); 50 | 51 | // Update enumerability 52 | _setMaterialByOwner(owner, material, 0); 53 | 54 | Transferability::TransferSingle(GetCallerAddress(), 0, owner, material.into(), qty.into()); 55 | } 56 | -------------------------------------------------------------------------------- /src-old/briq/transferability.cairo: -------------------------------------------------------------------------------- 1 | //########### 2 | //########### 3 | //########### 4 | // # Authorization patterns 5 | use traits::Into; 6 | use traits::TryInto; 7 | use option::OptionTrait; 8 | use starknet::contract_address; 9 | 10 | use briq_protocol::utilities::authorization::Auth::_only; 11 | 12 | use briq_protocol::ecosystem::to_set::toSet; 13 | use briq_protocol::utils::GetCallerAddress; 14 | 15 | use briq_protocol::library_erc1155::transferability::Transferability; 16 | use briq_protocol::briq::balance_enumerability::BalanceEnum; 17 | 18 | fn _onlySetAnd(address: felt252) { 19 | if GetCallerAddress() == toSet::get() { 20 | return (); 21 | } 22 | _only(address.try_into().unwrap()); 23 | return (); 24 | } 25 | 26 | //########### 27 | //########### 28 | //########### 29 | // Admin functions 30 | 31 | //@external 32 | fn transferFT_(sender: felt252, recipient: felt252, material: felt252, qty: felt252) { 33 | _onlySetAnd(sender); 34 | 35 | Transferability::_transfer(sender, recipient, material, qty); 36 | 37 | // Needs to come after transfer for balances to be accurate. 38 | BalanceEnum::_maybeUnsetMaterialByOwner(sender, material); 39 | BalanceEnum::_setMaterialByOwner(recipient, material, 0); 40 | } 41 | -------------------------------------------------------------------------------- /src-old/briq_factory.cairo: -------------------------------------------------------------------------------- 1 | use starknet::info::get_block_timestamp; 2 | 3 | #[contract] 4 | mod BriqFactory { 5 | struct Storage { 6 | last_stored_t: u128, 7 | last_purchase_time: u64, 8 | } 9 | const decimals: u128 = 1000000000000000000; // 18 decimals 10 | const estimated_fair_price: u128 = 0;//100000000000000000; // 0.1 11 | const slope: u128 = 100000000000000; 12 | const minimum: u128 = 100000000000000; 13 | const decay_per_second: u128 = 10000000000; // some value 14 | 15 | #[external] 16 | fn initialise(t: u128) { 17 | last_stored_t::write(t); 18 | } 19 | 20 | #[view] 21 | fn get_current_t() -> u128 { 22 | let t = last_stored_t::read() 23 | let time_since_last_purchase = get_block_timestamp() - last_purchase_time::read(); 24 | let decay = time_since_last_purchase * decay_per_second; 25 | if decay > t { 26 | return 0; 27 | } 28 | t - decay 29 | } 30 | 31 | #[view] 32 | fn get_unit_price() -> u128 { 33 | return 1; 34 | } 35 | 36 | #[external] 37 | fn buy(amount: u128) { 38 | let t = get_current_t(); 39 | let price = integrate(t, amount); 40 | last_purchase_time::write(get_block_timestamp()); 41 | last_stored_t::write(t + amount); 42 | 43 | // ACTUAL BUYING HERE 44 | } 45 | 46 | fn get_integral(t: u128) -> u128 { 47 | if t < estimated_fair_price { 48 | return get_exp_integral(t); 49 | } 50 | get_lin_integral(t) 51 | } 52 | 53 | fn get_exp_integral(t: u128) -> u128 { 54 | 0//return (math.exp(x / self.estimated_fair_price - 1) * self.a * self.estimated_fair_price * self.estimated_fair_price + self.b * x); 55 | } 56 | 57 | fn get_lin_integral(t: u128) -> u128 { 58 | slope * t * t / 2 + minimum * t 59 | //return super().get_integral(x); 60 | } 61 | 62 | fn integrate(t: u128, amount: u128) -> u128 { 63 | get_integral(t + amount) - get_integral(t) 64 | //if t + amount <= estimated_fair_price || t > self.estimated_fair_price { 65 | // get_integral(t + amount) - get_integral(t) 66 | //} else { 67 | // return self.get_exp_integral(self.estimated_fair_price) - self.get_exp_integral(t) + self.get_lin_integral( 68 | // t + amount) - self.get_lin_integral(self.estimated_fair_price) 69 | //} 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src-old/constants.cairo: -------------------------------------------------------------------------------- 1 | const c2_192: felt252 = 0x1000000000000000000000000000000000000000000000000; 2 | const c2_64_mask: felt252 = 0xffffffffffffffff; -------------------------------------------------------------------------------- /src-old/ecosystem.cairo: -------------------------------------------------------------------------------- 1 | mod genesis_collection; 2 | mod to_booklet; 3 | mod to_box; 4 | mod to_briq; 5 | mod to_set; 6 | mod to_attributes_registry; -------------------------------------------------------------------------------- /src-old/ecosystem/genesis_collection.cairo: -------------------------------------------------------------------------------- 1 | const GENESIS_COLLECTION: felt252 = 0x1; 2 | const DUCKS_COLLECTION: felt252 = 0x3; 3 | const ZENDUCKS_COLLECTION: felt252 = 0x4; -------------------------------------------------------------------------------- /src-old/ecosystem/to_attributes_registry.cairo: -------------------------------------------------------------------------------- 1 | #[contract] 2 | mod toAttributesRegistry { 3 | use briq_protocol::utilities::authorization::Auth::_onlyAdmin; 4 | use briq_protocol::utils::TempContractAddress; 5 | use briq_protocol::utils::GetCallerAddress; 6 | 7 | struct Storage { 8 | ar_address: TempContractAddress, 9 | } 10 | 11 | fn get() -> TempContractAddress { 12 | return ar_address::read(); 13 | } 14 | 15 | fn set(addr: TempContractAddress) { 16 | _onlyAdmin(); 17 | ar_address::write(addr) 18 | } 19 | 20 | fn _onlyAttributesRegistry() { 21 | //with_attr error_message("Only the attributes registry may call this function.") { 22 | assert(GetCallerAddress() == ar_address::read(), 'Unauthorized'); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src-old/ecosystem/to_booklet.cairo: -------------------------------------------------------------------------------- 1 | #[contract] 2 | mod toBooklet { 3 | use briq_protocol::utilities::authorization::Auth::_onlyAdmin; 4 | use briq_protocol::utils::TempContractAddress; 5 | 6 | struct Storage { 7 | booklet_address: TempContractAddress, 8 | } 9 | 10 | fn get() -> TempContractAddress { 11 | return booklet_address::read(); 12 | } 13 | 14 | fn set(addr: TempContractAddress) { 15 | _onlyAdmin(); 16 | booklet_address::write(addr) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src-old/ecosystem/to_box.cairo: -------------------------------------------------------------------------------- 1 | #[contract] 2 | mod toBox { 3 | use briq_protocol::utilities::authorization::Auth::_onlyAdmin; 4 | use briq_protocol::utils::TempContractAddress; 5 | 6 | struct Storage { 7 | box_address: TempContractAddress, 8 | } 9 | 10 | fn get() -> TempContractAddress { 11 | return box_address::read(); 12 | } 13 | 14 | fn set(addr: TempContractAddress) { 15 | _onlyAdmin(); 16 | box_address::write(addr) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src-old/ecosystem/to_briq.cairo: -------------------------------------------------------------------------------- 1 | #[contract] 2 | mod toBriq { 3 | use briq_protocol::utilities::authorization::Auth::_onlyAdmin; 4 | use briq_protocol::utils::TempContractAddress; 5 | 6 | struct Storage { 7 | briq_address: TempContractAddress, 8 | } 9 | 10 | fn get() -> TempContractAddress { 11 | return briq_address::read(); 12 | } 13 | 14 | fn set(addr: TempContractAddress) { 15 | _onlyAdmin(); 16 | briq_address::write(addr) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src-old/ecosystem/to_set.cairo: -------------------------------------------------------------------------------- 1 | #[contract] 2 | mod toSet { 3 | use briq_protocol::utilities::authorization::Auth::_onlyAdmin; 4 | use briq_protocol::utils::TempContractAddress; 5 | 6 | struct Storage { 7 | set_address: TempContractAddress, 8 | } 9 | 10 | fn get() -> TempContractAddress { 11 | return set_address::read(); 12 | } 13 | 14 | fn set(addr: TempContractAddress) { 15 | _onlyAdmin(); 16 | set_address::write(addr) 17 | } 18 | } 19 | 20 | //@storage_var 21 | //func _set_address() -> (address: felt) { 22 | //} 23 | 24 | //@view 25 | //func getSetAddress_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( 26 | // address: felt 27 | //) { 28 | // let (value) = _set_address.read(); 29 | // return (value,); 30 | //} 31 | // 32 | //@external 33 | //func setSetAddress_{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 34 | // address: felt 35 | //) { 36 | // _onlyAdmin(); 37 | // _set_address.write(address); 38 | // return (); 39 | //} 40 | -------------------------------------------------------------------------------- /src-old/lib.cairo: -------------------------------------------------------------------------------- 1 | //mod attributes_registry; Disabled until different bases bug is fixed 2 | mod booklet_nft; 3 | mod box_nft; 4 | mod briq; 5 | mod constants; 6 | mod ecosystem; 7 | mod library_erc721; 8 | mod library_erc1155; 9 | mod set_nft; 10 | mod shape; 11 | mod temp; 12 | mod types; 13 | mod utilities; 14 | mod utils; 15 | mod briq_factory; 16 | -------------------------------------------------------------------------------- /src-old/library_erc1155.cairo: -------------------------------------------------------------------------------- 1 | mod balance; 2 | mod approvals; 3 | mod transferability; -------------------------------------------------------------------------------- /src-old/library_erc1155/approvals.cairo: -------------------------------------------------------------------------------- 1 | 2 | #[contract] 3 | mod Approvals { 4 | use briq_protocol::utils::TempContractAddress; 5 | use briq_protocol::utils::GetCallerAddress; 6 | 7 | use briq_protocol::utils; 8 | 9 | #[event] 10 | fn ApprovalForAll(_owner: TempContractAddress, _operator: TempContractAddress, _approved: bool) { 11 | } 12 | 13 | // # approved_address is 'operator' in the spec, but I find that name rather unclear. 14 | struct Storage { 15 | _approval_all: LegacyMap<(TempContractAddress, TempContractAddress), bool>, 16 | } 17 | 18 | #[external] 19 | fn setApprovalForAll_(approved_address: TempContractAddress, is_approved: bool) { 20 | let caller = GetCallerAddress(); 21 | _setExplicitApprovalForAll( 22 | caller, approved_address, is_approved 23 | ); 24 | return (); 25 | } 26 | 27 | fn _setExplicitApprovalForAll(on_behalf_of: TempContractAddress, approved_address: TempContractAddress, is_approved: bool) { 28 | // Neither of these can be 0. 29 | //with_attr error_message("ERC721: either the caller or operator is the zero address") { 30 | //assert(on_behalf_of != 0, 'Bad caller'); 31 | //assert(approved_address != 0, 'Bad operator'); 32 | 33 | // Cannot approve yourself. 34 | //with_attr error_message("ERC721: approve to caller") { 35 | assert(on_behalf_of != approved_address, 'Cannot approve yourself'); 36 | 37 | // Make sure `is_approved` is a boolean (0 or 1) 38 | //with_attr error_message("ERC721: approved is not a Cairo boolean") { 39 | //assert(is_approved * (1 - is_approved) == 0, 'non-bool approved'); 40 | 41 | _approval_all::write((on_behalf_of, approved_address), is_approved); 42 | ApprovalForAll(on_behalf_of, approved_address, is_approved); 43 | return (); 44 | } 45 | 46 | #[view] 47 | fn isApprovedForAll_(on_behalf_of: TempContractAddress, address: TempContractAddress) -> bool { //(is_approved: felt252) { 48 | return _approval_all::read((on_behalf_of, address)); 49 | } 50 | 51 | // ## Auth 52 | 53 | fn _onlyApprovedAll(on_behalf_of: TempContractAddress) { 54 | let caller = GetCallerAddress(); 55 | // You can always approve on behalf of yourself. 56 | if (caller == on_behalf_of) { 57 | return (); 58 | } 59 | assert(isApprovedForAll_(on_behalf_of, caller), 'Not approved'); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src-old/library_erc1155/balance.cairo: -------------------------------------------------------------------------------- 1 | 2 | #[contract] 3 | mod Balance { 4 | use gas::withdraw_gas_all; 5 | use gas::get_builtin_costs; 6 | 7 | use traits::Copy; 8 | use traits::Into; 9 | use traits::TryInto; 10 | use option::OptionTrait; 11 | 12 | use array::SpanTrait; 13 | use array::ArrayTrait; 14 | use array::ArrayTCloneImpl; 15 | use clone::Clone; 16 | 17 | use briq_protocol::utils::TempContractAddress; 18 | use briq_protocol::utils::GetCallerAddress; 19 | 20 | use briq_protocol::utils; 21 | use briq_protocol::utils::check_gas; 22 | 23 | struct Storage { 24 | _balance: LegacyMap<(TempContractAddress, felt252), felt252>, 25 | } 26 | 27 | fn _increaseBalance(owner: TempContractAddress, token_id: felt252, number: felt252) { 28 | let balance = _balance::read((owner, token_id)); 29 | assert(balance < balance + number, 'Mint would overflow balance'); 30 | _balance::write((owner, token_id), balance + number); 31 | return (); 32 | } 33 | 34 | fn _decreaseBalance( 35 | owner: TempContractAddress, token_id: felt252, number: felt252, 36 | ) { 37 | let balance = _balance::read((owner, token_id)); 38 | assert(balance - number < balance, 'Insufficient balance'); 39 | _balance::write((owner, token_id), balance - number); 40 | return (); 41 | } 42 | 43 | // @view 44 | fn balanceOf_( 45 | owner: TempContractAddress, token_id: felt252 46 | ) -> felt252 { 47 | return _balance::read((owner, token_id)); 48 | } 49 | 50 | // @view 51 | //fn balanceOfBatch_( 52 | // mut owners: Array, mut token_ids: Array 53 | //) -> Array { 54 | // assert(owners.len() == token_ids.len(), 'Bad input'); 55 | // let mut balances = ArrayTrait::::new(); 56 | // loop { 57 | // if owners.len() == 0 { 58 | // break 0; 59 | // } 60 | // let balance = _balance::read((*owners.at(0), *token_ids.at(0))); 61 | // balances.append(balance); 62 | // owners.pop_front(); 63 | // token_ids.pop_front(); 64 | // }; 65 | // return balances; 66 | //} 67 | 68 | fn balanceOfBatch_( 69 | mut owners: Array, mut token_ids: Array 70 | ) -> Array { 71 | assert(owners.len() == token_ids.len(), 'Bad input'); 72 | return _balanceOfBatch_(owners, token_ids, ArrayTrait::::new()); 73 | } 74 | 75 | fn _balanceOfBatch_( 76 | mut owners: Array, mut token_ids: Array, mut balances: Array 77 | ) -> Array { 78 | check_gas(); 79 | if owners.len() == 0 { 80 | return balances; 81 | } 82 | let balance = _balance::read((*owners.at(0), *token_ids.at(0))); 83 | balances.append(balance); 84 | owners.pop_front(); 85 | token_ids.pop_front(); 86 | return _balanceOfBatch_(owners, token_ids, balances); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src-old/library_erc1155/transferability.cairo: -------------------------------------------------------------------------------- 1 | #[contract] 2 | mod Transferability { 3 | use traits::Into; 4 | 5 | use briq_protocol::utils::TempContractAddress; 6 | use briq_protocol::utils::GetCallerAddress; 7 | 8 | use briq_protocol::library_erc1155::balance::Balance; 9 | use briq_protocol::library_erc1155::approvals::Approvals; 10 | 11 | #[event] 12 | fn TransferSingle(_operator: TempContractAddress, _from: TempContractAddress, _to: TempContractAddress, _id: u256, _value: u256) { 13 | } 14 | 15 | #[event] 16 | fn TransferBatch( 17 | _operator: TempContractAddress, 18 | _from: TempContractAddress, 19 | _to: TempContractAddress, 20 | _ids: Array, 21 | _values: Array, 22 | ) { 23 | } 24 | 25 | fn _transfer( 26 | sender: TempContractAddress, recipient: TempContractAddress, token_id: felt252, value: felt252 27 | ) { 28 | assert(sender != 0, 'Bad input'); 29 | assert(recipient != 0, 'Bad input'); 30 | assert(sender - recipient != 0, 'Bad input'); 31 | assert(token_id != 0, 'Bad input'); 32 | assert(value != 0, 'Bad input'); 33 | 34 | // TODO: implement detailled approval? 35 | // Reset approval (0 cost if was 0 before on starknet I believe) 36 | // let (caller) = get_caller_address() 37 | // let (approved_value) = ERC1155_approvals.getApproved_(sender, token_id, caller) 38 | // ERC1155_approvals.approve_nocheck_(0, token_id) 39 | 40 | Balance::_decreaseBalance(sender, token_id, value); 41 | Balance::_increaseBalance(recipient, token_id, value); 42 | 43 | TransferSingle(GetCallerAddress(), sender, recipient, token_id.into(), value.into()); 44 | 45 | return (); 46 | } 47 | 48 | fn _transfer_burnable( 49 | sender: TempContractAddress, recipient: TempContractAddress, token_id: felt252, value: felt252 50 | ) { 51 | assert(sender != 0, 'Bad input'); 52 | assert(token_id != 0, 'Bad input'); 53 | assert(value != 0, 'Bad input'); 54 | 55 | // TODO: implement detailled approval? 56 | // Reset approval (0 cost if was 0 before on starknet I believe) 57 | // let (caller) = get_caller_address() 58 | // let (approved_value) = ERC1155_approvals.getApproved_(sender, token_id, caller) 59 | // ERC1155_approvals.approve_nocheck_(0, token_id) 60 | 61 | Balance::_decreaseBalance(sender, token_id, value); 62 | Balance::_increaseBalance(recipient, token_id, value); 63 | 64 | TransferSingle(GetCallerAddress(), sender, recipient, token_id.into(), value.into()); 65 | 66 | return (); 67 | } 68 | 69 | // @external 70 | fn safeTransferFrom_( 71 | sender: TempContractAddress, recipient: TempContractAddress, token_id: felt252, value: felt252, data: Array 72 | ) { 73 | // TODO -> support detailed approvals. 74 | Approvals::_onlyApprovedAll(sender); 75 | 76 | _transfer(sender, recipient, token_id, value); 77 | 78 | // TODO: called the receiver fntion. I'm not entirely sure how to handle accounts yet... 79 | 80 | return (); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src-old/library_erc721.cairo: -------------------------------------------------------------------------------- 1 | mod approvals; 2 | mod balance; 3 | mod transferability; -------------------------------------------------------------------------------- /src-old/library_erc721/balance.cairo: -------------------------------------------------------------------------------- 1 | //########### 2 | //########### 3 | //########### 4 | // Storage variables. 5 | 6 | #[contract] 7 | mod Balance { 8 | use briq_protocol::utils::TempContractAddress; 9 | use briq_protocol::utils; 10 | 11 | struct Storage { 12 | _balance: LegacyMap, 13 | _owner: LegacyMap, 14 | } 15 | 16 | // @view 17 | fn ownerOf_(token_id: felt252) -> TempContractAddress { 18 | // OZ ∆: don't fail on res == 0 19 | _owner::read(token_id) 20 | } 21 | 22 | // @view 23 | fn balanceOf_(owner: TempContractAddress) -> felt252 { 24 | // OZ ∆: No 0 check, I don't see the point. 25 | _balance::read(owner) 26 | } 27 | 28 | fn _increaseBalance(owner: TempContractAddress) { 29 | let balance = _balance::read(owner); 30 | assert(balance < balance + 1, 'Mint would overflow balance'); 31 | _balance::write(owner, balance + 1); 32 | return (); 33 | } 34 | 35 | fn _decreaseBalance( 36 | owner: TempContractAddress 37 | ) { 38 | let balance = _balance::read(owner); 39 | assert(balance - 1 < balance, 'Insufficient balance'); 40 | _balance::write(owner, balance - 1); 41 | return (); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src-old/library_erc721/transferability.cairo: -------------------------------------------------------------------------------- 1 | #[contract] 2 | mod Transferability { 3 | use briq_protocol::library_erc721::approvals::Approvals; 4 | use briq_protocol::library_erc721::balance::Balance; 5 | use briq_protocol::utils; 6 | 7 | use traits::Into; 8 | 9 | #[event] 10 | fn Transfer(from_: felt252, to_: felt252, token_id_: u256) {} 11 | 12 | fn _transfer(sender: felt252, recipient: felt252, token_id: felt252) { 13 | assert(recipient != 0, 'Bad input'); 14 | assert(sender - recipient != 0, 'Bad input'); 15 | assert(token_id != 0, 'Bad input'); 16 | 17 | // Reset approval (0 cost if was 0 before on starknet I believe) 18 | Approvals::approve_nocheck_(0, token_id); 19 | 20 | let curr_owner = Balance::_owner::read(token_id); 21 | assert(sender == curr_owner, 'Not owner'); 22 | Balance::_owner::write(token_id, recipient); 23 | 24 | Balance::_decreaseBalance(sender); 25 | Balance::_increaseBalance(recipient); 26 | 27 | Transfer(sender, recipient, token_id.into()); 28 | } 29 | 30 | // @external 31 | fn transferFrom_(sender: felt252, recipient: felt252, token_id: felt252) { 32 | Approvals::_onlyApproved(sender, token_id); 33 | _transfer(sender, recipient, token_id); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src-old/set_nft/token_uri.cairo: -------------------------------------------------------------------------------- 1 | // @view 2 | fn tokenURI_(token_id: felt252) -> Array { 3 | briq_protocol::utilities::token_uri::_getUrl( 4 | token_id, 5 | 'https://api.briq.construction', 6 | '/v1/uri/set/', 7 | 'starknet-mainnet/', 8 | '.json', 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /src-old/shape.cairo: -------------------------------------------------------------------------------- 1 | mod attribute; 2 | mod shape_store; 3 | -------------------------------------------------------------------------------- /src-old/temp.cairo: -------------------------------------------------------------------------------- 1 | mod Proxy; -------------------------------------------------------------------------------- /src-old/temp/Proxy.cairo: -------------------------------------------------------------------------------- 1 | mod Proxy { 2 | fn assert_only_admin() { 3 | assert(false, 'Not authorized'); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src-old/types.cairo: -------------------------------------------------------------------------------- 1 | #[derive(Copy, Drop, Serde)] 2 | struct FTSpec { 3 | token_id: felt252, 4 | qty: felt252, 5 | } 6 | 7 | 8 | #[derive(Copy, Drop, Serde)] 9 | struct ShapeItem { 10 | // Material is 64 bit so this is COLOR as short string shifted 136 bits left, and material. 11 | // The 128th bit indicates 'This is an NFT', at which point you need to refer to the list of NFTs. 12 | // (I'm shifting colors by 7 more bits so that the corresponding hex is easily readable and I don't need more). 13 | color_nft_material: felt252, 14 | // Stored as reversed two's completement, shifted by 64 bits. 15 | // (reversed az in -> the presence of the 64th bit indicates positive number) 16 | // (This is done so that sorting works) 17 | x_y_z: felt252, 18 | } 19 | -------------------------------------------------------------------------------- /src-old/utilities.cairo: -------------------------------------------------------------------------------- 1 | mod authorization; 2 | mod token_uri; 3 | mod IERC165; 4 | -------------------------------------------------------------------------------- /src-old/utilities/IERC165.cairo: -------------------------------------------------------------------------------- 1 | const IERC165_ID: felt252 = 0x01ffc9a7; 2 | 3 | const IERC721_ID: felt252 = 0x80ac58cd; 4 | const IERC721_METADATA_ID: felt252 = 0x5b5e139f; 5 | 6 | const IERC1155_ID: felt252 = 0xd9b67a26; 7 | const IERC1155_METADATA_ID: felt252 = 0x0e89341c; 8 | -------------------------------------------------------------------------------- /src-old/utilities/authorization.cairo: -------------------------------------------------------------------------------- 1 | mod Auth { 2 | 3 | use starknet::get_caller_address; 4 | use starknet::ContractAddress; 5 | use starknet::ContractAddressIntoFelt252; 6 | use traits::Into; 7 | 8 | use briq_protocol::temp::Proxy::Proxy; 9 | 10 | #[view] 11 | fn _only(address: ContractAddress) { 12 | if get_caller_address() != address { 13 | assert(false, 'Not authorized'); 14 | } 15 | } 16 | 17 | #[view] 18 | fn _onlyAdmin() { 19 | let caller: felt252 = get_caller_address().into(); 20 | // TODO: use match when that supports something other than 0 21 | if caller == 0x03eF5b02BCc5D30f3f0D35d55F365E6388fE9501eca216Cb1596940bf41083E2 { 22 | return (); 23 | } 24 | if caller == 0x059dF66aF2E0E350842b11EA6B5a903b94640C4ff0418b04CceDcC320F531A08 { 25 | return (); 26 | } 27 | Proxy::assert_only_admin(); 28 | } 29 | 30 | #[view] 31 | fn _onlyAdminAnd(address: ContractAddress) { 32 | if get_caller_address() == address { 33 | return (); 34 | } 35 | _onlyAdmin(); 36 | } 37 | } 38 | 39 | //%lang starknet 40 | 41 | //from starkware.cairo.common.cairo_builtins import HashBuiltin 42 | //from starkware.starknet.common.syscalls import get_caller_address 43 | //from contracts.vendor.openzeppelin.upgrades.library import Proxy 44 | 45 | //################### 46 | //################### 47 | //################### 48 | // Authorization patterns 49 | 50 | //func _only{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(address: felt) { 51 | // let (caller) = get_caller_address(); 52 | // if ((caller - address) == 0) { 53 | // return (); 54 | // } 55 | // // Failure 56 | // with_attr error_message("You are not authorized to call this function") { 57 | // assert 0 = 1; 58 | // } 59 | // return (); 60 | //} 61 | // 62 | //func _onlyAdmin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() { 63 | // let (caller) = get_caller_address(); 64 | // // Hardcoded briq team addresses. 65 | // if ((caller - 0x03eF5b02BCc5D30f3f0D35d55F365E6388fE9501eca216Cb1596940bf41083E2) * (caller - 0x059dF66aF2E0E350842b11EA6B5a903b94640C4ff0418b04CceDcC320F531A08) == 0) { 66 | // return (); 67 | // } 68 | // // Fallback to the proxy admin. 69 | // Proxy.assert_only_admin(); 70 | // return (); 71 | //} 72 | // 73 | //func _onlyAdminAnd{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(address: felt) { 74 | // let (caller) = get_caller_address(); 75 | // if ((caller - address) == 0) { 76 | // return (); 77 | // } 78 | // _onlyAdmin(); 79 | // return (); 80 | //} 81 | -------------------------------------------------------------------------------- /src-old/utilities/token_uri.cairo: -------------------------------------------------------------------------------- 1 | 2 | use array::ArrayTrait; 3 | use array::SpanTrait; 4 | 5 | use briq_protocol::utils::check_gas; 6 | 7 | use core::integer::u128_safe_divmod; 8 | use core::integer::u128_as_non_zero; 9 | use core::integer::u256_from_felt252; 10 | use core::integer::u256_as_non_zero; 11 | use core::integer::u256_safe_divmod; 12 | 13 | use traits::Into; 14 | use traits::TryInto; 15 | use option::OptionTrait; 16 | 17 | use briq_protocol::utils; 18 | 19 | #[event] 20 | fn URI(_value: Array, _id: u256) {} 21 | 22 | fn _getUrl( 23 | token_id: felt252, 24 | uri_p1: felt252, 25 | uri_p2: felt252, 26 | uri_p3: felt252, 27 | uri_p4: felt252, 28 | ) -> Array { // -> uri 29 | //let (data_address) = get_label_location(label); 30 | //let (local uri_out: felt252*) = alloc(); 31 | let mut uri_out = ArrayTrait::::new(); 32 | uri_out.append(uri_p1); 33 | uri_out.append(uri_p2); 34 | // TODO: change on a per-network basis? 35 | uri_out.append(uri_p3); 36 | 37 | let tok_as_ascii = felt_to_ascii_array(token_id); 38 | uri_out = insert_reverse(uri_out, tok_as_ascii.span()); 39 | 40 | uri_out.append(uri_p4); 41 | 42 | return uri_out; 43 | } 44 | 45 | fn insert_reverse(mut out: Array, mut data: Span) -> Array { 46 | check_gas(); 47 | if data.len() == 0 { 48 | return out; 49 | } 50 | let nb = *data.pop_back().unwrap(); 51 | out.append(nb.try_into().unwrap()); 52 | return insert_reverse(out, data); 53 | } 54 | 55 | fn felt_to_ascii_array(i: felt252) -> Array { 56 | let tok_u256 = u256_from_felt252(i); 57 | let out = ArrayTrait::::new(); 58 | _felt_to_ascii_array(out, tok_u256) 59 | } 60 | 61 | fn _felt_to_ascii_array(mut out: Array, i: u256) -> Array { 62 | check_gas(); 63 | let (q, r) = get_letter(i); 64 | out.append(r); 65 | if q == 0.into() { 66 | return out; 67 | } 68 | return _felt_to_ascii_array(out, q); 69 | } 70 | 71 | fn get_letter( 72 | i: u256, 73 | ) -> (u256, u256) { 74 | let (q, r) = u256_safe_divmod(i, u256_as_non_zero(10.into())); 75 | return (q, r + '0'.into()); 76 | } 77 | 78 | fn get_letter_hex( 79 | i: u256, 80 | ) -> (u256, u256) { 81 | let (q, r) = u256_safe_divmod(i, u256_as_non_zero(16.into())); 82 | if r < 10.into() { 83 | return (q, r + '0'.into()); 84 | } 85 | return (q, r + 'a'.into() - 10.into()); 86 | } 87 | -------------------------------------------------------------------------------- /src/briq_factory/constants.cairo: -------------------------------------------------------------------------------- 1 | // 10**18 2 | fn DECIMALS() -> felt252 { 3 | 1000000000000000000 4 | } 5 | 6 | // Arbitrary inflection point below which to use the lower_* curve 7 | fn INFLECTION_POINT() -> felt252 { 8 | 400000 * DECIMALS() 9 | } 10 | // Slope: Buying 100 000 briqs increases price per briq by 0.00001 11 | fn SLOPE() -> felt252 { 12 | 100000000 // 10**8 13 | } 14 | 15 | // Computed to hit the inflection point at 0.00003 per briq 16 | fn RAW_FLOOR() -> felt252 { 17 | -1 * 10000000000000 // - 10**13 18 | } 19 | 20 | // Actual floor price of the briqs = 0.0001 21 | fn LOWER_FLOOR() -> felt252 { 22 | 10000000000000 // 10**13 23 | } 24 | 25 | // Computed to hit the inflection point at 0.00003 per briq 26 | fn LOWER_SLOPE() -> felt252 { 27 | consteval_int!(5 * 10000000) // 5 * 10**7 28 | } 29 | 30 | // decay: for each second, reduce the price by so many wei. Computed to hit 200K per year. 31 | fn DECAY_PER_SECOND() -> felt252 { 32 | 6337791082068820 33 | } 34 | 35 | fn SURGE_SLOPE() -> felt252 { 36 | 100000000 // 10**8 37 | } 38 | 39 | fn MINIMAL_SURGE() -> felt252 { 40 | 250000 * DECIMALS() 41 | } 42 | 43 | fn SURGE_DECAY_PER_SECOND() -> felt252 { 44 | 4134 * 100000000000000 // 4134 * 10**14 : Decays over a week 45 | } 46 | 47 | fn MIN_PURCHASE() -> felt252 { 48 | 9 49 | } 50 | 51 | fn BRIQ_MATERIAL() -> felt252 { 52 | 1 53 | } 54 | -------------------------------------------------------------------------------- /src/cumulative_balance.cairo: -------------------------------------------------------------------------------- 1 | use starknet::ContractAddress; 2 | use traits::TryInto; 3 | use option::OptionTrait; 4 | 5 | fn CUM_BALANCE_TOKEN() -> ContractAddress { 6 | 'cum_balance'.try_into().unwrap() 7 | } 8 | 9 | fn CB_BRIQ() -> felt252 { 10 | 'briq' 11 | } 12 | 13 | fn CB_ATTRIBUTES() -> felt252 { 14 | 'attributes' 15 | } 16 | 17 | fn CB_TOTAL_SUPPLY_1155() -> felt252 { 18 | 'supply_1155' 19 | } 20 | -------------------------------------------------------------------------------- /src/erc/erc1155/interface.cairo: -------------------------------------------------------------------------------- 1 | 2 | #[starknet::interface] 3 | trait IERC1155Metadata { 4 | fn name(self: @TState) -> felt252; 5 | fn symbol(self: @TState) -> felt252; 6 | fn uri(self: @TState, token_id: u256) -> Array; // TODO: update this to something else? 7 | } 8 | -------------------------------------------------------------------------------- /src/erc/erc1155/internal_trait.cairo: -------------------------------------------------------------------------------- 1 | use starknet::ContractAddress; 2 | 3 | trait InternalTrait1155 { 4 | fn _is_approved_for_all_or_owner( 5 | self: @ContractState, from: ContractAddress, caller: ContractAddress 6 | ) -> bool; 7 | fn _set_approval_for_all( 8 | ref self: ContractState, 9 | owner: ContractAddress, 10 | operator: ContractAddress, 11 | approved: bool 12 | ); 13 | fn _safe_transfer_from( 14 | ref self: ContractState, 15 | from: ContractAddress, 16 | to: ContractAddress, 17 | id: u256, 18 | amount: u256, 19 | data: Array 20 | ); 21 | fn _safe_batch_transfer_from( 22 | ref self: ContractState, 23 | from: ContractAddress, 24 | to: ContractAddress, 25 | ids: Array, 26 | amounts: Array, 27 | data: Array 28 | ); 29 | fn _mint(ref self: ContractState, to: ContractAddress, id: u256, amount: u256); 30 | fn _burn(ref self: ContractState, id: u256, amount: u256); 31 | fn _safe_mint( 32 | ref self: ContractState, 33 | to: ContractAddress, 34 | id: u256, 35 | amount: u256, 36 | data: Span 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /src/erc/erc1155/models.cairo: -------------------------------------------------------------------------------- 1 | use presets::erc1155::erc1155::models::{ERC1155OperatorApproval, erc_1155_operator_approval}; 2 | 3 | use starknet::ContractAddress; 4 | 5 | #[derive(Model, Copy, Drop, Serde)] 6 | struct ERC1155Balance { 7 | #[key] 8 | token: ContractAddress, 9 | #[key] 10 | account: ContractAddress, 11 | #[key] 12 | id: felt252, 13 | amount: u128 14 | } 15 | 16 | use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait}; 17 | 18 | fn increase_balance( 19 | world: IWorldDispatcher, 20 | token: ContractAddress, 21 | account: ContractAddress, 22 | id: felt252, 23 | amount: u128) 24 | { 25 | let balance = get!(world, (token, account, id), ERC1155Balance); 26 | set!(world, ERC1155Balance { token, account, id, amount: balance.amount + amount }); 27 | } 28 | fn decrease_balance( 29 | world: IWorldDispatcher, 30 | token: ContractAddress, 31 | account: ContractAddress, 32 | id: felt252, 33 | amount: u128) 34 | { 35 | let balance = get!(world, (token, account, id), ERC1155Balance); 36 | set!(world, ERC1155Balance { token, account, id, amount: balance.amount - amount }); 37 | } 38 | -------------------------------------------------------------------------------- /src/erc/erc721/interface.cairo: -------------------------------------------------------------------------------- 1 | 2 | 3 | #[starknet::interface] 4 | trait IERC721Metadata { 5 | fn name(self: @TState) -> felt252; 6 | fn symbol(self: @TState) -> felt252; 7 | fn token_uri(self: @TState, token_id: u256) -> Array; 8 | } 9 | 10 | #[starknet::interface] 11 | trait IERC721MetadataCamelOnly { 12 | fn tokenURI(self: @TState, tokenId: u256) -> Array; 13 | } 14 | -------------------------------------------------------------------------------- /src/erc/erc721/internal_trait.cairo: -------------------------------------------------------------------------------- 1 | use starknet::ContractAddress; 2 | 3 | trait InternalTrait721 { 4 | fn _owner_of(self: @ContractState, token_id: u256) -> ContractAddress; 5 | fn _exists(self: @ContractState, token_id: u256) -> bool; 6 | fn _is_approved_or_owner( 7 | self: @ContractState, spender: ContractAddress, token_id: u256 8 | ) -> bool; 9 | fn _approve(ref self: ContractState, to: ContractAddress, token_id: u256); 10 | fn _set_approval_for_all( 11 | ref self: ContractState, 12 | owner: ContractAddress, 13 | operator: ContractAddress, 14 | approved: bool 15 | ); 16 | 17 | fn _transfer( 18 | ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 19 | ); 20 | 21 | fn _safe_transfer( 22 | ref self: ContractState, 23 | from: ContractAddress, 24 | to: ContractAddress, 25 | token_id: u256, 26 | data: Span 27 | ); 28 | 29 | fn _mint(ref self: ContractState, to: ContractAddress, token_id: u256); 30 | fn _burn(ref self: ContractState, token_id: u256); 31 | fn _safe_mint( 32 | ref self: ContractState, to: ContractAddress, token_id: u256, data: Span 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/erc/erc721/models.cairo: -------------------------------------------------------------------------------- 1 | use starknet::ContractAddress; 2 | 3 | use presets::erc721::models::{ERC721OperatorApproval, erc_721_operator_approval}; 4 | 5 | #[derive(Model, Copy, Drop, Serde)] 6 | struct ERC721Owner { 7 | #[key] 8 | token: ContractAddress, 9 | #[key] 10 | token_id: felt252, 11 | address: ContractAddress 12 | } 13 | 14 | #[derive(Model, Copy, Drop, Serde)] 15 | struct ERC721Balance { 16 | #[key] 17 | token: ContractAddress, 18 | #[key] 19 | account: ContractAddress, 20 | amount: u128, 21 | } 22 | 23 | #[derive(Model, Copy, Drop, Serde)] 24 | struct ERC721TokenApproval { 25 | #[key] 26 | token: ContractAddress, 27 | #[key] 28 | token_id: felt252, 29 | address: ContractAddress, 30 | } 31 | 32 | 33 | use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait}; 34 | 35 | fn increase_balance( 36 | world: IWorldDispatcher, 37 | token: ContractAddress, 38 | account: ContractAddress, 39 | amount: u128) 40 | { 41 | let balance = get!(world, (token, account), ERC721Balance); 42 | set!(world, ERC721Balance { token, account, amount: balance.amount + amount }); 43 | } 44 | fn decrease_balance( 45 | world: IWorldDispatcher, 46 | token: ContractAddress, 47 | account: ContractAddress, 48 | amount: u128) 49 | { 50 | let balance = get!(world, (token, account), ERC721Balance); 51 | set!(world, ERC721Balance { token, account, amount: balance.amount - amount }); 52 | } 53 | -------------------------------------------------------------------------------- /src/erc/mint_burn.cairo: -------------------------------------------------------------------------------- 1 | use starknet::ContractAddress; 2 | 3 | #[starknet::interface] 4 | trait MintBurn { 5 | fn mint(ref self: TState, owner: ContractAddress, token_id: felt252, amount: u128); 6 | fn burn(ref self: TState, owner: ContractAddress, token_id: felt252, amount: u128); 7 | } 8 | -------------------------------------------------------------------------------- /src/felt_math.cairo: -------------------------------------------------------------------------------- 1 | use traits::{Into, TryInto}; 2 | use option::OptionTrait; 3 | 4 | impl FeltBitAnd of BitAnd { 5 | fn bitand(lhs: felt252, rhs: felt252) -> felt252 { 6 | (Into::::into(lhs) & rhs.into()).try_into().unwrap() 7 | } 8 | } 9 | 10 | fn felt252_le(lhs: felt252, rhs: felt252) -> bool { 11 | Into::::into(lhs) <= rhs.into() 12 | } 13 | 14 | fn felt252_lt(lhs: felt252, rhs: felt252) -> bool { 15 | Into::::into(lhs) < rhs.into() 16 | } 17 | 18 | impl FeltOrd of PartialOrd { 19 | #[inline(always)] 20 | fn le(lhs: felt252, rhs: felt252) -> bool { 21 | felt252_le(lhs, rhs) 22 | } 23 | #[inline(always)] 24 | fn ge(lhs: felt252, rhs: felt252) -> bool { 25 | felt252_le(rhs, lhs) 26 | } 27 | #[inline(always)] 28 | fn lt(lhs: felt252, rhs: felt252) -> bool { 29 | felt252_lt(lhs, rhs) 30 | } 31 | #[inline(always)] 32 | fn gt(lhs: felt252, rhs: felt252) -> bool { 33 | felt252_lt(rhs, lhs) 34 | } 35 | } 36 | 37 | impl FeltDiv of Div { 38 | fn div(lhs: felt252, rhs: felt252) -> felt252 { 39 | // Use u256 division as the felt_div is on the modular field 40 | let lhs256: u256 = lhs.into(); 41 | let rhs256: u256 = rhs.into(); 42 | (lhs256 / rhs256).try_into().unwrap() 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/lib.cairo: -------------------------------------------------------------------------------- 1 | mod cumulative_balance; 2 | mod felt_math; 3 | mod types; 4 | mod world_config; 5 | mod supports_interface; 6 | mod uri; 7 | 8 | mod briq_factory; 9 | 10 | mod erc { 11 | mod mint_burn; 12 | mod erc721 { 13 | mod interface; 14 | mod internal_trait; 15 | mod models; 16 | } 17 | mod erc1155 { 18 | mod interface; 19 | mod internal_trait; 20 | mod models; 21 | } 22 | } 23 | 24 | mod migrate; 25 | 26 | mod attributes { 27 | mod attributes; 28 | mod attribute_group; 29 | } 30 | 31 | mod booklet { 32 | mod attribute; 33 | } 34 | 35 | mod box_nft { 36 | mod unboxing; 37 | } 38 | 39 | mod set_nft { 40 | mod assembly; 41 | } 42 | 43 | mod tokens { 44 | mod box_nft_sp; 45 | mod box_nft_briqmas; 46 | 47 | mod booklet_ducks; 48 | mod booklet_starknet_planet; 49 | mod booklet_briqmas; 50 | mod booklet_ducks_frens; 51 | //mod booklet_lil_ducks; 52 | 53 | mod briq_token; 54 | 55 | mod set_nft; 56 | 57 | mod set_nft_ducks; 58 | mod set_nft_sp; 59 | mod set_nft_briqmas; 60 | mod set_nft_1155_ducks_frens; 61 | //mod set_nft_1155_lil_ducks; 62 | } 63 | 64 | #[cfg(test)] 65 | mod tests { 66 | mod test_utils; 67 | 68 | mod shapes; 69 | mod briq_counter; 70 | 71 | mod test_attributes; 72 | mod test_box_nft; 73 | mod test_briq_token; 74 | 75 | mod test_set_nft; 76 | mod test_set_nft_multiple; 77 | mod test_set_nft_1155; 78 | 79 | mod test_world_config; 80 | mod test_briq_factory; 81 | 82 | mod test_check_fts_and_shape_match; 83 | 84 | mod test_uri; 85 | 86 | mod test_shape_packing; 87 | mod test_truck_shape; 88 | 89 | mod contract_upgrade; 90 | mod test_upgradeable; 91 | 92 | // mod test_migrate; 93 | } 94 | -------------------------------------------------------------------------------- /src/tests/briq_counter.cairo: -------------------------------------------------------------------------------- 1 | #[starknet::contract] 2 | mod TestBriqCounterAttributeHandler { 3 | use starknet::ContractAddress; 4 | 5 | use dojo::world::IWorldDispatcher; 6 | use briq_protocol::world_config::{WorldConfig, AdminTrait}; 7 | use briq_protocol::types::{PackedShapeItem, FTSpec}; 8 | 9 | #[storage] 10 | struct Storage {} 11 | 12 | // check that there is at least attribute_id count of briq in set 13 | fn verify_briq_count(attribute_id: u64, shape: Span, fts: Span) { 14 | assert(shape.len().into() >= attribute_id, 'not enough briq'); 15 | 16 | let mut total = 0_u128; 17 | let mut fts = fts; 18 | loop { 19 | match fts.pop_front() { 20 | Option::Some(ft) => { 21 | total += *ft.qty; 22 | }, 23 | Option::None => { 24 | break; 25 | } 26 | }; 27 | }; 28 | 29 | assert(total >= attribute_id.into(), 'not enough briq'); 30 | } 31 | 32 | #[external(v0)] 33 | impl briqCounterHandler of briq_protocol::attributes::attributes::IAttributeHandler { 34 | fn assign( 35 | ref self: ContractState, 36 | world: IWorldDispatcher, 37 | set_owner: ContractAddress, 38 | set_token_id: felt252, 39 | attribute_group_id: u64, 40 | attribute_id: u64, 41 | shape: Array, 42 | fts: Array, 43 | ) { 44 | verify_briq_count(attribute_id, shape.span(), fts.span()); 45 | } 46 | 47 | fn remove( 48 | ref self: ContractState, 49 | world: IWorldDispatcher, 50 | set_owner: ContractAddress, 51 | set_token_id: felt252, 52 | attribute_group_id: u64, 53 | attribute_id: u64 54 | ) {} 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/tests/contract_upgrade.cairo: -------------------------------------------------------------------------------- 1 | #[starknet::interface] 2 | trait IUselessContract { 3 | fn i_request_additional_tps(self: @TState) -> felt252; 4 | } 5 | 6 | #[dojo::contract] 7 | mod ContractUpgrade { 8 | use super::IUselessContract; 9 | 10 | #[external(v0)] 11 | impl UselessContract of IUselessContract { 12 | fn i_request_additional_tps(self: @ContractState) -> felt252 { 13 | 'father' 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/tests/test_box_nft.cairo: -------------------------------------------------------------------------------- 1 | use traits::{Into, TryInto, Default}; 2 | use option::{Option, OptionTrait}; 3 | use result::ResultTrait; 4 | use array::ArrayTrait; 5 | use serde::Serde; 6 | use starknet::ContractAddress; 7 | 8 | use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait}; 9 | use presets::erc721::erc721::interface::IERC721DispatcherTrait; 10 | use presets::erc1155::erc1155::interface::IERC1155DispatcherTrait; 11 | 12 | use briq_protocol::world_config::get_world_config; 13 | use briq_protocol::types::{FTSpec, ShapeItem}; 14 | use briq_protocol::tests::test_utils::{ 15 | WORLD_ADMIN, DEFAULT_OWNER, ZERO, DefaultWorld, spawn_briq_test_world, mint_briqs, impersonate 16 | }; 17 | 18 | use briq_protocol::tests::test_set_nft::convenience_for_testing::create_contract_attribute_group; 19 | use briq_protocol::erc::mint_burn::{MintBurnDispatcher, MintBurnDispatcherTrait}; 20 | use briq_protocol::box_nft::unboxing::{UnboxingDispatcher, UnboxingSafeDispatcher, UnboxingDispatcherTrait, UnboxingSafeDispatcherTrait}; 21 | use debug::PrintTrait; 22 | 23 | #[test] 24 | #[available_gas(300000000)] 25 | fn test_mint_and_unbox() { 26 | let DefaultWorld{world, 27 | briq_token, 28 | booklet_ducks, 29 | booklet_sp, 30 | box_nft, 31 | attribute_groups_addr, 32 | .. } = 33 | spawn_briq_test_world(); 34 | 35 | create_contract_attribute_group(world, attribute_groups_addr, 0x1, booklet_sp.contract_address, Zeroable::zero()); 36 | create_contract_attribute_group(world, attribute_groups_addr, 0x2, booklet_ducks.contract_address, Zeroable::zero()); 37 | 38 | let box_contract_address = box_nft.contract_address; 39 | 40 | assert(UnboxingSafeDispatcher { contract_address: box_nft.contract_address }.unbox(0x1).is_err(), 'expect error'); 41 | 42 | MintBurnDispatcher { contract_address: box_nft.contract_address }.mint( 43 | DEFAULT_OWNER(), 44 | 0x1, 45 | 0x4, 46 | ); 47 | MintBurnDispatcher { contract_address: box_nft.contract_address }.mint( 48 | DEFAULT_OWNER(), 49 | 10, 50 | 0x1, 51 | ); 52 | 53 | impersonate(DEFAULT_OWNER()); 54 | 55 | assert(briq_token.balance_of(DEFAULT_OWNER(), 1) == 0, 'bad balance'); 56 | assert(booklet_sp.balance_of(DEFAULT_OWNER(), 0x1) == 0, 'bad balance 1'); 57 | assert(box_nft.balance_of(DEFAULT_OWNER(), 0x1) == 4, 'bad balance 2'); 58 | 59 | UnboxingDispatcher { contract_address: box_nft.contract_address }.unbox(0x1); 60 | 61 | assert(briq_token.balance_of(DEFAULT_OWNER(), 1) == 434, 'bad balance 2.5'); 62 | assert(booklet_sp.balance_of(DEFAULT_OWNER(), 0x10000000000000001) == 1, 'bad balance 3'); 63 | assert(box_nft.balance_of(DEFAULT_OWNER(), 0x1) == 3, 'bad balance 4'); 64 | 65 | UnboxingDispatcher { contract_address: box_nft.contract_address }.unbox(10); 66 | 67 | assert(briq_token.balance_of(DEFAULT_OWNER(), 1) == 494, 'bad balance 5'); 68 | assert(booklet_ducks.balance_of(DEFAULT_OWNER(), 0x20000000000000001) == 1, 'bad balance 6'); 69 | assert(box_nft.balance_of(DEFAULT_OWNER(), 10) == 0, 'bad balance 7'); 70 | 71 | assert(UnboxingSafeDispatcher { contract_address: box_nft.contract_address }.unbox(10).is_err(), 'expect error'); 72 | } 73 | -------------------------------------------------------------------------------- /src/tests/test_briq_token.cairo: -------------------------------------------------------------------------------- 1 | use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait}; 2 | use presets::erc1155::erc1155::interface::IERC1155DispatcherTrait; 3 | 4 | use briq_protocol::tests::test_utils::{ 5 | WORLD_ADMIN, DEFAULT_OWNER, DefaultWorld, spawn_briq_test_world, mint_briqs, impersonate 6 | }; 7 | 8 | use debug::PrintTrait; 9 | 10 | #[test] 11 | #[available_gas(300000000)] 12 | // The error is an unwrap failed as we use u256 parameters, but then when storing it should fail to convert back. 13 | #[should_panic( 14 | expected: ('Option::unwrap failed.', 'ENTRYPOINT_FAILED') 15 | )] 16 | fn test_balance_overflow() { 17 | let DefaultWorld{world, briq_token, .. } = spawn_briq_test_world(); 18 | 19 | let two_power_128: u256 = 340282366920938463463374607431768211456; 20 | let two_power_128_minus_one = 340282366920938463463374607431768211455; 21 | assert(two_power_128 == two_power_128_minus_one.into() + 1, 'ok'); 22 | mint_briqs(world, DEFAULT_OWNER(), 1, two_power_128_minus_one); 23 | mint_briqs(world, DEFAULT_OWNER(), 1, 1); 24 | } 25 | -------------------------------------------------------------------------------- /src/tests/test_migrate.cairo: -------------------------------------------------------------------------------- 1 | use traits::{Into, TryInto, Default}; 2 | use option::{Option, OptionTrait}; 3 | use result::ResultTrait; 4 | use array::ArrayTrait; 5 | use serde::Serde; 6 | 7 | use starknet::testing::{set_caller_address, set_contract_address}; 8 | use starknet::ContractAddress; 9 | 10 | use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait}; 11 | 12 | use presets::presets::erc_common::utils::{system_calldata}; 13 | use presets::presets::erc721::erc721::interface::IERC721DispatcherTrait; 14 | use presets::presets::erc1155::erc1155::interface::{IERC1155DispatcherTrait}; 15 | use presets::presets::erc1155::erc1155::models::ERC1155BalanceTrait; 16 | 17 | use briq_protocol::tests::test_utils::{ 18 | WORLD_ADMIN, DEFAULT_OWNER, ZERO, DefaultWorld, spawn_briq_test_world, mint_briqs, impersonate 19 | }; 20 | use briq_protocol::world_config::get_world_config; 21 | 22 | use debug::PrintTrait; 23 | 24 | use briq_protocol::migrate::MigrateAssetsParams; 25 | 26 | #[test] 27 | #[available_gas(3000000000)] 28 | fn test_migrate_signature() { 29 | let DefaultWorld{world, .. } = spawn_briq_test_world(); 30 | 31 | world 32 | .execute( 33 | 'migrate_assets', 34 | system_calldata( 35 | MigrateAssetsParams { 36 | migrator: 0x69cfa382ea9d2e81aea2d868b0dd372f70f523fa49a765f4da320f38f9343b3 37 | .try_into() 38 | .unwrap(), 39 | current_briqs: 271, 40 | briqs_to_migrate: 50, 41 | set_to_migrate: 0x0, 42 | backend_signature_r: 0x7e3ef759fe84319b8b2bf01d0bad2e9a3f7ca015a7f79d31427a06a5dcd1021, 43 | backend_signature_s: 0x3518d16d1abf68e6d5dc93c9461cb84750a1f181084837dba7b1568daa2a135, 44 | } 45 | ) 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /src/tests/test_shape_packing.cairo: -------------------------------------------------------------------------------- 1 | use briq_protocol::types::{ShapeItem, PackedShapeItem, ShapePacking}; 2 | 3 | use debug::PrintTrait; 4 | 5 | #[test] 6 | #[available_gas(3000000000)] 7 | fn test_shape_packing_a() { 8 | let shape = ShapeItem { 9 | color: '#ffaaff'.try_into().unwrap(), 10 | material: 1, 11 | x: 1, 12 | y: 2, 13 | z: 3, 14 | }; 15 | let packed = ShapePacking::pack(shape); 16 | //packed.color_material.print(); 17 | //packed.x_y_z.print(); 18 | assert(packed.color_material == 0x236666616166660000000000000001, 'bad color mat packing'); 19 | assert(packed.x_y_z == 0x800000018000000280000003, 'bad color mat packing'); 20 | let unpacked = ShapePacking::unpack(packed); 21 | assert(unpacked.color == '#ffaaff'.try_into().unwrap(), 'bad color unpacking'); 22 | assert(unpacked.material == 1, 'bad material unpacking'); 23 | assert(unpacked.x == shape.x, 'bad x unpacking'); 24 | assert(unpacked.y == shape.y, 'bad y unpacking'); 25 | assert(unpacked.z == shape.z, 'bad z unpacking'); 26 | } 27 | 28 | #[test] 29 | #[available_gas(3000000000)] 30 | fn test_shape_packing_b() { 31 | let shape = ShapeItem { 32 | color: '#ffaaff'.try_into().unwrap(), 33 | material: 1, 34 | x: 0, 35 | y: 0, 36 | z: 0, 37 | }; 38 | let packed = ShapePacking::pack(shape); 39 | //packed.color_material.print(); 40 | //packed.x_y_z.print(); 41 | assert(packed.color_material == 0x236666616166660000000000000001, 'bad color mat packing'); 42 | assert(packed.x_y_z == 0x800000008000000080000000, 'bad color mat packing'); 43 | let unpacked = ShapePacking::unpack(packed); 44 | assert(unpacked.color == '#ffaaff'.try_into().unwrap(), 'bad color unpacking'); 45 | assert(unpacked.material == 1, 'bad material unpacking'); 46 | assert(unpacked.x == shape.x, 'bad x unpacking'); 47 | assert(unpacked.y == shape.y, 'bad y unpacking'); 48 | assert(unpacked.z == shape.z, 'bad z unpacking');} 49 | 50 | 51 | 52 | #[test] 53 | #[available_gas(3000000000)] 54 | fn test_shape_packing_c() { 55 | let shape = ShapeItem { 56 | color: '#ffaaff'.try_into().unwrap(), 57 | material: 1, 58 | x: -1, 59 | y: -0x80000000, 60 | z: -1, 61 | }; 62 | let packed = ShapePacking::pack(shape); 63 | assert(packed.color_material == 0x236666616166660000000000000001, 'bad color mat packing'); 64 | assert(packed.x_y_z == 0x7fffffff000000007fffffff, 'bad color mat packing'); 65 | let unpacked = ShapePacking::unpack(packed); 66 | assert(unpacked.color == '#ffaaff'.try_into().unwrap(), 'bad color unpacking'); 67 | assert(unpacked.material == 1, 'bad material unpacking'); 68 | assert(unpacked.x == shape.x, 'bad x unpacking'); 69 | assert(unpacked.y == shape.y, 'bad y unpacking'); 70 | assert(unpacked.z == shape.z, 'bad z unpacking'); 71 | } 72 | -------------------------------------------------------------------------------- /src/tests/test_world_config.cairo: -------------------------------------------------------------------------------- 1 | use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait}; 2 | 3 | use briq_protocol::tests::test_utils::{ 4 | DefaultWorld, spawn_briq_test_world, WORLD_ADMIN, DEFAULT_OWNER, impersonate, 5 | }; 6 | use briq_protocol::world_config::ISetupWorldDispatcherTrait; 7 | 8 | #[test] 9 | #[available_gas(300000000)] 10 | fn test_world_admin_can_setup_world() { 11 | impersonate(WORLD_ADMIN()); 12 | 13 | let DefaultWorld { world, setup_world, .. } = spawn_briq_test_world(); 14 | setup_world.execute( 15 | world, 16 | starknet::contract_address_const::<1>(), 17 | starknet::contract_address_const::<2>(), 18 | starknet::contract_address_const::<3>(), 19 | starknet::contract_address_const::<4>(), 20 | ); 21 | } 22 | 23 | 24 | #[test] 25 | #[available_gas(300000000)] 26 | #[should_panic] 27 | fn test_not_world_admin_cannot_setup_world() { 28 | impersonate(WORLD_ADMIN()); 29 | 30 | let DefaultWorld { world, setup_world, .. } = spawn_briq_test_world(); 31 | 32 | impersonate(DEFAULT_OWNER()); 33 | 34 | setup_world.execute( 35 | world, 36 | starknet::contract_address_const::<1>(), 37 | starknet::contract_address_const::<2>(), 38 | starknet::contract_address_const::<3>(), 39 | starknet::contract_address_const::<4>(), 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /src/types.cairo: -------------------------------------------------------------------------------- 1 | use starknet::storage_access::{Store, StorePacking}; 2 | use serde::Serde; 3 | use traits::{TryInto, Into}; 4 | use option::OptionTrait; 5 | 6 | use briq_protocol::felt_math::{FeltBitAnd, FeltDiv}; 7 | 8 | 9 | #[derive(Copy, Drop, Serde)] 10 | struct AttributeItem { 11 | attribute_group_id: u64, 12 | attribute_id: u64, 13 | } 14 | 15 | #[derive(Copy, Drop, Serde,)] 16 | struct FTSpec { 17 | token_id: felt252, 18 | qty: u128, 19 | } 20 | 21 | #[derive(Copy, Drop, Serde, Store)] 22 | struct ShapeItem { 23 | // ASCII short string 24 | color: u64, 25 | material: u64, 26 | x: felt252, 27 | y: felt252, 28 | z: felt252, 29 | } 30 | 31 | #[derive(Copy, Drop, Serde, Store)] 32 | struct PackedShapeItem { 33 | color_material: felt252, 34 | x_y_z: felt252, 35 | } 36 | 37 | const TWO_POW_64: felt252 = 0x10000000000000000; 38 | const TWO_POW_32: felt252 = 0x100000000; 39 | const TWO_POW_31: felt252 = 0x80000000; 40 | 41 | const TWO_POW_64_MASK: felt252 = 0xFFFFFFFFFFFFFFFF; 42 | const TWO_POW_32_MASK: felt252 = 0xFFFFFFFF; 43 | 44 | impl ShapePacking of StorePacking { 45 | fn pack(value: ShapeItem) -> PackedShapeItem { 46 | PackedShapeItem { 47 | color_material: value.color.into() * TWO_POW_64 + value.material.into(), 48 | x_y_z: (value.z + TWO_POW_31) + ( 49 | (value.y + TWO_POW_31) * TWO_POW_32) + ( 50 | (value.x + TWO_POW_31) * TWO_POW_64 51 | ), 52 | } 53 | } 54 | 55 | fn unpack(value: PackedShapeItem) -> ShapeItem { 56 | ShapeItem { 57 | color: (((value.color_material & (-1 - TWO_POW_64_MASK))).into() 58 | / Into::::into(TWO_POW_64)) 59 | .try_into() 60 | .unwrap(), 61 | material: (value.color_material & TWO_POW_64_MASK).try_into().unwrap(), 62 | x: (value.x_y_z / TWO_POW_32 / TWO_POW_32 & TWO_POW_32_MASK) - TWO_POW_31, 63 | y: (value.x_y_z / TWO_POW_32 & TWO_POW_32_MASK) - TWO_POW_31, 64 | z: (value.x_y_z & TWO_POW_32_MASK) - TWO_POW_31, 65 | } 66 | } 67 | } 68 | 69 | use briq_protocol::felt_math::FeltOrd; 70 | 71 | impl PackedShapeItemOrd of PartialOrd { 72 | #[inline(always)] 73 | fn le(lhs: PackedShapeItem, rhs: PackedShapeItem) -> bool { 74 | lhs.x_y_z <= rhs.x_y_z 75 | } 76 | #[inline(always)] 77 | fn ge(lhs: PackedShapeItem, rhs: PackedShapeItem) -> bool { 78 | lhs.x_y_z >= rhs.x_y_z 79 | } 80 | #[inline(always)] 81 | fn lt(lhs: PackedShapeItem, rhs: PackedShapeItem) -> bool { 82 | lhs.x_y_z < rhs.x_y_z 83 | } 84 | #[inline(always)] 85 | fn gt(lhs: PackedShapeItem, rhs: PackedShapeItem) -> bool { 86 | lhs.x_y_z > rhs.x_y_z 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/uri.cairo: -------------------------------------------------------------------------------- 1 | //use to_byte_array::{FormatAsByteArray, AppendFormattedToByteArray}; 2 | 3 | use starknet::info::get_tx_info; 4 | 5 | fn get_api_url(path: felt252) -> Array { //ByteArray { 6 | let chain_id = get_tx_info().unbox().chain_id; 7 | if chain_id == 'SN_MAIN' { 8 | return array!['https://api.briq.construction', '/v1/uri/', path, '/starknet-mainnet-dojo/']; 9 | //return "https://api.briq.construction/v1/uri/"; 10 | } else if chain_id == 'SN_GOERLI' { 11 | return array!['https://api.test.sltech.company', '/v1/uri/', path, '/starknet-testnet-dojo/']; 12 | //return "https://api.test.sltech.company/v1/uri/"; 13 | } else { 14 | return array!['https://api.briq.construction', '/v1/uri/', path, '/starknet-testnet-dojo/']; 15 | //return "https://api.briq.construction/v1/uri/"; 16 | } 17 | } 18 | 19 | fn to_reversed_ascii_decimal(token_id: u256) -> Array { 20 | let mut result: Array = array![]; 21 | let mut token_id = token_id; 22 | if token_id == 0 { 23 | result.append(48); 24 | return result; 25 | } 26 | loop { 27 | if token_id == 0 { 28 | break; 29 | } 30 | let digit = token_id % 10_u256; 31 | result.append((digit + 48_u256).try_into().unwrap()); 32 | token_id = token_id / 10_u256; 33 | }; 34 | result 35 | } 36 | 37 | //fn get_url(path: ByteArray, token_id: u256) -> Array { 38 | fn get_url(path: felt252, token_id: u256) -> Array { 39 | let mut result = get_api_url(path); 40 | let mut num = to_reversed_ascii_decimal(token_id).span(); 41 | loop { 42 | if num.len() == 0 { 43 | break; 44 | } 45 | result.append(*num.pop_back().unwrap()); 46 | }; 47 | result.append('.json'); 48 | result 49 | //(get_api_url() + path + "/" + token_id.format_as_byte_array(10_u256.try_into().unwrap()) + ".json").into() 50 | } 51 | 52 | impl ByteArrayToArrayFelt of Into> { 53 | fn into(self: ByteArray) -> Array { 54 | let mut result: Array = array![]; 55 | let mut span = self.data.span(); 56 | loop { 57 | if span.len() == 0 { 58 | break; 59 | } 60 | let word = *(span.pop_front().unwrap()); 61 | result.append(word.into()); 62 | }; 63 | if self.pending_word_len > 0 { 64 | result.append(self.pending_word.into()); 65 | } 66 | result 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /tests-old/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briqNFT/briq-protocol/5db5f812812b3039e94ecce96506fe8dd94a1c69/tests-old/__init__.py -------------------------------------------------------------------------------- /tests-old/attributes_registry_test.cairo: -------------------------------------------------------------------------------- 1 | use array::ArrayTrait; 2 | use array::SpanTrait; 3 | 4 | use briq_protocol::attributes_registry::AttributesRegistry; 5 | 6 | use debug::PrintTrait; 7 | 8 | const ADDRESS: felt252 = 0x1234; 9 | 10 | #[test] 11 | #[available_gas(999999)] 12 | fn test_assign_attribute() { 13 | let mut calldata = ArrayTrait::new(); 14 | calldata.append(ADDRESS); 15 | 16 | let tb = AttributesRegistry::__external::total_balance(calldata.span()); 17 | assert (*tb.at(0_u32) == 0, 'tb == 0'); 18 | 19 | let mut calldata = ArrayTrait::new(); 20 | calldata.append(ADDRESS); 21 | calldata.append(1234); 22 | calldata.append(88); 23 | calldata.append(0); 24 | calldata.append(0); 25 | calldata.append(0); 26 | 27 | AttributesRegistry::__external::assign_attribute(calldata.span()); 28 | 29 | let mut calldata = ArrayTrait::new(); 30 | calldata.append(ADDRESS); 31 | let tb = AttributesRegistry::__external::total_balance(calldata.span()); 32 | (*tb.at(0_u32)).print(); 33 | assert (*tb.at(0_u32) == 1, 'tb == 1'); 34 | } 35 | 36 | #[test] 37 | #[available_gas(999999)] 38 | fn test_bad_address() { 39 | let mut calldata = ArrayTrait::new(); 40 | calldata.append(ADDRESS); 41 | 42 | let tb = AttributesRegistry::__external::total_balance(calldata.span()); 43 | assert (*tb.at(0_u32) == 0, 'tb == 0'); 44 | 45 | let mut calldata = ArrayTrait::new(); 46 | calldata.append(0); 47 | calldata.append(1234); 48 | calldata.append(88); 49 | calldata.append(0); 50 | calldata.append(0); 51 | calldata.append(0); 52 | 53 | AttributesRegistry::__external::assign_attribute(calldata.span()); 54 | } 55 | -------------------------------------------------------------------------------- /tests-old/booklet_test.cairo: -------------------------------------------------------------------------------- 1 | use briq_protocol::booklet_nft::BookletNFT; 2 | use debug::PrintTrait; 3 | 4 | #[test] 5 | #[available_gas(999999)] 6 | fn test_storage_conflict() { 7 | BookletNFT::setAttributesRegistryAddress_(345678); 8 | let toto = BookletNFT::getBoxAddress_(); 9 | assert(toto == 0, 'toto should be 0'); 10 | } 11 | -------------------------------------------------------------------------------- /tests-old/box_test.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | import os 3 | from typing import Tuple 4 | import pytest 5 | import pytest_asyncio 6 | 7 | from starkware.starknet.testing.starknet import Starknet 8 | from starkware.starknet.testing.starknet import StarknetContract 9 | from starkware.starkware_utils.error_handling import StarkException 10 | 11 | from .conftest import declare_and_deploy, proxy_contract 12 | 13 | @pytest_asyncio.fixture(scope="session") 14 | async def factory_root(): 15 | starknet = await Starknet.empty() 16 | [box_contract, _] = await declare_and_deploy(starknet, "box_nft.cairo") 17 | return (starknet, box_contract) 18 | 19 | @pytest_asyncio.fixture 20 | async def factory(factory_root): 21 | [starknet, box_contract] = factory_root 22 | state = Starknet(state=starknet.state.copy()) 23 | return namedtuple('State', ['starknet', 'box_contract'])( 24 | starknet=state, 25 | box_contract=proxy_contract(state, box_contract), 26 | ) 27 | 28 | @pytest.mark.asyncio 29 | async def test_view(factory): 30 | assert (await factory.box_contract.get_box_data(0x1).call()).result.data == factory.box_contract.BoxData(54, 0, 0, 0, 0, 0xcafe) 31 | assert (await factory.box_contract.get_box_data(0x2).call()).result.data == factory.box_contract.BoxData(20, 10, 0, 0, 0, 0xdead) 32 | #with pytest.raises(StarkException): 33 | # TODO: this now returns random garbage. 34 | # await factory.box_contract.get_box_data(0x3).call() 35 | -------------------------------------------------------------------------------- /tests-old/briq_factory_test.cairo: -------------------------------------------------------------------------------- 1 | use array::ArrayTrait; 2 | use array::SpanTrait; 3 | 4 | use briq_protocol::briq_factory::BriqFactory; 5 | 6 | use debug::PrintTrait; 7 | 8 | const ADDRESS: felt252 = 0x1234; 9 | 10 | #[test] 11 | #[available_gas(999999)] 12 | fn test_get_t() { 13 | BriqFactory::initialise(BriqFactory::decimals); 14 | 15 | assert(BriqFactory::get_current_t() == BriqFactory::decimals, 'bad T'); 16 | 17 | BriqFactory::integrate(6478383, 1).print(); 18 | BriqFactory::integrate(10000, 10).print(); 19 | assert(BriqFactory::integrate(6478383, 1) == 647838450000000000000, 'bad T'); 20 | assert(BriqFactory::integrate(6478383, 347174) == 230939137995400000000000000, 'bad T'); 21 | } 22 | -------------------------------------------------------------------------------- /tests-old/cairo_project.toml: -------------------------------------------------------------------------------- 1 | [crate_roots] 2 | briq_protocol = "../src" 3 | tests = "." 4 | -------------------------------------------------------------------------------- /tests-old/lib.cairo: -------------------------------------------------------------------------------- 1 | //mod attributes_registry_test; 2 | //mod booklet_test; 3 | //mod uri_string; 4 | //mod shape_test; 5 | mod briq_factory_test; -------------------------------------------------------------------------------- /tests-old/shape_ducks_test.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | import os 3 | from types import SimpleNamespace 4 | import pytest 5 | import pytest_asyncio 6 | 7 | from starkware.starknet.testing.starknet import Starknet 8 | from starkware.starkware_utils.error_handling import StarkException 9 | from starkware.starknet.business_logic.state.state_api_objects import BlockInfo 10 | 11 | from .conftest import declare, declare_and_deploy, proxy_contract, compile 12 | 13 | ADDRESS = 0xcafe 14 | OTHER_ADDRESS = 0xfade 15 | DISALLOWED_ADDRESS = 0xdead 16 | 17 | 18 | @pytest_asyncio.fixture(scope="session") 19 | async def factory_root(): 20 | starknet = await Starknet.empty() 21 | 22 | [shapes, _] = await declare_and_deploy(starknet, "shape/shape_store_ducks.cairo") 23 | 24 | return (starknet, shapes) 25 | 26 | 27 | @pytest_asyncio.fixture 28 | async def factory(factory_root): 29 | [starknet, shapes] = factory_root 30 | state = Starknet(state=starknet.state.copy()) 31 | return SimpleNamespace( 32 | starknet=state, 33 | shapes=proxy_contract(state, shapes), 34 | ) 35 | 36 | 37 | @pytest.mark.asyncio 38 | async def test_bids(factory): 39 | assert (await factory.shapes.get_local_index_(2 * 2**192 + 3).call()).result.res == 0 40 | -------------------------------------------------------------------------------- /tests-old/shape_test.cairo: -------------------------------------------------------------------------------- 1 | use array::ArrayTrait; 2 | use array::SpanTrait; 3 | 4 | use debug::PrintTrait; 5 | 6 | const ADDRESS: felt252 = 0x1234; 7 | 8 | use briq_protocol::shape::shape_store; 9 | 10 | #[test] 11 | #[available_gas(999999)] 12 | fn test_decompress_shape() { 13 | let color_nft_material = 0x233966323835610000000000000000000000000000000001; 14 | let x_y_z = 0x7ffffffffffffffe80000000000000008000000000000002; 15 | let decompressed = shape_store::decompress_data(shape_store::ShapeItem { color_nft_material, x_y_z }, 0); 16 | assert(decompressed.material == 1, 'Bad material'); 17 | assert(decompressed.color == 9914735276864865, 'Bad color'); // #9f285a 18 | assert(decompressed.material == 1, 'Bad material'); 19 | assert(decompressed.x == -2, 'Bad x'); 20 | assert(decompressed.y == 0, 'Bad y'); 21 | assert(decompressed.z == 2, 'Bad z'); 22 | assert(decompressed.nft_token_id == 0, 'Bad NFT'); 23 | } 24 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | ignore = E128, E722 -------------------------------------------------------------------------------- /vs-briq.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "cairols.venvCommand": ". venv/bin/activate", 9 | "python.defaultInterpreterPath": "./venv/bin/python", 10 | "python.analysis.typeCheckingMode": "basic", 11 | "python.linting.enabled": true, 12 | "python.linting.mypyEnabled": true, 13 | "python.linting.flake8Enabled": true 14 | } 15 | } --------------------------------------------------------------------------------