Squishy Contract

The Squishy Contract`s goal is to serve as a template for nearly any asset in the world for tokenization and effective and secure on-chain management.

Contract Structure

  1. Overview: Each smart contract represents a single asset and manages all associated rights. It contains a bundle of ERC721 tokens, each representing a portion of the asset's ownership and rights.

  2. Components:

    • Asset Management Module: Handles the core functionality related to the asset, including ownership, rights distribution, and dividend payouts.

    • Token Management Module: Manages the creation, transfer, and tracking of individual ERC721 tokens.

    • Role-Based Access Control (RBAC) Module: Integrates with Paddle Identity to manage user roles and permissions.

    • Data Storage Module: Interfaces with IPFS for metadata storage and on-chain data management.

Code Structure:

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract AssetManagementContract is AccessControlEnumerable {
    uint256 public lastApprovedPrice; // Price determined at minting, updated by oracles
    uint256 public listingPrice;      // User-set price for listing
    string public assetStatus;
    string public ipfsHash;           // IPFS hash for extended metadata
    ERC721 public tokenContract;

    // Define roles
    bytes32 public constant DIVIDEND_MANAGER_ROLE = keccak256("DIVIDEND_MANAGER_ROLE");
    bytes32 public constant RENT_MANAGER_ROLE = keccak256("RENT_MANAGER_ROLE");
    bytes32 public constant ASSET_MANAGER_ROLE = keccak256("ASSET_MANAGER_ROLE");

    // Events
    event AssetStatusUpdated(string newStatus);
    event PriceUpdated(uint256 newPrice);

    // Metadata struct for on-chain data
    struct AssetMetadata {
        address assetAddress;
        uint256 buildingYear;
        uint256 verificationCompanyId;
        bool isMetadataSet;
    }

    mapping(uint256 => AssetMetadata) public assetMetadata;

    constructor(uint256 _assetPrice, string memory _ipfsHash, address _tokenContract) {
        lastApprovedPrice = _assetPrice;
        ipfsHash = _ipfsHash;
        tokenContract = ERC721(_tokenContract);

        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
    }

    // Update asset status
    function updateAssetStatus(string memory newStatus) public onlyRole(ASSET_MANAGER_ROLE) {
        assetStatus = newStatus;
        emit AssetStatusUpdated(newStatus);
    }

    // Set listing price
    function setListingPrice(uint256 _listingPrice) public onlyRole(ASSET_MANAGER_ROLE) {
        listingPrice = _listingPrice;
        emit PriceUpdated(_listingPrice);
    }

    // Calculate price per token
    function getPricePerToken(bool useListingPrice) public view returns (uint256) {
        uint256 totalSupply = tokenContract.totalSupply();
        require(totalSupply > 0, "No tokens minted yet");
        uint256 price = useListingPrice ? listingPrice : lastApprovedPrice;
        return price / totalSupply;
    }

    // Minting tokens with metadata pointer check
    function mintTokenWithMetadata(uint256 tokenId, AssetMetadata memory metadata) public onlyRole(ASSET_MANAGER_ROLE) {
        if (!assetMetadata[tokenId].isMetadataSet) {
            assetMetadata[tokenId] = metadata;
        }
        tokenContract.mint(msg.sender, tokenId);
    }

    // Additional functions for role management, DAO integration, etc.
    // ...

}

// ERC721 Token Contract with minting function
contract AssetToken is ERC721, AccessControlEnumerable {
    // Token minting logic here
    // ...
}

// Subcontracts for specific rights management
// ...

Key Features and Operations

  • Dual Pricing Mechanism: Differentiates between 'last approved price' and 'listing price', catering to different market scenarios.

  • Asset Metadata Management: On-chain metadata includes basic asset details, with extended data stored on IPFS.

  • Dynamic Price Calculation: Offers the flexibility to calculate the price per token based on either the 'last approved price' or the 'listing price'.

  • Minting with Metadata Check: Ensures metadata is not duplicated by creating pointers to already stored data.

  • Role-Based Access and Event Logging: Ensures secure and transparent operations within the contract, with specific roles assigned for different operations.

Token Structure

  1. ERC721 Standard: Each token is an ERC721, ensuring uniqueness and non-fungibility.

  2. Metadata Pointer: Tokens contain a pointer to the contract's stored IPFS hash, avoiding redundant data storage.

  3. Uniform Value and Rights: All tokens have equal value and represent an equal share of the asset's rights.

Code Structure:

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

// Interface for AssetManagementContract
interface IAssetManagementContract {
    function getLastApprovedPrice() external view returns (uint256);
    function getListingPrice() external view returns (uint256);
    function getTokenSupply() external view returns (uint256);
    function getIpfsHash() external view returns (string memory);
}

contract AssetToken is ERC721, Ownable {
    uint256 private _tokenIds;
    address public assetManagementContractAddress;

    // Constructor setting the asset management contract address
    constructor(address _assetManagementContract) ERC721("AssetToken", "ATKN") {
        require(_assetManagementContract != address(0), "Invalid contract address");
        assetManagementContractAddress = _assetManagementContract;
    }

    // Mint new tokens
    function mint(address to) public onlyOwner {
        _tokenIds++;
        _mint(to, _tokenIds);
    }

    // Get price per token based on the last approved price
    function getLastApprovedPricePerToken() public view returns (uint256) {
        IAssetManagementContract amc = IAssetManagementContract(assetManagementContractAddress);
        uint256 lastApprovedPrice = amc.getLastApprovedPrice();
        uint256 totalSupply = amc.getTokenSupply();
        require(totalSupply > 0, "Total supply is zero");
        return lastApprovedPrice / totalSupply;
    }

    // Get price per token based on the listing price
    function getListingPricePerToken() public view returns (uint256) {
        IAssetManagementContract amc = IAssetManagementContract(assetManagementContractAddress);
        uint256 listingPrice = amc.getListingPrice();
        uint256 totalSupply = amc.getTokenSupply();
        require(totalSupply > 0, "Total supply is zero");
        return listingPrice / totalSupply;
    }

    // Override tokenURI to point to metadata stored in AssetManagementContract
    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
        IAssetManagementContract amc = IAssetManagementContract(assetManagementContractAddress);
        string memory ipfsHash = amc.getIpfsHash();
        return string(abi.encodePacked("ipfs://", ipfsHash));
    }

    // Additional functions related to token management go here
}

Key Enhancements and Operations

  • Asset Management Contract Interface: Introduces an interface IAssetManagementContract to interact with the AssetManagementContract. This creates a direct connection between the tokens and the contract, enabling access to crucial data like pricing and metadata.

  • Dynamic Pricing: Incorporates functions getLastApprovedPricePerToken and getListingPricePerToken to calculate the price per token based on the last approved asset price or listing price, divided by the total number of tokens.

  • Token Minting: The mint function is responsible for creating new tokens. The token ID is incremented with each mint, ensuring uniqueness.

  • Metadata Linkage: Overrides the tokenURI function to provide a URI pointing to the asset's metadata stored on IPFS. This ensures that each token is linked to its relevant metadata.

Token Parameters

  1. Minimum Value Requirement: Each token must have a value above €500.

  2. Transferability: Tokens can be transferred 24/7, leveraging the inherent features of blockchain technology.

  3. Rights Representation: Each token represents an equal percentage of ownership, rental rights, dividend rights, and other associated rights.

On- & Off-Chain Data Storage

  1. On-Chain Storage: Utilizes Ethereum blockchain for storing critical data such as asset price, token information, and user roles.

  2. Off-Chain Storage (IPFS): Metadata about the asset is stored on IPFS, with the hash reference stored on-chain. This approach ensures data integrity and accessibility without overloading the blockchain.

IPFS / On-Chain

  1. Metadata Management: Asset metadata, including descriptions, valuations, and legal information, is stored on IPFS.

  2. On-Chain Hash Reference: The smart contract stores the IPFS hash, ensuring metadata is tamper-proof and permanently accessible.

Token Minting

  1. One-Time Minting Process: Tokens are minted in a single transaction to minimize costs and ensure efficiency.

  2. External Input Handling: The contract accepts external inputs (such as the number of tokens to mint and IPFS hashes) for minting processes.

Role-Based Access Control with Paddle Identity

  1. Integration with Paddle Identity: The contract utilizes Paddle Identity for secure and private user authentication using zk-proofs and biometric data.

  2. Role Assignment: Users are assigned roles based on their token ownership, enabling specific rights and actions within the contract.

Predefined Roles through the Tokenization Contract

  1. Dividend Receiver: Entitled to receive dividends from the asset's earnings. The contract automatically distributes dividends from its Ethereum pool to these role holders.

  2. Voting Rights: Token holders may have voting rights on decisions affecting the asset, such as maintenance or structural changes.

  3. Asset Manager Role: A designated role for managing significant decisions about the asset, subject to token holder approval.

Conclusion

This technical documentation outlines a comprehensive and scalable smart contract and token structure for asset tokenization. It leverages the ERC721 standard, integrates with Paddle Identity for role-based access, and utilizes both on-chain and IPFS solutions for efficient data management. The contract is designed to be adaptable for various assets, with a focus on security, transparency, and efficiency in managing and representing asset ownership and rights on the Ethereum blockchain.

Last updated

Logo