| 
 | 1 | +// Copyright (C) Use Ink (UK) Ltd.  | 
 | 2 | +//  | 
 | 3 | +// Licensed under the Apache License, Version 2.0 (the "License");  | 
 | 4 | +// you may not use this file except in compliance with the License.  | 
 | 5 | +// You may obtain a copy of the License at  | 
 | 6 | +//  | 
 | 7 | +//     http://www.apache.org/licenses/LICENSE-2.0  | 
 | 8 | +//  | 
 | 9 | +// Unless required by applicable law or agreed to in writing, software  | 
 | 10 | +// distributed under the License is distributed on an "AS IS" BASIS,  | 
 | 11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  | 
 | 12 | +// See the License for the specific language governing permissions and  | 
 | 13 | +// limitations under the License.  | 
 | 14 | + | 
 | 15 | +//! ERC-20 precompile interface for pallet-assets.  | 
 | 16 | +//!  | 
 | 17 | +//! This module provides the standard ERC-20 token interface for interacting with  | 
 | 18 | +//! assets managed by `pallet-assets` through the precompile mechanism.  | 
 | 19 | +//!  | 
 | 20 | +//! # Overview  | 
 | 21 | +//!  | 
 | 22 | +//! The ERC-20 Assets precompile allows smart contracts to interact with fungible assets  | 
 | 23 | +//! from `pallet-assets` using the ERC-20 interface. Each asset gets its own  | 
 | 24 | +//! precompile address, calculated by encoding the asset ID in the address.  | 
 | 25 | +//!  | 
 | 26 | +//! # Precompile Address  | 
 | 27 | +//!  | 
 | 28 | +//! - **Index**: `0x0120`  | 
 | 29 | +//! - **Address Format**: `[asset_id][...zeros...][0x0120]0000`  | 
 | 30 | +//!  | 
 | 31 | +//! Use [`crate::prefixed_address`] to calculate the correct address for a specific asset.  | 
 | 32 | +//!  | 
 | 33 | +//! # Example  | 
 | 34 | +//!  | 
 | 35 | +//! ```rust,ignore  | 
 | 36 | +//! use ink_precompiles::{erc20::{Erc20Ref, PRECOMPILE_INDEX}, prefixed_address};  | 
 | 37 | +//!  | 
 | 38 | +//! #[ink::contract]  | 
 | 39 | +//! mod my_contract {  | 
 | 40 | +//!     use super::*;  | 
 | 41 | +//!  | 
 | 42 | +//!     #[ink(storage)]  | 
 | 43 | +//!     pub struct MyContract {  | 
 | 44 | +//!         asset_id: u32,  | 
 | 45 | +//!     }  | 
 | 46 | +//!  | 
 | 47 | +//!     impl MyContract {  | 
 | 48 | +//!         #[ink(constructor)]  | 
 | 49 | +//!         pub fn new(asset_id: u32) -> Self {  | 
 | 50 | +//!             Self { asset_id }  | 
 | 51 | +//!         }  | 
 | 52 | +//!  | 
 | 53 | +//!         pub fn get_balance(&self, account: ink::Address) -> ink::U256 {  | 
 | 54 | +//!             let precompile_addr = prefixed_address(PRECOMPILE_INDEX, self.asset_id);  | 
 | 55 | +//!             let erc20: Erc20Ref = precompile_addr.into();  | 
 | 56 | +//!             erc20.balanceOf(account)  | 
 | 57 | +//!         }  | 
 | 58 | +//!     }  | 
 | 59 | +//! }  | 
 | 60 | +//! ```  | 
 | 61 | +//!  | 
 | 62 | +//! # References  | 
 | 63 | +//!  | 
 | 64 | +//! - [Polkadot SDK Assets Precompile](https://github.com/paritytech/polkadot-sdk/blob/master/substrate/frame/assets/src/precompiles.rs)  | 
 | 65 | +//! - [ERC-20 Token Standard](https://eips.ethereum.org/EIPS/eip-20)  | 
 | 66 | +
  | 
 | 67 | +/// ERC-20 Assets precompile index.  | 
 | 68 | +pub const PRECOMPILE_INDEX: u16 = 0x0120;  | 
 | 69 | + | 
 | 70 | +/// Type alias for asset IDs.  | 
 | 71 | +pub type AssetId = u32;  | 
 | 72 | + | 
 | 73 | +/// Defines the ERC-20 interface of the Asset Hub precompile.  | 
 | 74 | +#[ink::contract_ref(abi = "sol")]  | 
 | 75 | +pub trait Erc20 {  | 
 | 76 | +    /// Returns the total supply of tokens.  | 
 | 77 | +    ///  | 
 | 78 | +    /// # Solidity Signature  | 
 | 79 | +    ///  | 
 | 80 | +    /// ```solidity  | 
 | 81 | +    /// function totalSupply() external view returns (uint256);  | 
 | 82 | +    /// ```  | 
 | 83 | +    #[ink(message)]  | 
 | 84 | +    #[allow(non_snake_case)]  | 
 | 85 | +    fn totalSupply(&self) -> ink::U256;  | 
 | 86 | + | 
 | 87 | +    /// Returns the balance of an account.  | 
 | 88 | +    ///  | 
 | 89 | +    /// # Arguments  | 
 | 90 | +    /// * `account` - The address to query the balance of  | 
 | 91 | +    ///  | 
 | 92 | +    /// # Solidity Signature  | 
 | 93 | +    ///  | 
 | 94 | +    /// ```solidity  | 
 | 95 | +    /// function balanceOf(address account) external view returns (uint256);  | 
 | 96 | +    /// ```  | 
 | 97 | +    #[ink(message)]  | 
 | 98 | +    #[allow(non_snake_case)]  | 
 | 99 | +    fn balanceOf(&self, account: ink::Address) -> ink::U256;  | 
 | 100 | + | 
 | 101 | +    /// Transfers tokens to another account.  | 
 | 102 | +    ///  | 
 | 103 | +    /// # Arguments  | 
 | 104 | +    /// * `to` - The recipient address  | 
 | 105 | +    /// * `value` - The amount of tokens to transfer  | 
 | 106 | +    ///  | 
 | 107 | +    /// # Returns  | 
 | 108 | +    ///  | 
 | 109 | +    /// Returns `true` if the transfer was successful.  | 
 | 110 | +    ///  | 
 | 111 | +    /// # Solidity Signature  | 
 | 112 | +    ///  | 
 | 113 | +    /// ```solidity  | 
 | 114 | +    /// function transfer(address to, uint256 value) external returns (bool);  | 
 | 115 | +    /// ```  | 
 | 116 | +    #[ink(message)]  | 
 | 117 | +    fn transfer(&mut self, to: ink::Address, value: ink::U256) -> bool;  | 
 | 118 | + | 
 | 119 | +    /// Returns the allowance for a spender on behalf of an owner.  | 
 | 120 | +    ///  | 
 | 121 | +    /// This shows how many tokens `spender` is allowed to spend on behalf of `owner`.  | 
 | 122 | +    ///  | 
 | 123 | +    /// # Arguments  | 
 | 124 | +    /// * `owner` - The token owner's address  | 
 | 125 | +    /// * `spender` - The spender's address  | 
 | 126 | +    ///  | 
 | 127 | +    /// # Solidity Signature  | 
 | 128 | +    ///  | 
 | 129 | +    /// ```solidity  | 
 | 130 | +    /// function allowance(address owner, address spender) external view returns (uint256);  | 
 | 131 | +    /// ```  | 
 | 132 | +    #[ink(message)]  | 
 | 133 | +    fn allowance(&self, owner: ink::Address, spender: ink::Address) -> ink::U256;  | 
 | 134 | + | 
 | 135 | +    /// Approves a spender to spend tokens on behalf of the caller.  | 
 | 136 | +    ///  | 
 | 137 | +    /// # Arguments  | 
 | 138 | +    /// * `spender` - The address authorized to spend tokens  | 
 | 139 | +    /// * `value` - The maximum amount the spender can spend  | 
 | 140 | +    ///  | 
 | 141 | +    /// # Returns  | 
 | 142 | +    ///  | 
 | 143 | +    /// Returns `true` if the approval was successful.  | 
 | 144 | +    ///  | 
 | 145 | +    /// # Solidity Signature  | 
 | 146 | +    ///  | 
 | 147 | +    /// ```solidity  | 
 | 148 | +    /// function approve(address spender, uint256 value) external returns (bool);  | 
 | 149 | +    /// ```  | 
 | 150 | +    #[ink(message)]  | 
 | 151 | +    fn approve(&mut self, spender: ink::Address, value: ink::U256) -> bool;  | 
 | 152 | + | 
 | 153 | +    /// Transfers tokens from one account to another using allowance.  | 
 | 154 | +    ///  | 
 | 155 | +    /// The caller must have sufficient allowance from the `from` account.  | 
 | 156 | +    ///  | 
 | 157 | +    /// # Arguments  | 
 | 158 | +    /// * `from` - The address to transfer tokens from  | 
 | 159 | +    /// * `to` - The recipient address  | 
 | 160 | +    /// * `value` - The amount of tokens to transfer  | 
 | 161 | +    ///  | 
 | 162 | +    /// # Returns  | 
 | 163 | +    ///  | 
 | 164 | +    /// Returns `true` if the transfer was successful.  | 
 | 165 | +    ///  | 
 | 166 | +    /// # Solidity Signature  | 
 | 167 | +    ///  | 
 | 168 | +    /// ```solidity  | 
 | 169 | +    /// function transferFrom(address from, address to, uint256 value) external returns (bool);  | 
 | 170 | +    /// ```  | 
 | 171 | +    #[ink(message)]  | 
 | 172 | +    #[allow(non_snake_case)]  | 
 | 173 | +    fn transferFrom(  | 
 | 174 | +        &mut self,  | 
 | 175 | +        from: ink::Address,  | 
 | 176 | +        to: ink::Address,  | 
 | 177 | +        value: ink::U256,  | 
 | 178 | +    ) -> bool;  | 
 | 179 | +}  | 
 | 180 | + | 
 | 181 | +/// Creates a new ERC-20 precompile reference for the given asset ID.  | 
 | 182 | +///  | 
 | 183 | +/// # Arguments  | 
 | 184 | +/// * `asset_id` - The ID of the asset to interact with  | 
 | 185 | +///  | 
 | 186 | +/// # Returns  | 
 | 187 | +///  | 
 | 188 | +/// Returns an `Erc20Ref` that can be used to call precompile methods.  | 
 | 189 | +///  | 
 | 190 | +/// # Example  | 
 | 191 | +///  | 
 | 192 | +/// ```rust,ignore  | 
 | 193 | +/// use ink_precompiles::erc20::erc20;  | 
 | 194 | +///  | 
 | 195 | +/// let asset_id = 1;  | 
 | 196 | +/// let erc20_ref = erc20(asset_id);  | 
 | 197 | +/// let balance = erc20_ref.balanceOf(account);  | 
 | 198 | +/// ```  | 
 | 199 | +pub fn erc20(asset_id: AssetId) -> Erc20Ref {  | 
 | 200 | +    let address = crate::prefixed_address(PRECOMPILE_INDEX, asset_id);  | 
 | 201 | +    address.into()  | 
 | 202 | +}  | 
 | 203 | + | 
 | 204 | +#[cfg(test)]  | 
 | 205 | +mod tests {  | 
 | 206 | +    use super::*;  | 
 | 207 | + | 
 | 208 | +    #[test]  | 
 | 209 | +    fn erc20_precompile_address_format() {  | 
 | 210 | +        // ERC20 Assets precompile for asset ID 1 should be at the correct address  | 
 | 211 | +        let expected = [  | 
 | 212 | +            0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  | 
 | 213 | +            0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00,  | 
 | 214 | +        ];  | 
 | 215 | + | 
 | 216 | +        let address = crate::prefixed_address(PRECOMPILE_INDEX, 1);  | 
 | 217 | +        let address_bytes: [u8; 20] = address.into();  | 
 | 218 | + | 
 | 219 | +        assert_eq!(address_bytes, expected);  | 
 | 220 | +    }  | 
 | 221 | + | 
 | 222 | +    #[test]  | 
 | 223 | +    fn erc20_precompile_address_for_multiple_assets() {  | 
 | 224 | +        // Test asset ID 42  | 
 | 225 | +        let address_42 = crate::prefixed_address(PRECOMPILE_INDEX, 42);  | 
 | 226 | +        let bytes_42: [u8; 20] = address_42.into();  | 
 | 227 | + | 
 | 228 | +        // First 4 bytes should be asset ID (42 = 0x0000002a)  | 
 | 229 | +        assert_eq!(&bytes_42[0..4], &[0x00, 0x00, 0x00, 0x2a]);  | 
 | 230 | + | 
 | 231 | +        // Bytes 16-19 should be precompile index (0x0120)  | 
 | 232 | +        assert_eq!(&bytes_42[16..20], &[0x01, 0x20, 0x00, 0x00]);  | 
 | 233 | +    }  | 
 | 234 | +}  | 
0 commit comments