Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
7b94d43
eth: Introduce ChainSpec and structural distinction between EVM and T…
shamardy Apr 23, 2025
83049c1
eth: Refactor CoinProtocol for chain_id/network, update activation an…
shamardy Apr 23, 2025
af4867b
eth: Move chain_id into protocol_data, propagate ChainSpec everywhere
shamardy Apr 24, 2025
d26b0c6
eth/tron: add TRON address type and parsing
shamardy Apr 24, 2025
7b74fb6
style(tron): use alphabetical order for derive attributes
shamardy May 5, 2025
bfce81d
tron: impl TryFrom` and `AsRef` for `Address`
shamardy May 5, 2025
e9ba8d0
tron: rename `ADDRESS_HEX_PREFIX` to `ADDRESS_PREFIX`, add index safe…
shamardy May 5, 2025
3676b1f
tron: return error for unimplemented `TRX` address generation in `add…
shamardy May 5, 2025
b7806a7
tests: remove chain_id from higher level `eth_conf` in `test_classic_…
shamardy May 5, 2025
59deab6
tron: return error for unimplemented `TRX` address generation in `ord…
shamardy May 5, 2025
bf2dd93
tron: return explicit unsupported Tron chain error in Metamask activa…
shamardy May 5, 2025
fbaca78
tron: remove `check if 1inch supports tron` todos
shamardy May 5, 2025
f904359
tron: optimize address parsing and improve error message clarity
shamardy May 6, 2025
2b44429
eth: take `chain_id` from protocol config for v1, remove redundant to…
shamardy May 9, 2025
7b02f35
tests: add ETH platform config to MM_CTX for compatibility with proto…
shamardy May 9, 2025
7e61403
tests: increase `WASM_BINDGEN_TEST_TIMEOUT` by 2 minutes
shamardy May 9, 2025
6ce97d2
chore: move tron mod declaration to its right place
shamardy May 9, 2025
e8fd60a
increase `WASM_BINDGEN_TEST_TIMEOUT` more
shamardy May 9, 2025
33f6996
tests: increase `wait_check_stats_swap_status` timeout in all tests
shamardy May 9, 2025
dec3d1d
tests: increase `wait_check_stats_swap_status` timeout again
shamardy May 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions mm2src/coins/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ doctest = false
async-std = { version = "1.5", features = ["unstable"] }
async-trait = "0.1.52"
base64 = "0.21.2"
# Todo: remove this and rely on bs58 throught the whole codebase
Comment thread
borngraced marked this conversation as resolved.
base58 = "0.2.0"
bip32 = { version = "0.2.2", default-features = false, features = ["alloc", "secp256k1-ffi"] }
bitcoin_hashes = "0.11"
bitcrypto = { path = "../mm2_bitcoin/crypto" }
blake2b_simd = { version = "0.5.10", optional = true }
bs58 = "0.4.0"
byteorder = "1.3"
bytes = "0.4"
cfg-if = "1.0"
Expand Down
164 changes: 0 additions & 164 deletions mm2src/coins/coins_tests.rs

This file was deleted.

87 changes: 76 additions & 11 deletions mm2src/coins/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ use fee_estimation::eip1559::{block_native::BlocknativeGasApiCaller, infura::Inf

pub mod erc20;
use erc20::get_token_decimals;

pub mod tron;

Comment thread
borngraced marked this conversation as resolved.
Outdated
pub(crate) mod eth_swap_v2;
use eth_swap_v2::{extract_id_from_tx_data, EthPaymentType, PaymentMethod, SpendTxSearchParams};

Expand Down Expand Up @@ -762,6 +765,30 @@ struct SavedErc20Events {
latest_block: U64,
}

/// Specifies which blockchain the EthCoin operates on: EVM-compatible or TRON.
/// This distinction allows unified logic for EVM & TRON coins.
#[derive(Clone, Debug)]
pub enum ChainSpec {
Evm { chain_id: u64 },
Tron { network: tron::Network },
Comment thread
borngraced marked this conversation as resolved.
}

impl ChainSpec {
pub fn chain_id(&self) -> Option<u64> {
match self {
ChainSpec::Evm { chain_id } => Some(*chain_id),
ChainSpec::Tron { .. } => None,
}
}

pub fn kind(&self) -> &'static str {
match self {
ChainSpec::Evm { .. } => "EVM",
ChainSpec::Tron { .. } => "TRON",
}
}
}
Comment thread
borngraced marked this conversation as resolved.

