Skip to content

Releases: devel0x/electronero

Electronero Network: Release "Opcode Origin" v0.1.0

30 Jun 20:12
10c6499

Choose a tag to compare

🚀 Electronero Network – Token Subsystem Release (XRC20) "Opcode Origin"

🔧 Overview

This release introduces the XRC20 Token Subsystem, enabling fungible tokens directly on the Electronero network — without compromising the core privacy model.

Built into the core protocol, this system provides:

  • ⚙️ On-chain token creation, minting, burning, and transfer
  • 🔐 Private sender/receiver identities via stealth addresses
  • 🔍 Public token balances and transaction history for auditability
  • 🧩 Seamless integration with simplewallet and new RPC endpoints

Electronero introduces a comprehensive XRC‑20 token system accessible from both the CLI and RPC. Deploy new tokens with token_create, pay a deployment fee to governance, and manage balances privately as each operation rides on standard ring‑signature transactions. Transfers apply a small governance fee and optional creator fee while commands like token_balance and token_allowance reveal holdings and approved spenders.

Creators may mint or burn supply, transfer ownership, and pause or unpause transfers. Accounts can be frozen with governance approval, and token_transfer_from enables delegated spending much like ERC‑20. History and discovery tools such as token_history, tokens_deployed, and my_tokens help track every asset. Tokens reside in <data-dir>/tokens.bin so wallets stay in sync after rescanning the chain. Together these features empower a rich token ecosystem right within Electronero.

✨ New Features

✅ Token Subsystem (XRC20)

  • Create and manage fungible tokens.
  • Supports token-level metadata: name, symbol, supply, decimals.
  • Protocol-level TOKEN_OPERATION transaction type.
  • All token state (balances, total supply, tx log) is stored on-chain.

🔐 Hybrid Privacy Model

  • Balances and transfers are publicly visible.
  • User identities remain private via stealth address mechanisms.
  • No RingCT/decoys for token operations — enabling lightweight auditability.

🔒 Address Model

  • Only base wallets (primary account, index 0) can create tokens.
  • Integrated addresses and subaddresses are fully supported as token holders.
  • All transfer and balance functions work with standard and subaddresses.

🖥️ RPC + CLI Enhancements

🎯 Core RPC Methods

  • token_transfer
  • token_approve
  • token_transfer_from
  • token_burn
  • token_mint
  • token_set_fee
  • token_lock_fee
  • token_pause / token_unpause
  • token_freeze / token_unfreeze
  • token_allowance
  • token_info
  • token_balance
  • token_history
  • token_history_addr
  • tokens_deployed
  • my_tokens
  • all_tokens

All token RPC calls support account_index (AINDEX) to select source subaddress. Defaults to base address if not provided.


🧪 Simplewallet Commands

token_create <name> <symbol> <supply> [creator_fee]
token_balance <token_address> [owner]
token_transfer <token_address> <to> <amount>
token_approve <token_address> <spender> <amount>
token_transfer_from <token_address> <from> <to> <amount>
token_burn <token_address> <amount>
token_mint <token_address> <amount>
token_set_fee <token_address> <creator_fee>
token_lock_fee <token_address>
token_pause <token_address>
token_unpause <token_address>
token_freeze <token_address> <account>
token_unfreeze <token_address> <account>
token_info <token_address>
token_allowance <token_address> <owner> <spender>
token_history <token_address>
token_history_addr <address>
all_tokens
tokens_deployed [address]
my_tokens [address]

🧪 Code Examples

The all_tokens command lists every token known to the wallet:

bool simple_wallet::all_tokens(const std::vector<std::string> &args) {
  if (!m_tokens_path.empty())
    m_tokens.load(m_tokens_path);
  std::vector<token_info> list;
  m_tokens.list_all(list);
  for (const auto &t : list)
    message_writer() << t.name << " (" << t.symbol << ") " << t.address;
  return true;
}

