1. Home
  2. Docs
  3. Nexi Docs
  4. Key functions
  5. Contracts execution

Contracts execution

To enhance accessibility and usability for developers working with smart contracts on the Nexi chain, let’s add some key functions and their code snippets that will improve contract interaction, visibility, and maintainability. Below are important functions and patterns you can include in your contracts to streamline developer access and interaction:

1. Safe Ownership Management

Implement ownership control to secure access to administrative functions:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

contract MyToken is ERC20, Ownable {
constructor() ERC20("MyToken", "MTK") {
_mint(msg.sender, 1000000 * 10 ** decimals());
}

// Function to mint new tokens, only accessible by the owner
function mint(address to, uint256 amount) external onlyOwner {
_mint(to, amount);
}

// Function to burn tokens from the owner's balance
function burn(uint256 amount) external onlyOwner {
_burn(msg.sender, amount);
}
}

2. Pause Contract Operations

Adding pausing functionality allows freezing contract operations in emergencies:

import "@openzeppelin/contracts/security/Pausable.sol";

contract MyToken is ERC20, Ownable, Pausable {
constructor() ERC20("MyToken", "MTK") {
_mint(msg.sender, 1000000 * 10 ** decimals());
}

// Override transfer functions to check for pause state
function transfer(address recipient, uint256 amount) public override whenNotPaused returns (bool) {
return super.transfer(recipient, amount);
}

function transferFrom(address sender, address recipient, uint256 amount) public override whenNotPaused returns (bool) {
return super.transferFrom(sender, recipient, amount);
}

// Functions to pause and unpause the contract
function pause() external onlyOwner {
_pause();
}

function unpause() external onlyOwner {
_unpause();
}
}

3. Event Logging for Better Transparency

Events are crucial for tracking contract interactions. Here’s an example of adding events:

// Define events to log critical actions
event TokensMinted(address indexed to, uint256 amount);
event TokensBurned(address indexed from, uint256 amount);

// Add emit statements in functions
function mint(address to, uint256 amount) external onlyOwner {
_mint(to, amount);
emit TokensMinted(to, amount);
}

function burn(uint256 amount) external onlyOwner {
_burn(msg.sender, amount);
emit TokensBurned(msg.sender, amount);
}

4. Access Control for Multiple Roles

Instead of a single owner, AccessControl allows defining multiple roles with specific permissions:

import "@openzeppelin/contracts/access/AccessControl.sol";

contract MyToken is ERC20, AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");

constructor() ERC20("MyToken", "MTK") {
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_setupRole(MINTER_ROLE, msg.sender);
_setupRole(BURNER_ROLE, msg.sender);
}

function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) {
_mint(to, amount);
}

function burn(uint256 amount) external onlyRole(BURNER_ROLE) {
_burn(msg.sender, amount);
}
}

5. Utility Functions for Enhanced Interaction

Adding utility functions can simplify interaction for developers and users:

// Function to check the total supply
function getTotalSupply() external view returns (uint256) {
return totalSupply();
}

// Function to check the token balance of an address
function getBalance(address account) external view returns (uint256) {
return balanceOf(account);
}

// Function to allow batch transfers (multiple recipients)
function batchTransfer(address[] calldata recipients, uint256[] calldata amounts) external {
require(recipients.length == amounts.length, "Array lengths do not match");
for (uint256 i = 0; i < recipients.length; i++) {
transfer(recipients[i], amounts[i]);
}
}

6. Error Handling and Custom Errors

Custom errors can save gas and make the code cleaner:

// Define custom errors
error Unauthorized(address caller);
error InsufficientBalance(uint256 requested, uint256 available);

// Use errors in functions
function withdraw(uint256 amount) external {
if (msg.sender != owner()) revert Unauthorized(msg.sender);
if (amount > balanceOf(msg.sender)) revert InsufficientBalance(amount, balanceOf(msg.sender));
_transfer(msg.sender, address(this), amount);
}

These functions and patterns make your contracts more robust, manageable, and easier to interact with for developers, enhancing the overall development experience on the Nexi chain.