Comment thread
borngraced marked this conversation as resolved.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum EthCoinType {
/// Ethereum itself or it's forks: ETC/others
Expand Down Expand Up @@ -816,6 +843,8 @@ impl From<PrivKeyBuildPolicy> for EthPrivKeyBuildPolicy {
pub struct EthCoinImpl {
ticker: String,
pub coin_type: EthCoinType,
/// Specifies the underlying blockchain (EVM or TRON).
pub chain_spec: ChainSpec,
Comment on lines +845 to +846

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitty nit: feels like this field could be integrated into coin_type?

EthCoinType::Eth { chain_id: u64 }
EthCoinType::TRX { network }
and later ...
EthCoinType::TRC20 { network, token_address }

@shamardy shamardy Apr 27, 2025

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the first solution I considered ofcourse, I opted for chain_spec since I think this will require the least amount of code changes, it will be more clear in subsequent stages where I might refactor this if needed, now to the explanation on why I opted for chain_spec.

What is different between EVMs and Tron are actually the chain specs not coin types, so Tron platform coin and it's tokens have the same characteristics as EVM coins and tokens. If we were to use your suggested coin_type, swaps code will need more changing than with having ChainSpec, just take a look at all the matching for EthCoinType in eth.rs file, here is an example function https://github.com/KomodoPlatform/komodo-defi-framework/blob/af4867b7086ac716bd6c49107dbc0994e07208ca/mm2src/coins/eth.rs#L3824 since etmoic swap contracts work with Tron without any changes (I tested them, the spend function needs more checks but I think it works, all others are tested completely), this function will require no changes if we abstracted approve, allowance,wait_for_required_allowance and sign_and_send_transaction over both chain types. Actually only sign_and_send_transaction will require changes and the other 3 will probably just use the Tron RPC instead so they will use an abstraction over both RPC types.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aha i see ur point. if ERC20 is so close to TRC20 to the point that we can replace the latter with the former, then we only have one discriminator which is the chain-spec. 👍

(p.s. same for Eth & TRX)

@shamardy shamardy Apr 29, 2025

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After this PR I will look into if just adding EthCoinType::TRX { network } is better and using EthCoinType::ERC20 with both platform coin types. So the abstraction becomes over the platform coin like how lightning is implemented, but I still think chain_spec is better. All will be revealed while working on web3 RPC and signing methods.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unresolved it, as we sometimes come back to this thread to re-read

pub(crate) priv_key_policy: EthPrivKeyPolicy,
/// Either an Iguana address or a 'EthHDWallet' instance.
/// Arc is used to use the same hd wallet from platform coin if we need to.
Expand All @@ -836,7 +865,6 @@ pub struct EthCoinImpl {
/// Coin needs access to the context in order to reuse the logging and shutdown facilities.
/// Using a weak reference by default in order to avoid circular references and leaks.
pub ctx: MmWeak,
chain_id: u64,
/// The name of the coin with which Trezor wallet associates this asset.
trezor_coin: Option<String>,
/// the block range used for eth_getLogs
Expand Down Expand Up @@ -1043,7 +1071,7 @@ impl EthCoinImpl {
}

#[inline(always)]
pub fn chain_id(&self) -> u64 { self.chain_id }
pub fn chain_id(&self) -> Option<u64> { self.chain_spec.chain_id() }
}

async fn get_raw_transaction_impl(coin: EthCoin, req: RawTransactionRequest) -> RawTransactionResult {
Expand Down Expand Up @@ -1161,7 +1189,16 @@ pub async fn withdraw_erc1155(ctx: MmArc, withdraw_type: WithdrawErc1155) -> Wit
.build()
.map_to_mm(|e| WithdrawError::InternalError(e.to_string()))?;
let secret = eth_coin.priv_key_policy.activated_key_or_err()?.secret();
let signed = tx.sign(secret, Some(eth_coin.chain_id))?;
let chain_id = match eth_coin.chain_spec {
ChainSpec::Evm { chain_id } => chain_id,
// Todo: Add support for Tron NFTs
ChainSpec::Tron { .. } => {
return MmError::err(WithdrawError::InternalError(
"Tron is not supported for withdraw_erc1155 yet".to_owned(),
))
},
};
let signed = tx.sign(secret, Some(chain_id))?;
let signed_bytes = rlp::encode(&signed);
let fee_details = EthTxFeeDetails::new(gas, pay_for_gas_option, fee_coin)?;

Expand Down Expand Up @@ -1252,7 +1289,16 @@ pub async fn withdraw_erc721(ctx: MmArc, withdraw_type: WithdrawErc721) -> Withd
.build()
.map_to_mm(|e| WithdrawError::InternalError(e.to_string()))?;
let secret = eth_coin.priv_key_policy.activated_key_or_err()?.secret();
let signed = tx.sign(secret, Some(eth_coin.chain_id))?;
let chain_id = match eth_coin.chain_spec {
ChainSpec::Evm { chain_id } => chain_id,
// Todo: Add support for Tron NFTs
ChainSpec::Tron { .. } => {
return MmError::err(WithdrawError::InternalError(
"Tron is not supported for withdraw_erc721 yet".to_owned(),
))
},
};
let signed = tx.sign(secret, Some(chain_id))?;
let signed_bytes = rlp::encode(&signed);
let fee_details = EthTxFeeDetails::new(gas, pay_for_gas_option, fee_coin)?;

Expand Down Expand Up @@ -2639,9 +2685,18 @@ async fn sign_transaction_with_keypair<'a>(
let tx_builder = tx_builder_with_pay_for_gas_option(coin, tx_builder, pay_for_gas_option)
.map_err(|e| TransactionErr::Plain(e.get_inner().to_string()))?;
let tx = tx_builder.build()?;
let chain_id = match coin.chain_spec {
ChainSpec::Evm { chain_id } => chain_id,
// Todo: Add Tron signing logic
ChainSpec::Tron { .. } => {
return Err(TransactionErr::Plain(
"Tron is not supported for sign_transaction_with_keypair yet".into(),
))
},
};

Ok((
tx.sign(key_pair.secret(), Some(coin.chain_id))?,
tx.sign(key_pair.secret(), Some(chain_id))?,
web3_instances_with_latest_nonce,
))
}
Expand Down Expand Up @@ -6403,7 +6458,7 @@ pub async fn eth_coin_from_conf_and_request(
}

let (coin_type, decimals) = match protocol {
CoinProtocol::ETH => (EthCoinType::Eth, ETH_DECIMALS),
CoinProtocol::ETH { .. } => (EthCoinType::Eth, ETH_DECIMALS),
CoinProtocol::ERC20 {
platform,
contract_address,
Expand Down Expand Up @@ -6444,6 +6499,8 @@ pub async fn eth_coin_from_conf_and_request(

let sign_message_prefix: Option<String> = json::from_value(conf["sign_message_prefix"].clone()).unwrap_or(None);

// Todo: write documentation note about this
// Kept only for v1 activation but should be removed in time, needed for both ETH and ERC20
let chain_id = try_s!(conf["chain_id"]
.as_u64()
.ok_or_else(|| format!("chain_id is not set for {}", ticker)));
Expand Down Expand Up @@ -6480,6 +6537,8 @@ pub async fn eth_coin_from_conf_and_request(
priv_key_policy: key_pair,
derivation_method: Arc::new(derivation_method),
coin_type,
// Tron is not supported for v1 activation
chain_spec: ChainSpec::Evm { chain_id },
sign_message_prefix,
swap_contract_address,
swap_v2_contracts: None,
Expand All @@ -6493,7 +6552,6 @@ pub async fn eth_coin_from_conf_and_request(
max_eth_tx_type,
ctx: ctx.weak(),
required_confirmations,
chain_id,
trezor_coin,
logs_block_range: conf["logs_block_range"].as_u64().unwrap_or(DEFAULT_LOGS_BLOCK_RANGE),
address_nonce_locks,
Expand Down Expand Up @@ -6782,6 +6840,8 @@ fn calc_total_fee(gas: U256, pay_for_gas_option: &PayForGasOption) -> NumConvers
}
}

// Todo: Tron have a different concept from gas (Energy, Bandwidth and Free Transaction), it should be added as a different function
// and this should be part of a trait abstracted over both types
#[allow(clippy::result_large_err)]
fn tx_builder_with_pay_for_gas_option(
eth_coin: &EthCoin,
Expand All @@ -6793,9 +6853,14 @@ fn tx_builder_with_pay_for_gas_option(
PayForGasOption::Eip1559(Eip1559FeePerGas {
max_priority_fee_per_gas,
max_fee_per_gas,
}) => tx_builder
.with_priority_fee_per_gas(max_fee_per_gas, max_priority_fee_per_gas)
.with_chain_id(eth_coin.chain_id),
}) => {
let chain_id = eth_coin
.chain_id()
.ok_or_else(|| WithdrawError::InternalError("chain_id should be set for an EVM coin".to_string()))?;
tx_builder
.with_priority_fee_per_gas(max_fee_per_gas, max_priority_fee_per_gas)
.with_chain_id(chain_id)
},
};
Ok(tx_builder)
}
Expand Down Expand Up @@ -7299,6 +7364,7 @@ impl EthCoin {
let coin = EthCoinImpl {
ticker: self.ticker.clone(),
coin_type: new_coin_type,
chain_spec: self.chain_spec.clone(),
priv_key_policy: self.priv_key_policy.clone(),
derivation_method: Arc::clone(&self.derivation_method),
sign_message_prefix: self.sign_message_prefix.clone(),
Expand All @@ -7315,7 +7381,6 @@ impl EthCoin {
swap_txfee_policy: Mutex::new(self.swap_txfee_policy.lock().unwrap().clone()),
max_eth_tx_type: self.max_eth_tx_type,
ctx: self.ctx.clone(),
chain_id: self.chain_id,
trezor_coin: self.trezor_coin.clone(),
logs_block_range: self.logs_block_range,
address_nonce_locks: Arc::clone(&self.address_nonce_locks),
Expand Down
Loading