MANTRA Chain
  • Introduction
    • Overview
    • What is MANTRA Chain
    • Why MANTRA Chain
    • Building on MANTRA Chain
    • MANTRA RWA Suite
  • Using MANTRA Chain
    • MANTRA Chain Wallet Setup
      • Connect to Testnet
      • Connect to Mainnet
      • Testnet Faucet
    • MANTRA Zone
    • MANTRA Bridge
      • MANTRA Bridge User Guide
      • MANTRA Bridge FAQs
    • Stake
      • Stake User Guide
      • Stake FAQs
    • MANTRA Swap
      • MANTRA Swap User Guide
      • Liquidity Pools User Guide
      • MANTRA Swap FAQs
    • Claiming Journey
      • Check If You're Eligible
      • Claiming Journey User Guide
      • I've redeemed my rewards, now what?
    • Chakra Pool
      • What can I do with my USDY
      • Chakra Pool Deposit Guide
      • How KARMA works for Chakra Pool
        • How much KARMA do I get for depositing into Chakra Pool?
        • Can I make multiple deposits and how will KARMA be calculated then?
        • My KARMA is not updated even though I make multiple deposits?
        • Will I get daily KARMA for my deposit on USDY Chakra Pool launched on Hongbai earlier?
      • Chakra Pool FAQs
    • MANTRA Zone Leaderboard
      • MANTRA Zone Leaderboard User Guide
        • Mission Guides
          • Pledge your OM 🕉️
          • Claim OM Staking Rewards
          • OMly Drip 💧
      • MANTRA Zone Leaderboard FAQs
  • Developing on MANTRA Chain
    • Getting Started
      • DuKong Testnet
      • Install Prerequisites
      • Setting Up Dev Environment
      • Compiling a Contract
      • Deployment and Interaction
    • CosmWasm Quick Start Guide
      • CosmWasm Contracts
      • Understanding CosmWasm File Structure
    • DAPP Tooling
      • Cosmos SDK
      • Important Libraries
      • Developer Resources
    • Developer FAQs
  • MANTRA Smart Contracts
    • Overview
    • MANTRA Dex
      • 🚢Deployments
      • 📚Common Types
      • 💰Fee Collector
      • ⌛Epoch Manager
      • 🌊Pool Manager
      • 🎁Farm Manager
    • 🎊Claimdrop Contract V1
    • 🎁Claimdrop Contract V2
    • Audits
  • Node & Validator Operations
    • Overview
    • Node Setup & Deployment
      • Node System Requirements
      • Running a Node
      • How to run Mantrachain with Systemd
      • Validator Nodes
        • Connect sidecar
    • Validator Architecture Recommendations
      • Secure Validator
    • Governance
      • Draft a Proposal
        • Text Proposal
        • Community Spend Proposal
        • Software Upgrade
        • Cancel Software Upgrade
      • Submitting a Proposal
      • Verifying your transaction
      • Depositing funds
      • Voting on a Proposal
    • Download nodes snapshots
  • MANTRA Chain with EVM (Alpha)
    • Getting Started on OMSTEAD Testnet
    • Address Conversion
  • Setup Instructions
  • Using Foundry
    • Deployment with Foundry
  • Using Hardhat
    • Deployment with Hardhat
  • Mainnet OM Information
    • Background
    • Updated OM Tokenomics
    • OM Distribution Updates
    • Inflation & Vesting Schedule
    • Legacy tokens to mainnet staking tokens
  • Appendix
    • Frequently Asked Questions
      • General FAQs
        • What is MANTRA Chain?
        • What are the key features/ modules available to builders on MANTRA Chain?
        • What support is available for developers and validators that join the ecosystem
        • Where can i find MANTRA Social Accounts & Communities?
        • How can an Issue or a bug be reported?
        • How can I identify official MANTRA communication and websites?
      • Validator FAQs
        • What does a new validator’s journey look like?
        • What are my key responsibilities as a validator?
        • What is uptime and how is it maintained?
        • What are the hardware requirements?
        • How to get validator keys?
        • How can I connect a validator from an older testnet to the newest testnet?
        • How do I create a backup snapshot of a node?
        • How do I restore a node from a snapshot?
        • What are the different states a validator can be in?
        • How do I unjail a validator?
        • How do I edit a validator's description?
        • What is self-delegation?
        • Is there a minimum amount of OM that must be delegated to be an active validator?
      • Developer FAQs
        • What is MANTRA Chain, and how does it utilize the Cosmos ecosystem?
        • How does MANTRA Chain ensure interoperability with other blockchains in the Cosmos ecosystem?
        • What is the Cosmos ecosystem, and how does it differ from other blockchain platforms?
        • Which programming languages are commonly used for developing on the Cosmos ecosystem?
        • How do I get started with development on the Cosmos ecosystem?
        • What are Cosmos SDK and Tendermint, and how do they work together?
        • What are Cosmos zones and how do they interact?
        • How do I build and deploy smart contracts on the Cosmos ecosystem?
        • How do I interact with Cosmos chains and applications programmatically?
        • What is the best way to learn about developing for the MANTRA Chain and Cosmos ecosystem?
      • Hongbai FAQs
        • Where do I sign up for Hongbai?
        • Why do I need to provide 2 wallets during signup?
        • How can I Login?
        • What should I do if I forgot my password?
        • What can I do if i forget my registered email?
        • Where do i verify my email?
        • How to connect my Metamask wallet?
        • Is Hongbai app a testnet or live app?
        • Can I change my wallet ?
        • Error “invalid user credentials“
        • Why can i not Invest?
      • How to Guides
        • Install Keplr Wallet
        • Keplr and Ledger
        • Add DuKong Testnet to Keplr
        • Get Tokens from Faucet
        • Track activity on Leaderboard
      • More Help & Support
      • MANTRA Zone FAQs
        • MANTRA Zone General FAQs
          • What wallets are currently supported on the MANTRA Zone?
          • How do I connect my wallet to the MANTRA Zone?
          • How do I copy my connected wallet’s address?
          • How do I connect a different wallet to the MANTRA Zone?
          • Can I disconnect my wallet from MANTRA Zone?
          • How can I resolve seeing multiple entires of the same amount of testnet OM in my Keplr wallet?
          • The question that I have is not covered by the FAQ, what should I do?
        • MANTRA Bridge FAQs
          • What is MANTRA Bridge?
          • Why should I use the MANTRA Bridge?
          • Is there a fee for using the MANTRA Bridge?
          • Do I have to pay gas fees?
          • What tokens will I receive?
          • How does the migration process work?
          • How long should the MANTRA Bridge order take?
          • How can I track the status of my MANTRA Bridge order?
          • What happens to my ERC20 OM tokens once I use the MANTRA Bridge?
          • Which network can I use on the MANTRA Bridge?
          • What should I do if I see ‘Wrong network’ error on the app?
          • Will there be a two-way bridge in the future?
          • My MANTRA Bridge order is stuck on Ethereum?! How can I fix this?
          • Who can I contact for support during the migration process?
          • Which EVM chains can I bridge from?
          • Are the OM tokens I receive wrapped tokens or the native MANTRA Chain OM tokens?
          • Is the MANTRA Bridge a non-custodial solution?
          • What happens if the MANTRA Bridge is hacked or goes offline?
          • Are there any risks associated with using the MANTRA Bridge?
          • How does the MANTRA Bridge ensure the security of my tokens during migration?
          • Can I use my existing Ethereum wallet to access the MANTRA Bridge?
          • How does wallet linking and airdrop participation work?
        • Stake FAQs
          • What is Stake?
          • What are validators, and how do I choose one?
          • What rewards can I earn by staking $OM?
          • How are staking rewards calculated?
          • Can I choose to unstake my $OM coins?
          • What is the unbonding period?
          • Can I change validators after staking $OM?
          • Can I stake $OM with multiple validators?
          • Are there any risks involved with staking $OM?
          • Will my rewards gets automatically restaked after redemption?
          • Do I lose my claimable rewards when performing actions with a validator with unclaimed rewards?
          • My wallet is showing a 0 balance even though I have funds. What should I do?
          • How do I know if my $OM is staked?
        • Chakra Pool FAQs
          • What are the Chakra Pool rewards?
          • What is USDY?
          • How do I get the yield from the treasury bonds by holding USDY?
          • Can I change my linked wallet?
          • I don’t remember my linked wallet - what can I do?
          • What are the bonus rewards and where I can see my rewards?
          • How are the USDY rewards calculated and when can I redeem my USDY rewards?
          • How are the $OM rewards calculated and when can I redeem my $OM rewards?
          • How are the $ONDO rewards calculated and when can I redeem my $ONDO rewards?
          • What can I do with my USDY and $OM rewards after redemption?
        • MANTRA Zone Leaderboard FAQs
          • I have someone that wants to participate in the MANTRA Zone Leaderboard but they don’t know how.
          • What does the ⚡icon and ‘Get + x KARMA’ text under the ‘KARMA’ section refer to?
          • The KARMA on the Zone Leaderboard only lists MANTRA Chain wallets, how to check my EVM wallet rank?
          • Which wallet IDs are visible on the Leaderboard Ranking Table?
          • Why are there three different categories for the Leaderboard Ranking Table?
          • How can I get the Leaderboard Ranking Table to show where my connected wallet ranks currently?
          • How can I search where a specific wallet ID ranks in the Leaderboard Ranking Table?
          • How can I search what wallet ID currently ranks in a specific ranking number?
          • My KARMA and/or multiplier amount did not update on the Leaderboard Ranking Table, why is that?
          • What is the calculation for the KARMA?
          • I’m unable to see the OMly Faucet on my Keplr/Cosmostation wallet
          • I'm unable to see my OMly Faucet Claim transaction on the explorer
          • The question that I have is not covered by the FAQ, what should I do?
        • MANTRA Swap FAQs
          • What is Swap?
          • How can I swap?
          • I have connected my EVM wallet, but I’m still unable to swap. Why?
          • Where can I view my past transactions?
          • Why is my swap button disabled?
          • Why can’t I swap with a slippage more than 5%?
          • What is Exchange Rate?
          • How do I get OM tokens in my wallet?
          • How do I get other tokens in my wallet?
          • Will the swap fee be deducted from my sell or buy token?
          • Why can’t I see the balance of tokens in my wallet but on the Swap panel?
          • How do I add liquidity to a pool?
          • I am unable to provide liquidity to the pool.
          • While adding liquidity, why are the two tokens USD value not equal?
          • How do I withdraw my funds from Liquidity Pools?
          • Why can I not see my tokens for withdrawal?
          • Why can I not remove liquidity?
          • Why is my deposited value of the two tokens changing in my position?
          • Do liquidity pools have a bonding period?
          • What are LP tokens?
          • What can I do with my LP tokens?
          • Why can I not see any LP token balance?
          • I am connected with my MANTRA wallet, but still cannot view my positions?
          • I provided liquidity, but I can’t see my position. What should I do?
          • Why is my transaction not successful?
    • Glossary
    • MANTRA Blockchain Tech
    • MANTRA's Incentivised Testnet
      • Testnet Phase 1
      • Hongbai - Testnet Phase 2
  • Third Party Bridges
    • Base Bridge
    • Polygon Bridge
Powered by GitBook
On this page
  • Using the CLI
  • Rust Implementation
  • JavaScript Implementation
  • Python Implementation
  1. MANTRA Chain with EVM (Alpha)

Address Conversion

PreviousGetting Started on OMSTEAD TestnetNextSetup Instructions

Last updated 10 hours ago

MANTRA Chain uses the Bech32 address format, which contains a human-readable format for encoding addresses (addresses start with mantra1). This format is used to represent addresses in a way that is more user-friendly and less error-prone than traditional hexadecimal formats (addresses start with 0x). You can find Bech32 details in the . MANTRA Chain also supports EVM module, which is compatible with the Ethereum Virtual Machine (EVM) and uses the standard hexadecimal address format. With the same private key, you can derive both EVM address and Bech32 address with coin type 60.

In order to convert between a Bech32 format address and an EVM format address, we provide the following sample codes below:

Using the CLI

Download the latest binary from page

Basic command:

$ mantrachaind debug addr mantra1z3yty3yswenj4ngk0wg5qmqf25ssr3wfqayuhv

Belows are outputs of the above command:

Address: [20 72 178 68 144 118 103 42 205 22 123 145 64 108 9 85 33 1 197 201]
Address (hex): 1448B2449076672ACD167B91406C09552101C5C9
Bech32 Acc: mantra1z3yty3yswenj4ngk0wg5qmqf25ssr3wfqayuhv
Bech32 Val: mantravaloper1z3yty3yswenj4ngk0wg5qmqf25ssr3wfyx9jmf
Bech32 Con: mantravalcons1z3yty3yswenj4ngk0wg5qmqf25ssr3wfs4kwhg

Command with better output:

mantrachaind-v5 debug addr mantra1z3yty3yswenj4ngk0wg5qmqf25ssr3wfqayuhv | grep "Address (hex):" | awk '{print "0x"$3}

output:

0x1448B2449076672ACD167B91406C09552101C5C9

Rust Implementation

Check test cases for examples of how to use the functions.

/// Codes are referenced from https://github.com/bitcoinjs/bech32/blob/master/src/index.ts
use std::collections::HashMap;
use std::sync::LazyLock;

const MANTRA_ADDRESS_PREFIX: &str = "mantra";

/// These characters are carefully designed as part of the BIP173 specification to avoid common transcription errors.
/// Reference: https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
const ALPHABET: &str = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";

static ALPHABET_MAP: LazyLock<HashMap<char, u8>> = LazyLock::new(|| {
    let mut map: HashMap<char, u8> = HashMap::new();
    for (z, x) in ALPHABET.chars().enumerate() {
        map.insert(x, z as u8);
    }
    map
});

/// Convert EVM address to MANTRA address.
/// References:
/// - https://docs.cronos.org/for-dapp-developers/chain-integration/adress-conversion
/// - https://en.bitcoin.it/wiki/BIP_0173
///
/// # Arguments
/// * `evm_address` - The EVM address as a hex string (with or without 0x prefix)
///
/// # Returns
/// * `Result<String, Box<dyn std::error::Error>>` - The MANTRA address or an error
pub fn convert_evm_address_to_mantra_address(
    evm_address: &str,
) -> Result<String, Box<dyn std::error::Error>> {
    // Remove "0x" prefix if present
    let hex_str = evm_address.trim_start_matches("0x").to_ascii_lowercase();

    // Convert hex string to bytes
    let evm_address_bytes = hex::decode(hex_str)?;

    // Convert bits from 8 to 5
    let converted_bits = convert_bits(&evm_address_bytes, 8, 5, true)?;

    // Encode using bech32
    let mantra_address = bech32_encoder(MANTRA_ADDRESS_PREFIX, &converted_bits, None)?;

    Ok(mantra_address)
}

#[allow(dead_code)]
pub fn convert_mantra_address_to_eth_address(
    mantra_address: &str,
) -> Result<String, Box<dyn std::error::Error>> {
    let decoded: Decoded = bech32_decoder(mantra_address, None)?;
    let hex_bytes = convert_bits(&decoded.words, 5, 8, false)?;
    Ok(format!("0x{}", hex::encode(hex_bytes)))
}

/// General power-of-2 base conversion.
/// References:
/// - https://en.bitcoin.it/wiki/Bech32
/// - https://github.com/fiatjaf/bech32/blob/master/bech32/__init__.py
///
/// # Arguments
/// * `data` - Input data as a slice of bytes
/// * `from_bits` - Number of bits in source representation
/// * `to_bits` - Number of bits in target representation
/// * `pad` - Whether to pad a result if needed
///
/// # Returns
/// * `Result<Vec<u8>, Box<dyn std::error::Error>>` - Converted data or error if conversion fails
pub fn convert_bits(
    data: &[u8],
    from_bits: u32,
    to_bits: u32,
    pad: bool,
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
    let mut acc = 0u32;
    let mut bits = 0u32;
    let mut ret = Vec::new();
    let maxv = (1 << to_bits) - 1;
    let max_acc = (1 << (from_bits + to_bits - 1)) - 1;

    for &value in data {
        let value = value as u32;
        if value >= (1 << from_bits) {
            return Err("Invalid data for base conversion: value out of range".into());
        }
        acc = (acc << from_bits | value) & max_acc;
        bits += from_bits;
        while bits >= to_bits {
            bits -= to_bits;
            ret.push(((acc >> bits) & maxv) as u8);
        }
    }

    if pad && bits > 0 {
        ret.push(((acc << (to_bits - bits)) & maxv) as u8);
    } else if !pad && (bits >= from_bits || (acc << (to_bits - bits)) & maxv != 0) {
        return Err("Invalid padding in base conversion".into());
    }

    Ok(ret)
}

