The [Mock bridge contract](https://github.com/rsksmart/rsk-runes/tree/main/contracts) defines a new token type, **RuneToken**, based on the **ERC-1155 standard**. It also uses the **Ownable** contract, which restricts certain functions to the contract's owner. ## **Key Imports:** ```plaintext import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; ``` * **ERC1155**: This is a token standard that supports both fungible and non-fungible tokens within the same contract. * **Ownable**: This contract standard restricts certain actions to only the contract's owner (the one who deployed it or someone assigned as the owner). * **Strings**: A utility library for working with string conversions. ## **Main Components of the Contract** ### **Events:** * `TokensFrozen`: Emits an event when tokens are frozen for a specific account. * `TokensUnfrozen`: Emits an event when tokens are unfrozen. ### **Data Structures:** * **Balance**: Holds the account and balance of a token. * **TokenInfo**: Contains details about a token, such as its URI ( **Uniform Resource Identifier**), name, symbol, maximum supply, current supply, default minting amount, and balance. ### **Mappings:** * `_tokenInfos`: Stores the information about each token, keyed by the token ID. * `_userTokens`: Tracks all tokens held by a user. * `_frozenTokens`: Keeps track of how many tokens are frozen for each user. ## **2\. The Constructor** ```js constructor(address initialOwner) ERC1155("") Ownable(initialOwner) {} ``` * **ERC1155 ("")**: This calls the ERC1155 constructor, but the URI is set as an empty string initially. * **Ownable (initialOwner)**: The `Ownable` contract is initialized with the `initialOwner` as the owner of the contract, allowing only this address to perform certain actions (e.g., minting). ## **3\. The `uri` Function** ``` function uri(uint256 tokenId) public view override returns (string memory) { return _tokenInfos[tokenId].uri; } ``` This function returns the URI for a given token ID. The URI typically points to a metadata file that contains additional details about the token (e.g., images, descriptions). ## **4\. Minting Fungible Tokens** ```js function mintFungible( string memory tokenURI, string memory runeName, string memory symbol, uint256 maxSupply, uint256 initialSupply, uint256 defaultMintAmount, address receiver ) public onlyOwner { // Function logic here } ``` This function allows the owner of the contract to mint fungible tokens. ### **Steps Involved:** 1. **Check max supply**: Ensure that the initial supply is not greater than the maximum allowed supply. 2. **Generate a token ID**: A unique token ID is created by hashing the `runeName` using `keccak256`. 3. **Token ID uniqueness check**: Ensure that the token ID doesn't already exist. 4. **Save Token Info**: Store details about the token in the `_tokenInfos` mapping. 5. **Mint the token**: Mint the specified amount (`initialSupply`) to the `receiver` address. 6. **Track ownership**: Add the minted token to the user's list of owned tokens using `_addUserToken`. ## **5\. Minting Non-Fungible Tokens (NFTs)** ```js function mintNonFungible( string memory tokenURI, string memory runeName, string memory symbol, address receiver ) public onlyOwner { // Function logic here } ``` This function is similar to `mintFungible` but for minting non-fungible tokens. A non-fungible token is a unique token, meaning only one exists. ### **Key Differences:** * **Max Supply** is always `1` for non-fungible tokens. * **Current Supply** is also set to `1`. ## **6\. Minting More Tokens** ```js function mintMore( string memory runeName, address receiver ) external onlyOwner { // Function logic here } ``` This function allows the contract owner to mint additional tokens of an existing fungible token, as long as the new supply doesn’t exceed the maximum supply. ### **Key Steps:** 1. **Check token existence**: Ensure the token exists by checking its `maxSupply`. 2. **Check supply limits**: Ensure the current supply plus the new minting amount doesn’t exceed the max supply. 3. **Mint tokens**: Mint more tokens to the `receiver`. ## **7\. Token Freezing and Unfreezing** ### **Freezing Tokens:** ```js function freezeTokens(string memory runeName, uint256 amount, address owner) external onlyOwner { // Function logic here } ``` * Freezing tokens restricts the user from transferring them. * The function ensures that the account has sufficient tokens to freeze. * The frozen amount is added to `_frozenTokens`. ### **Unfreezing Tokens:** ```js function unfreezeTokens(string memory runeName, uint256 amount, address owner) external onlyOwner { // Function logic here } ``` * This function unfreezes the tokens, allowing the user to transfer them again. * The frozen amount is reduced from `_frozenTokens`. ## **8\. Token Information Queries** ### **Get Token Information:** ```js function getTokenInfo(uint256 tokenId, address holder) public view returns (TokenInfo memory) { // Function logic here } ``` * This function retrieves the details about a token (such as URI, name, symbol, max supply, etc.). * It can also include the token balance of a specific `holder` if the `holder` address is provided. ### **Get Tokens Owned by a User:** ```js function getUserTokens(address user) public view returns (uint256[] memory) { return _userTokens[user]; } ``` * This function returns a list of all token IDs owned by a specific user. ## **9\. Token Transfer Functions with Freezing Consideration** ERC1155 includes transfer functions (`safeTransferFrom` and `safeBatchTransferFrom`). These are overridden in this contract to take into account frozen tokens. ```js function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes memory data ) public virtual override { require(balanceOf(from, id) >= amount + _frozenTokens[id][from], "Insufficient unlocked balance for transfer"); super.safeTransferFrom(from, to, id, amount, data); } ``` This ensures that users cannot transfer frozen tokens. The contract checks that the unlocked balance (total balance minus frozen balance) is sufficient before allowing transfers. ## **10\. Overriding `balanceOf` to Consider Frozen Tokens** ```js function balanceOf(address account, uint256 tokenId) public view override returns (uint256) { uint256 totalBalance = super.balanceOf(account, tokenId); uint256 frozenBalance = _frozenTokens[tokenId][account]; return totalBalance - frozenBalance; } ``` This function returns the number of **unfrozen** tokens owned by a user for a specific token ID. :::info[Complete Codebase on GitHub] [**Complete RSK-Runes**](https://github.com/rsksmart/rsk-runes/tree/main/contracts) ::: ## **Smart Contract Deployment** To deploy the Runes smart contract using Remix IDE, follow these steps in detail: ### **Step 1: Access Remix IDE** 1. Open your web browser and go to [Remix IDE](https://remix.ethereum.org/#lang=en&optimize=false&runs=200&evmVersion=null&version=soljson-v0.8.26+commit.8a97fa7a.js). ### **Step 2: Create a New File** 1. In the Remix IDE, navigate to the **File Explorer** (the first icon on the left sidebar). 2. Click on the **file** icon to create a new file. 3. Name the file `RuneToken.sol`. ### **Step 3: Copy and Paste the Smart Contract** 1. Locate the `RuneToken.sol` file from the RSK-RUNES repository under the contracts folder 2. Open the `RuneToken.sol` file and copy the entire smart contract code. 3. Paste the copied code into the newly created `RuneToken.sol` file in Remix IDE. 4. Click on the **Save** icon (the disk icon) to save the file. ### **Step 4: Compile the Smart Contract** 1. Go to the **Solidity Compiler** tab (the third icon in the left sidebar). 2. Make sure the compiler version matches `0.8.26`. If not, select the correct version from the dropdown menu. 3. Click on the **Compile RuneToken.sol** button. A green check icon inside a circle will appear, indicating that the compilation was successful. ### **Step 5: Deploy the Smart Contract** 1. Navigate to the **Deploy & Run Transactions** tab (the fourth icon in the left sidebar). 2. Under **Environment**, select **Remix VM** 3. In the **Account** dropdown, copy the first address by clicking the icon next to it. 4. Paste the copied address into the **Deploy** input field. 5. Click the **Deploy** button. ### **Step 6: Copy the Smart Contract Address** 1. After deployment, scroll down to see the **Deployed Contracts** section. 2. You will find the generated smart contract address listed there. Copy this address for your records. ### **Alternative Method to Copy the Contract Address** 1. Alternatively, you can also copy the contract address from the **Transaction Receipt** that appears after deployment. 2. Look for the contract address in the receipt and copy it as well.