Highrise Contracts
Introduction
- This repo contains contracts to support:
- Highrise land and estates
- Funding contract to run the sales on our webpage
Contracts functionality
Land and Estate
-
HighriseLand
andHighriseEstate
contracts are ERC721 tokens representing parts of Highrise world that inherit following openzeppelin contracts:- ERC721Upgradeable
- ERC721EnumerableUpgradeable
- ERC721RoyaltyUpgradeable
- AccessControlUpgradeable
- Initializable
-
Both of these are supported to work on OpenSea.
- Owner of the contract for opensea is defined by
OWNER_ROLE
in access control. - Gas-less listing is supported by overriding
isApprovedForAll
method
- Owner of the contract for opensea is defined by
-
Estates can be constructed/deconstructed from/to land parcels. Supported shapes are 3x3, 6x6, 9x9 and 12x12
-
To create estates from land parcels user must first approve estate contract for transferring. This is achieved by ERC721
approve
function inHighriseLand
contract and in custom batch approval functionapproveForTransfer
function inHighriseLandV2
-
Both Land and Estate contracts are intended to be upgradeable. This is done by using OpenZeppelin
TransparentUpgradeableProxy
to separate the proxy and implementation contracts. This provides us with flexibility to add new logic and storage variables to the contracts in the future.
Token IDs and Coordinates
- Land coordinates are in range (-250, 250)
- Token ID is generated on Highrise backend with the following logic:
from struct import pack
def coordinates_to_token_id(coords: tuple[int, int]) -> int:
"""X and Y are 2 bytes each."""
as_bytes = pack(">hh", coords[0], coords[1])
return int.from_bytes(as_bytes, "big")
- See https://docs.python.org/3/library/struct.html for more understanding.
- Estate contract has
token_id>coordinates
conversion function implemented. In that way estate contract can validate that parcels passed for estate creation have valid shape. - Two tests are implemented to validate
token_id<>coordinates
conversion works:test_coordinates_parsing
- generates token id from coordinates in python and calls estate contract parse function. Compares that function returned coordinates match the ones initially used fortoken_id
generationtest_full_map_to_estates
- All land parcels in the map are merged into 3x3 estates- these tests are skipped when running
brownie test
due to long execution time
Funding contract
HighriseLandFund
contract is a temporary contract used for sale purposes. It collects users payments and mints the Land tokens to the users.- Contract States:
ENABLED
- contract accepts payments and mints LandDISABLED
- contract does not accept payments, owner can enable the contract or withdraw funds
- Entrypoint to the contract from the user perspective is the
fund
function, which accepts a payload signed with a wallet key on our backend. - When the payload signature is confirmed the payload is unpacked into
tokenId
,expiry
andcost
for minting purposes. After requirements are met Land NFT is minted to the user FundLandEvent
is emitted when funding transaction is successful. Pending potential removal - not required anymore since we can track ERC721Transfer
events directly.- Contract is granted
MINTER_ROLE
forHighriseLand
Prerequisites
- Install
ganache
- https://trufflesuite.com/ganache/ - Install
brownie
(pipx
is also required to install it)# `pipx` install python3 -m pip install --user pipx python3 -m pipx ensurepath # install `brownie` with `pipx` pipx install eth-brownie
- Setup
.env
file and include the following variablesDEV_ACCOUNT_NAME
- account stored inbrownie
used for deploying to testnetsWEB3_INFURA_PROJECT_ID
- gateway to blockchain development - set up your account here https://infura.io/ETHERSCAN_TOKEN
- create account on https://etherscan.io/ - used for verifying and publishing smart contractsENVIRONMENT_NAME
- Highrise environment that contracts will be used on
Brownie commands
brownie init
- initializes the project and creates project structurebrownie compile
- compiles all contracts withincontracts/
brownie run <script>
- runs custom python scriptbrownie test
- runs testsbrownie accounts list
- lists all stored accountsbrownie accounts new <account-name>
- import existing account via private key. Stored accounts are in encrypted JSON files known askeystores
brownie networks list
- list all available networks to connectbrownie networks add
- add new network
Ethereum Theory
- Why to emit events
- There are two types of Solidity event parameters: indexed and not indexed,
- Events are used for return values from the transaction and as a cheap data storage,
- Blockchain keeps event parameters in transaction logsEvents can be filtered by name and by contract address