fn bech32_encoder(prefix: &str, words: &[u8], limit: Option<usize>) -> Result<String, String> {
    let limit = limit.unwrap_or(90);

    if prefix.len() + 7 + words.len() > limit {
        return Err("Exceeds length limit".to_string());
    }

    let prefix = prefix.to_lowercase();

    // determine chk mod
    let mut chk = prefix_chk(&prefix).map_err(|e| e)?;

    let mut result = format!("{}1", prefix);

    for &x in words {
        if x >> 5 != 0 {
            return Err("Non-5-bit word".to_string());
        }

        chk = polymod_step(chk) ^ (x as u32);
        result.push(ALPHABET.chars().nth(x as usize).unwrap());
    }

    chk = (0..6).fold(chk, |chk, _| polymod_step(chk)) ^ 1;

    for i in 0..6 {
        let v = (chk >> ((5 - i) * 5)) & 0x1f;
        result.push(ALPHABET.chars().nth(v as usize).unwrap());
    }

    Ok(result)
}

#[derive(Debug)]
#[allow(dead_code)]
pub struct Decoded {
    pub prefix: String,
    pub words: Vec<u8>,
}

#[allow(dead_code)]
pub fn bech32_decoder(str_input: &str, limit: Option<usize>) -> Result<Decoded, String> {
    let limit = limit.unwrap_or(90);

    if str_input.len() < 8 {
        return Err(format!("{} too short", str_input));
    }

    if str_input.len() > limit {
        return Err("Exceeds length limit".to_string());
    }

    // Don't allow mixed case
    let lowered = str_input.to_lowercase();
    let uppered = str_input.to_uppercase();

    if str_input != lowered && str_input != uppered {
        return Err(format!("Mixed-case string {}", str_input));
    }

    let str_normalized = &lowered;

    let split = match str_normalized.rfind('1') {
        Some(pos) => pos,
        None => return Err(format!("No separator character for {}", str_normalized)),
    };

    if split == 0 {
        return Err(format!("Missing prefix for {}", str_normalized));
    }

    let prefix = str_normalized[..split].to_string();
    let word_chars = &str_normalized[split + 1..];

    if word_chars.len() < 6 {
        return Err("Data too short".to_string());
    }

    let mut chk = match prefix_chk(&prefix) {
        Ok(checksum) => checksum,
        Err(error) => return Err(error),
    };

    let mut words = Vec::new();

    for (i, c) in word_chars.chars().enumerate() {
        let v = match ALPHABET_MAP.get(&c) {
            Some(value) => *value,
            None => return Err(format!("Unknown character {}", c)),
        };

        chk = polymod_step(chk) ^ (v as u32);

        // Not in the checksum?
        if i + 6 >= word_chars.len() {
            continue;
        }
        words.push(v);
    }

    if chk != 1 {
        return Err(format!("Invalid checksum for {}", str_normalized));
    }

    Ok(Decoded { prefix, words })
}

