diff --git a/crates/sui-rosetta/src/operations.rs b/crates/sui-rosetta/src/operations.rs index fd1bfd1443223..ced23f58e402b 100644 --- a/crates/sui-rosetta/src/operations.rs +++ b/crates/sui-rosetta/src/operations.rs @@ -651,7 +651,9 @@ impl Operations { .ok_or_else(|| anyhow!("Response balance changes should not be empty."))? { if let Ok(currency) = cache.get_currency(&balance_change.coin_type).await { - balance_changes.push((balance_change.clone(), currency)); + if !currency.symbol.is_empty() { + balance_changes.push((balance_change.clone(), currency)); + } } } diff --git a/crates/sui-rosetta/tests/custom_coins/test_coin_no_symbol/Move.toml b/crates/sui-rosetta/tests/custom_coins/test_coin_no_symbol/Move.toml new file mode 100644 index 0000000000000..3f5e73de1f5c7 --- /dev/null +++ b/crates/sui-rosetta/tests/custom_coins/test_coin_no_symbol/Move.toml @@ -0,0 +1,9 @@ +[package] +name = "test_coin" +edition = "2024.beta" # edition = "legacy" to use legacy (pre-2024) Move + +[dependencies] +Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" } + +[addresses] +test_coin = "0x0" \ No newline at end of file diff --git a/crates/sui-rosetta/tests/custom_coins/test_coin_no_symbol/sources/test_coin.move b/crates/sui-rosetta/tests/custom_coins/test_coin_no_symbol/sources/test_coin.move new file mode 100644 index 0000000000000..715857a7245da --- /dev/null +++ b/crates/sui-rosetta/tests/custom_coins/test_coin_no_symbol/sources/test_coin.move @@ -0,0 +1,24 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +/// Module: test_coin +module test_coin::test_coin { + + use sui::coin; + + public struct TEST_COIN has drop {} + + fun init(witness: TEST_COIN, ctx: &mut TxContext) { + let (treasury, metadata) = coin::create_currency( + witness, + 6, + b"", + b"", + b"", + option::none(), + ctx + ); + transfer::public_freeze_object(metadata); + transfer::public_transfer(treasury, ctx.sender()) + } +} diff --git a/crates/sui-rosetta/tests/custom_coins/test_coin_utils.rs b/crates/sui-rosetta/tests/custom_coins/test_coin_utils.rs index c7e640b268fc1..13ba4aa714af6 100644 --- a/crates/sui-rosetta/tests/custom_coins/test_coin_utils.rs +++ b/crates/sui-rosetta/tests/custom_coins/test_coin_utils.rs @@ -131,8 +131,8 @@ pub async fn init_package( client: &SuiClient, keystore: &Keystore, sender: SuiAddress, + path: &Path, ) -> Result { - let path = Path::new("tests/custom_coins/test_coin"); let path_buf = base::reroot_path(Some(path))?; let move_build_config = MoveBuildConfig::default(); @@ -266,7 +266,14 @@ async fn test_mint() { let keystore = &test_cluster.wallet.config.keystore; let sender = test_cluster.get_address_0(); - let init_ret = init_package(&client, keystore, sender).await.unwrap(); + let init_ret = init_package( + &client, + keystore, + sender, + Path::new("tests/custom_coins/test_coin"), + ) + .await + .unwrap(); let address1 = test_cluster.get_address_1(); let address2 = test_cluster.get_address_2(); diff --git a/crates/sui-rosetta/tests/custom_coins_tests.rs b/crates/sui-rosetta/tests/custom_coins_tests.rs index 57f6233a69292..77094e35e9d7d 100644 --- a/crates/sui-rosetta/tests/custom_coins_tests.rs +++ b/crates/sui-rosetta/tests/custom_coins_tests.rs @@ -8,15 +8,16 @@ mod test_coin_utils; use serde_json::json; use std::num::NonZeroUsize; +use std::path::Path; use sui_json_rpc_types::{ SuiExecutionStatus, SuiTransactionBlockEffectsAPI, SuiTransactionBlockResponseOptions, }; use sui_rosetta::operations::Operations; -use sui_rosetta::types::Currencies; use sui_rosetta::types::{ AccountBalanceRequest, AccountBalanceResponse, AccountIdentifier, Currency, CurrencyMetadata, NetworkIdentifier, SuiEnv, }; +use sui_rosetta::types::{Currencies, OperationType}; use sui_rosetta::CoinMetadataCache; use sui_rosetta::SUI; use test_cluster::TestClusterBuilder; @@ -37,7 +38,14 @@ async fn test_custom_coin_balance() { let (rosetta_client, _handle) = start_rosetta_test_server(client.clone()).await; let sender = test_cluster.get_address_0(); - let init_ret = init_package(&client, keystore, sender).await.unwrap(); + let init_ret = init_package( + &client, + keystore, + sender, + Path::new("tests/custom_coins/test_coin"), + ) + .await + .unwrap(); let address1 = test_cluster.get_address_1(); let address2 = test_cluster.get_address_2(); @@ -160,7 +168,14 @@ async fn test_custom_coin_transfer() { let keystore = &test_cluster.wallet.config.keystore; // TEST_COIN setup and mint - let init_ret = init_package(&client, keystore, sender).await.unwrap(); + let init_ret = init_package( + &client, + keystore, + sender, + Path::new("tests/custom_coins/test_coin"), + ) + .await + .unwrap(); let balances_to = vec![(COIN1_BALANCE, sender)]; let coin_type = init_ret.coin_tag.to_canonical_string(true); let _mint_res = mint(&client, keystore, init_ret, balances_to) @@ -234,3 +249,55 @@ async fn test_custom_coin_transfer() { serde_json::to_string(&ops2).unwrap() ); } + +#[tokio::test] +async fn test_custom_coin_without_symbol() { + const COIN1_BALANCE: u64 = 100_000_000_000_000_000; + let test_cluster = TestClusterBuilder::new().build().await; + let sender = test_cluster.get_address_0(); + let client = test_cluster.wallet.get_client().await.unwrap(); + let keystore = &test_cluster.wallet.config.keystore; + + // TEST_COIN setup and mint + let init_ret = init_package( + &client, + keystore, + sender, + Path::new("tests/custom_coins/test_coin_no_symbol"), + ) + .await + .unwrap(); + + let balances_to = vec![(COIN1_BALANCE, sender)]; + let mint_res = mint(&client, keystore, init_ret, balances_to) + .await + .unwrap(); + + let tx = client + .read_api() + .get_transaction_with_options( + mint_res.digest, + SuiTransactionBlockResponseOptions::new() + .with_input() + .with_effects() + .with_balance_changes() + .with_events(), + ) + .await + .unwrap(); + + assert_eq!( + &SuiExecutionStatus::Success, + tx.effects.as_ref().unwrap().status() + ); + let coin_cache = CoinMetadataCache::new(client, NonZeroUsize::new(2).unwrap()); + let ops = Operations::try_from_response(tx, &coin_cache) + .await + .unwrap(); + + for op in ops { + if op.type_ == OperationType::SuiBalanceChange { + assert!(!op.amount.unwrap().currency.symbol.is_empty()) + } + } +}