If your wallet ever falls out of sync you can rebuild the local token data with
rescan_token_operations <start_height> which replays every token transaction
from the given height.

void t_cryptonote_protocol_handler<t_core>::rescan_token_operations(uint64_t from_height) {
  m_tokens = token_store();
  auto& bc = m_core.get_blockchain_storage();
  uint64_t top = bc.get_current_blockchain_height();
  for (uint64_t h = from_height; h < top; ++h) {
    bc.for_all_transactions(h, [this, h](const cryptonote::transaction &tx) {
      process_token_tx(tx, h);
    });
  }
  if (!m_tokens_path.empty())
    m_tokens.save(m_tokens_path);
}

Token transactions embed opcode data in the transaction tx_extra field. The protocol extracts this information via process_token_tx, which calls parse_token_extra to decode the operation and parameters. Once verified, the handler updates the local token store and persists the result:

void t_cryptonote_protocol_handler<t_core>::process_token_tx(const cryptonote::transaction &tx,
                                                             uint64_t height) {
  std::vector<cryptonote::tx_extra_field> fields;
  if (!cryptonote::parse_tx_extra(tx.extra, fields))
    return;
  cryptonote::tx_extra_token_data tdata;
  if (!find_tx_extra_field_by_type(fields, tdata))
    return;
  token_op_type op;
  std::vector<std::string> parts;
  crypto::signature sig;
  bool has_sig;
  if (!parse_token_extra(tdata.data, op, parts, sig, has_sig))
    return;
  // ...verify signer and execute operation...
  if (op == token_op_type::transfer)
    m_tokens.transfer_by_address(parts[0], parts[1], parts[2], std::stoull(parts[3]));
  if (!m_tokens_path.empty())
    m_tokens.save(m_tokens_path);
}

Token metadata lives in <data-dir>/tokens.bin and is managed by the wallet's token_store class. The loader restores token balances and history:

bool token_store::load(const std::string &file) {
  std::ifstream ifs(file, std::ios::binary);
  if (!ifs) {
    MERROR("Failed to open token store " << file);
    return false;
  }
  try {
    boost::archive::binary_iarchive ia(ifs);
    token_store_data data;
    ia >> data;
    tokens = std::move(data.tokens);
    transfer_history = std::move(data.transfers);
    rebuild_indexes();
  } catch(const std::exception &e) {
    MERROR("Failed to load token store " << file << ": " << e.what());
    return false;
  }
  return true;
}

🧠 Design Notes

  • Subaddress-aware: all wallet and RPC operations support subaddress accounts.
  • Token IDs are 64-bit identifiers (token_id) mapped to human-readable names.
  • Built on top of LMDB, integrated with consensus and mempool validation layer.

⚠️ Known Limitations / Notes

  • Tokens do not use RingCT or decoy outputs.
  • Only the amount and token ID are visible — sender and recipient are concealed.
  • This is a semi-public system designed for hybrid use cases (compliance + privacy).
  • Only base wallet addresses can create tokens.

🧰 Developer Tips

  • Use get_subaddress_as_str({account, index}) to fetch subaddress strings.
  • Token creation and ownership is restricted to account index 0.
  • All token operations are broadcast through the P2P network and mined in blocks.

📅 What's Next

  • ✅ Launching on-chain marketplace for token distribution
  • 🧪 Semi-Fungible Tokens (SFT) with functionality similar to ERC-1155
  • 📜 Open-source release of token subsystem for third-party audit
  • 🧩 UI integrations
  • 🔐 zkProof + ring signature research for private token metadata (Phase 2)

Modules

Electronero ships language-specific RPC wrappers in the modules/ directory. modules/NodeJs.md shows using electronero.js to access wallet and daemon RPCs from Node.js. modules/PHP.md demonstrates a cURL-based PHP wrapper, while modules/go.md documents a typed Go client. The Python helpers in modules/python.md offer simple DaemonRPC and WalletRPC classes with usage examples.

Merged Pull Requests

Read more