fn polymod_step(pre: u32) -> u32 {
    let b = pre >> 25;
    ((pre & 0x1ffffff) << 5)
        ^ (if ((b >> 0) & 1) != 0 { 0x3b6a57b2 } else { 0 })
        ^ (if ((b >> 1) & 1) != 0 { 0x26508e6d } else { 0 })
        ^ (if ((b >> 2) & 1) != 0 { 0x1ea119fa } else { 0 })
        ^ (if ((b >> 3) & 1) != 0 { 0x3d4233dd } else { 0 })
        ^ (if ((b >> 4) & 1) != 0 { 0x2a1462b3 } else { 0 })
}

fn prefix_chk(prefix: &str) -> Result<u32, String> {
    let mut chk: u32 = 1;

    for &code in prefix.as_bytes() {
        if code < 33 || code > 126 {
            return Err(format!("Invalid prefix ({})", prefix));
        }
        chk = polymod_step(chk) ^ (code as u32 >> 5);
    }

    chk = polymod_step(chk);

    for &code in prefix.as_bytes() {
        chk = polymod_step(chk) ^ (code as u32 & 0x1f);
    }

    Ok(chk)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_convert_evm_address_to_mantra_address() {
        let evm_address = "0x1448b2449076672aCD167b91406c09552101C5C9";
        let mantra_address = "mantra1z3yty3yswenj4ngk0wg5qmqf25ssr3wfqayuhv";
        let result = convert_evm_address_to_mantra_address(evm_address);
        assert!(result.is_ok());

        let converted_mantra_address = result.unwrap();
        assert!(mantra_address.starts_with("mantra"));
        assert!(mantra_address.len() > 6); // More than just the prefix
        assert_eq!(converted_mantra_address.as_str(), mantra_address);
    }

    #[test]
    fn test_convert_evm_address_without_0x_prefix() {
        let evm_address = "FaAEcfd80e8c8572bA96657c84eCc9003Ed1b27B";
        let mantra_address = "mantra1l2hvlkqw3jzh9w5kv47gfmxfqqldrvnmkun5lg";
        let result = convert_evm_address_to_mantra_address(evm_address);
        assert!(result.is_ok());

        let converted_mantra_address = result.unwrap();
        assert!(mantra_address.starts_with("mantra"));
        assert!(mantra_address.len() > 6); // More than just the prefix
        assert_eq!(converted_mantra_address.as_str(), mantra_address);
    }

    #[test]
    fn test_convert_bits() {
        let data = vec![0xFF, 0xFF];
        let result = convert_bits(&data, 8, 5, true);
        assert!(result.is_ok());
    }

    #[test]
    fn test_convert_bits_invalid_data() {
        let data = vec![0xFF]; // 8 bits but trying to convert from 4 bits
        let result = convert_bits(&data, 4, 8, true);
        assert!(result.is_err());
    }

    #[test]
    fn test_convert_mantra_address_to_eth_address() {
        let mantra_address = "mantra1z3yty3yswenj4ngk0wg5qmqf25ssr3wfqayuhv";
        let expected_eth_address = "0x1448b2449076672acd167b91406c09552101c5c9";
        let result = convert_mantra_address_to_eth_address(mantra_address);
        assert!(result.is_ok());
        let converted_eth_address = result.unwrap();
        assert_eq!(converted_eth_address.to_lowercase(), expected_eth_address);
    }
}

JavaScript Implementation

"use strict";

import {bech32} from "bech32";

const mantraAddressPrefix = 'mantra';

/**
 * Convert EVM address to MANTRA address.
 * References:
 * - https://docs.cronos.org/for-dapp-developers/chain-integration/adress-conversion
 * - https://en.bitcoin.it/wiki/BIP_0173
 * @param {string} evmAddress
 * @returns {string}
 */
export const convertEVMAddressToMantraAddress = (evmAddress) => {
  const evmAddressBuffer = Buffer.from(evmAddress.slice(2), 'hex');
  const evmAddressBytes = Array.from(evmAddressBuffer);
  const bz = convertBits(evmAddressBytes, 8, 5);
  return bech32.encode(mantraAddressPrefix, bz);
}

export const convertMantraAddressToEVMAddress = (mantraAddress) => {
  const decoded = bech32.decode(mantraAddress);
  const hexBytes = convertBits(decoded.words, 5, 8, false);
  return `0x${Buffer.from(hexBytes).toString('hex')}`;
}

/**
 * General power-of-2 base conversion.
 * References:
 * - https://en.bitcoin.it/wiki/Bech32
 * - https://github.com/fiatjaf/bech32/blob/master/bech32/__init__.py
 * @param {Array<number>} data - Input data as an array of integers
 * @param {number} fromBits - Number of bits in source representation
 * @param {number} toBits - Number of bits in target representation
 * @param {boolean} pad - Whether to pad a result if needed
 * @returns {Array<number>|null} - Converted data or null if conversion fails
 */
export const convertBits = (data, fromBits, toBits, pad = true) => {
  let acc = 0;
  let bits = 0;
  const ret = [];
  const maxv = (1 << toBits) - 1;
  const max_acc = (1 << (fromBits + toBits - 1)) - 1;

  for (const value of data) {
    if (value < 0 || (value >> fromBits)) {
      return null;
    }
    acc = ((acc << fromBits) | value) & max_acc;
    bits += fromBits;
    while (bits >= toBits) {
      bits -= toBits;
      ret.push((acc >> bits) & maxv);
    }
  }

  if (pad) {
    if (bits > 0) {
      ret.push((acc << (toBits - bits)) & maxv);
    }
  } else if (bits >= fromBits || ((acc << (toBits - bits)) & maxv)) {
    return null;
  }

  return ret;
}

Test cases

import { expect } from 'chai';
import { convertEVMAddressToMantraAddress, convertMantraAddressToEVMAddress } from "<YOUR_PATH>/<TO_THE_FUNCTIONS>.js";

describe('Address Conversion', () => {
  it('should be able to convert EVM address to MANTRA address', () => {
    const evmAddress = '0x1448b2449076672aCD167b91406c09552101C5C9';
    const expectedMantraAddress = 'mantra1z3yty3yswenj4ngk0wg5qmqf25ssr3wfqayuhv';
    const convertedAddress = convertEVMAddressToMantraAddress(evmAddress);
    expect(convertedAddress.toLowerCase()).to.equal(expectedMantraAddress.toLowerCase());
  });

  it('should be able to convert MANTRA address to EVM address', () => {
    const mantraAddress = 'mantra1z3yty3yswenj4ngk0wg5qmqf25ssr3wfqayuhv';
    const expectedEVMAddress = '0x1448b2449076672aCD167b91406c09552101C5C9';
    const convertedAddress = convertMantraAddressToEVMAddress(mantraAddress);
    expect(convertedAddress.toLowerCase()).to.equal(expectedEVMAddress.toLowerCase());
  })
});

Python Implementation

From mantra1 to 0x

import bech32

bech32_address = "mantra1z3yty3yswenj4ngk0wg5qmqf25ssr3wfqayuhv"

_, bz = bech32.bech32_decode(bech32_address)
hexbytes=bytes(bech32.convertbits(bz, 5, 8))
eth_address = '0x' + hexbytes.hex()
print(eth_address)

Vice versa, from 0x to mantra1

import bech32

eth_address = "0x1448b2449076672acd167b91406c09552101c5c9"
eth_address_bytes = bytes.fromhex(eth_address[2:])

bz = bech32.convertbits(eth_address_bytes, 8, 5)
bech32_address = bech32.bech32_encode("crc",bz)
print(bech32_address)
BIP-0173
MANTRA Chain Releases