Skip to content

midegdugarova/Assets

Repository files navigation

Pallet-Assets Precompile for Polkadot Asset Hub

This repository contains Solidity contract templates for interacting with the pallet-assets precompile on Polkadot Asset Hub (formerly Statemint).

Overview

The pallet-assets precompile allows Solidity smart contracts to interact with Polkadot - native assets on Polkadot Asset Hub through the Ethereum compatibility layer provided by pallet-revive.

How Precompile Addresses Work

In pallet-revive, each asset has its own unique precompile address derived from its Asset ID. This differs from traditional single-precompile approaches.

Address Format

The precompile address encodes the Asset ID in the first 4 bytes and includes a precompile index at bytes 17-18:

  • Format: 0x[AssetID as 8 hex chars][26 zeros][Precompile Index][2 zeros]
  • Formula: address = (assetId << 128) | (0x0120 << 16)
  • Code source: Polkadot SDK Assets Precompile

Address Structure (20 bytes):

  • Bytes 0-3: Asset ID (4 bytes)
  • Bytes 4-16: Zeros (13 bytes)
  • Bytes 17-18: Precompile index 0x0120 (2 bytes)
  • Byte 19: Zero (1 byte)

The precompile index 0x0120 is for TrustBackedAssets on Passet Hub.

Examples

Asset ID Precompile Address
1 0x0000000100000000000000000000000001200000
1000 0x000003e800000000000000000000000001200000
42000 0x0000a41000000000000000000000000001200000

The AssetsPrecompile library (included in IAssets.sol) handles address computation automatically:

uint256 assetId = 1000;
address precompileAddress = AssetsPrecompile.getAddress(assetId);
// Returns: 0x000003e800000000000000000000000001200000

Files

  • IAssets.sol - Standard ERC20 interface for pallet-assets precompiles and the AssetsPrecompile helper library
  • AssetManager.sol - Example implementation contract demonstrating common usage patterns

Features

IERC20Assets Interface

The interface provides a standard ERC20-compatible API for interacting with Substrate assets.

Currently Supported Functions (based on pallet-revive implementation):

View Functions

  • totalSupply() - Get total supply
  • balanceOf(account) - Get account balance
  • allowance(owner, spender) - Get allowance

State-Changing Functions

  • transfer(recipient, amount) - Transfer tokens
  • transferFrom(owner, recipient, amount) - Transfer from allowance
  • approve(spender, amount) - Approve spender

Note: Additional pallet-assets functions (like name, symbol, decimals, mint, burn, freeze, etc.) are not yet exposed through the pallet-revive precompile interface. Only the core ERC20 functions listed above are currently available.

AssetsPrecompile Library

Helper library for working with asset precompiles:

  • getAddress(assetId) - Compute the precompile address for an Asset ID
  • getAsset(assetId) - Get the IERC20Assets interface instance for an Asset ID

AssetManager Contract

Example contract demonstrating practical usage:

  • Query total supply and balances
  • Check allowances
  • Transfer assets
  • Approve and transferFrom patterns
  • Deposit/withdraw patterns for DeFi applications
  • Batch transfers
  • Helper functions for address computation

Usage

Basic Transfer Example

import "./IAssets.sol";

contract MyContract {
    using AssetsPrecompile for uint256;

    function transferTokens(uint256 assetId, address recipient, uint256 amount) external {
        IERC20Assets asset = assetId.getAsset();
        asset.transfer(recipient, amount);
    }
}

Direct Address Usage

import "./IAssets.sol";

contract MyContract {
    function transferByAddress(uint256 assetId, address recipient, uint256 amount) external {
        // Compute the precompile address
        address precompileAddress = AssetsPrecompile.getAddress(assetId);

        // Use the address directly
        IERC20Assets asset = IERC20Assets(precompileAddress);
        asset.transfer(recipient, amount);
    }
}

Querying Asset Info

import "./IAssets.sol";

contract MyContract {
    using AssetsPrecompile for uint256;

    function getAssetBalance(uint256 assetId, address account) external view returns (
        uint256 balance,
        uint256 totalSupply,
        uint256 allowance
    ) {
        IERC20Assets asset = assetId.getAsset();
        balance = asset.balanceOf(account);
        totalSupply = asset.totalSupply();
        allowance = asset.allowance(account, msg.sender);
    }
}

Approve and TransferFrom Pattern

import "./IAssets.sol";

contract MyContract {
    using AssetsPrecompile for uint256;

    // User approves contract
    function approveContract(uint256 assetId, uint256 amount) external {
        IERC20Assets asset = assetId.getAsset();
        asset.approve(address(this), amount);
    }

    // Contract transfers from user
    function depositFromUser(uint256 assetId, address user, uint256 amount) external {
        IERC20Assets asset = assetId.getAsset();
        asset.transferFrom(user, address(this), amount);
    }
}

Working with Multiple Assets

import "./IAssets.sol";

contract MultiAssetVault {
    using AssetsPrecompile for uint256;

    function depositMultiple(
        uint256[] calldata assetIds,
        uint256[] calldata amounts
    ) external {
        require(assetIds.length == amounts.length, "Length mismatch");

        for (uint256 i = 0; i < assetIds.length; i++) {
            IERC20Assets asset = assetIds[i].getAsset();
            asset.transferFrom(msg.sender, address(this), amounts[i]);
        }
    }
}

Deployment

This project is configured for deployment to Polkadot Asset Hub (Polkadot Hub) using Hardhat.

Prerequisites

  1. Node.js and npm: Version 22.5+ and npm 10.9.0+ are required
  2. Test tokens: Get PAS test tokens from Polkadot Faucet
  3. Private key: Have a wallet private key ready for deployment

Setup

  1. Install dependencies:

    npm install
  2. Set your private key securely:

    npx hardhat vars set PRIVATE_KEY "your_private_key_here"

    Note: Never commit your private key to version control. Hardhat vars stores it securely.

Compilation

Compile contracts for PolkaVM:

npm run compile
# or
npx hardhat compile

This compiles your Solidity contracts to PolkaVM bytecode using the @parity/hardhat-polkadot plugin.

Deployment to Polkadot Hub

Deploy to TestNet (Passet Hub)

npm run deploy:testnet
# or
npx hardhat ignition deploy ./ignition/modules/AssetManager.js --network polkadotHubTestnet

TestNet Details:

Important Notes

Asset IDs

  • Asset IDs are numeric identifiers (uint32) for assets created on the Substrate side
  • Each Asset ID maps to a unique precompile address
  • Asset IDs can be queried through the Substrate RPC or block explorer
  • The maximum Asset ID is 4,294,967,295 (uint32 max)

Precompile Address Computation

The precompile address for any asset can be computed as:

address = (assetId << 128) | (0x0120 << 16)

This shifts the Asset ID to the first 4 bytes of the 20-byte address and positions the precompile index 0x0120 at bytes 17-18 (by shifting it left 16 bits).

Permissions

Some functions require special permissions:

  • Owner - Can mint, freeze, transfer ownership, set team, set metadata
  • Admin - Can freeze/thaw accounts, force transfers
  • Issuer - Can mint and burn
  • Freezer - Can freeze and thaw accounts

Standard users can:

  • Transfer their own assets
  • Approve and use transferFrom
  • Burn their own assets
  • Query asset information

Minimum Balance

Assets on Polkadot have a minimum balance requirement (existential deposit). Ensure transfers maintain accounts above this threshold, or they will be reaped.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors