From c5a0f83247438bfe5753ad4bd4868bc64f6cd22b Mon Sep 17 00:00:00 2001 From: Patrick Kuo Date: Sun, 13 Oct 2024 13:36:17 +0100 Subject: [PATCH] [Rosetta] - skip balance change response in rosetta if symbol is empty (#19838) ## Description This is a fix to disregard coin balance change response for coins with empty symbol. ## Test plan Unit test --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-rosetta/src/operations.rs | 4 +- .../test_coin_no_symbol/Move.toml | 9 +++ .../sources/test_coin.move | 24 ++++++ .../tests/custom_coins/test_coin_utils.rs | 11 ++- .../sui-rosetta/tests/custom_coins_tests.rs | 73 ++++++++++++++++++- 5 files changed, 115 insertions(+), 6 deletions(-) create mode 100644 crates/sui-rosetta/tests/custom_coins/test_coin_no_symbol/Move.toml create mode 100644 crates/sui-rosetta/tests/custom_coins/test_coin_no_symbol/sources/test_coin.move 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()) + } + } +}