├── .husky ├── .gitignore └── pre-commit ├── .gitignore ├── .prettierignore ├── hardhat.config.ts ├── .eslintrc.js ├── README.md ├── contracts ├── parcel.sol └── ERC721Parcel.sol ├── .circleci └── config.yml ├── LICENSE └── package.json /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | artifacts 2 | cache 3 | node_modules 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore artifacts: 2 | artifacts 3 | cache 4 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import { HardhatUserConfig } from 'hardhat/config' 2 | 3 | export default { 4 | solidity: "0.8.4", 5 | } as HardhatUserConfig; 6 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | module.exports = { 4 | root: true, 5 | parser: "@typescript-eslint/parser", 6 | plugins: ["@typescript-eslint"], 7 | extends: [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/recommended", 10 | "prettier", 11 | ], 12 | rules: { 13 | "@typescript-eslint/no-unused-vars": ["warn", { args: "none" }], 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @citydaoproject/contracts 2 | 3 | CityDAO's Solidity smart contracts 4 | 5 | 6 | ## Prerequisites 7 | 8 | - [NodeJS](https://nodejs.org/en/) 9 | - v12.22.4 <= 10 | 11 | ## Installation 12 | 13 | To install all necessary dependencies, from project root run: 14 | 15 | ```shell 16 | npm ci 17 | ``` 18 | 19 | ## Compiling contracts 20 | 21 | To compile the contracts, from project root run: 22 | 23 | ```shell 24 | npm run compile 25 | ``` 26 | -------------------------------------------------------------------------------- /contracts/parcel.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | // import "./ERC721Tradable.sol"; 6 | 7 | /// @author CityDAO team 8 | /// @title A simple ERC721 token that represents land parcels 9 | 10 | contract Parcel is ERC721Parcel { 11 | constructor(address _proxyRegistryAddress) 12 | ERC721Tradable("CityDAO land parcels", "CITY", _proxyRegistryAddress) 13 | {} 14 | 15 | function baseTokenURI() public pure override returns (string memory) { 16 | return "https://parcels.cityDAO.com/api/parcels/"; 17 | } 18 | 19 | function contractURI() public pure returns (string memory) { 20 | return "https://parcels.cityDAO.com/contracts/ERC721-parcels"; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | node: circleci/node@3.0.0 5 | 6 | jobs: 7 | compile: 8 | executor: 9 | name: node/default 10 | steps: 11 | - checkout 12 | - node/install-packages 13 | - run: npm run compile 14 | - persist_to_workspace: 15 | root: . 16 | paths: 17 | - . 18 | 19 | lint: 20 | executor: 21 | name: node/default 22 | steps: 23 | - checkout 24 | # Install the latest npm - the node Orb takes care of it 25 | - node/install-npm 26 | # Install dependencies - the node Orb take care of installation and dependency caching 27 | - node/install-packages: 28 | cache-path: node_modules 29 | override-ci-command: npm ci 30 | - run: npm run lint 31 | # Save workspace for subsequent jobs 32 | - persist_to_workspace: 33 | root: . 34 | paths: 35 | - . 36 | 37 | workflows: 38 | version: 2 39 | build_and_test: 40 | jobs: 41 | - lint 42 | - compile: 43 | requires: 44 | - lint 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 CityDAO 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@citydaoproject/contracts", 3 | "description": "CityDAO's Solidity smart contracts", 4 | "private": true, 5 | "version": "0.1.0", 6 | "scripts": { 7 | "compile": "hardhat compile", 8 | "lint": "eslint . --ext .ts", 9 | "prepare": "husky install", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "lint-staged": { 13 | "*.{js,ts}": [ 14 | "eslint --fix" 15 | ] 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/citydaoproject/contracts.git" 20 | }, 21 | "keywords": [ 22 | "cityDAO", 23 | "ethereum", 24 | "smart contracts", 25 | "solidity", 26 | "web3" 27 | ], 28 | "author": "CityDAO", 29 | "license": "MIT", 30 | "bugs": { 31 | "url": "https://github.com/citydaoproject/contracts/issues" 32 | }, 33 | "homepage": "https://github.com/citydaoproject/contracts#readme", 34 | "devDependencies": { 35 | "@typescript-eslint/eslint-plugin": "^4.28.5", 36 | "@typescript-eslint/parser": "^4.28.5", 37 | "eslint": "^7.32.0", 38 | "eslint-config-prettier": "^8.3.0", 39 | "hardhat": "^2.5.0", 40 | "husky": "^7.0.1", 41 | "lint-staged": "^11.1.1", 42 | "ts-node": "^10.1.0" 43 | }, 44 | "dependencies": { 45 | "@openzeppelin/contracts": "^4.2.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /contracts/ERC721Parcel.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.9.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 6 | import "@openzeppelin/contracts/utils/Counters.sol"; 7 | import "@openzeppelin/contracts/utils/Strings.sol"; 8 | import "hardhat/console.sol"; 9 | 10 | /// @author CityDAO team: @odyslam, @CruzMolina 11 | 12 | contract cityDaoParcel is ERC721, Ownable { 13 | address proxyRegistryAddress; 14 | int256 parcelId = 0; 15 | uint256 private _currentTokenId = 0; 16 | 17 | mapping(uint256 => uint256) public tokenSupply; 18 | mapping(uint256 => address) public parcelIdToOwners; 19 | mapping(uint256 => Parcel) public parcelIdToParcels; 20 | 21 | constructor( 22 | string memory _name, 23 | string memory _symbol, 24 | address _proxyRegistryAddress 25 | ) ERC721(_name, _symbol) { 26 | proxyRegistryAddress = _proxyRegistryAddress; 27 | _initializeEIP712(_name); 28 | } 29 | 30 | struct Parcel { 31 | bytes32 lat; 32 | bytes32 long; 33 | uint16 leaseLength; 34 | string metadataLocationHash; 35 | // locationHash = IPFS:// or AR:// 36 | } 37 | 38 | function transferParcel( 39 | address _toAddress, 40 | address _fromAddress, 41 | uint256 _parcelId 42 | ) public { 43 | safeTransferFrom(_toAddress, _fromAddress, _parcelId, 1); 44 | // Assume that the above will revert if _fromAddress does not own the parcel 45 | parcelIdToOwners[_parcelId] = _toAddress; 46 | } 47 | 48 | function mintParcelToAddress( 49 | address _toAddress, 50 | uint256 _quantity, 51 | bytes32 _lat, 52 | bytes32 _long, 53 | uint16 _leaseLength, 54 | string _metadataLocationHash 55 | ) external onlyOwner returns (uint256) { 56 | uint256 newTokenId = _getNextTokenId(); 57 | Parcel parcel = Parcel( 58 | _lat, 59 | _long, 60 | _leaseLength, 61 | _metadataLocationHash 62 | ); 63 | parcelIdToOwners[parcelId] = _toAddress; 64 | parcelIdToParcels[parcelId] = parcel; 65 | _mint(_toAddress, parcelId); 66 | parcelOwners[_toAddress] = parcel; 67 | _incrementTokenId(); 68 | return parcelId; 69 | } 70 | 71 | function getParcelOwner(uint256 _parcelId) external view returns (address) { 72 | return parcelIdToOwners[_parcelId]; 73 | } 74 | 75 | function setBaseMetadataURI(string memory _newBaseMetadataURI) 76 | public 77 | onlyOwner 78 | { 79 | _setBaseMetadataURI(_newBaseMetadataURI); 80 | } 81 | 82 | function _incrementTokenId() private { 83 | _currentTokenId++; 84 | } 85 | 86 | function baseTokenURI() public pure virtual returns (string memory); 87 | 88 | function tokenURI(uint256 _tokenId) 89 | public 90 | pure 91 | override 92 | returns (string memory) 93 | { 94 | return 95 | string( 96 | abi.encodePacked(baseTokenURI(), Strings.toString(_tokenId)) 97 | ); 98 | } 99 | 100 | function isApprovedForAll(address owner, address operator) 101 | public 102 | view 103 | override 104 | returns (bool) 105 | { 106 | // Whitelist OpenSea proxy contract for easy trading. 107 | ProxyRegistry proxyRegistry = ProxyRegistry(proxyRegistryAddress); 108 | if (address(proxyRegistry.proxies(owner)) == operator) { 109 | return true; 110 | } 111 | 112 | return super.isApprovedForAll(owner, operator); 113 | } 114 | 115 | /** 116 | * This is used instead of msg.sender as transactions won't be sent by the original token owner, but by OpenSea. 117 | */ 118 | function _msgSender() internal view override returns (address sender) { 119 | return ContextMixin.msgSender(); 120 | } 121 | } 122 | --------------------------------------------------------------------------------