Sui.

Пост

Поделитесь своими знаниями.

Meaning.Sui.
Jul 09, 2025
Экспертные Вопросы и Ответы

Чеканите монету 2 раза на один и тот же адрес

Если я чеканю монету 2 раза по одному и тому же адресу, суммы не суммируются. В тесте отображается только последняя монетная сумма?

module package_addr::my_coin;
//https://docs.sui.io/guides/developer/coin
use sui::coin::{Self, Coin, TreasuryCap, CoinMetadata};

public struct MY_COIN has drop {}
const TOTAL_SUPPLY: u64 = 1_000_000_000_000_000_000;//includes decimal zeros
const INITIAL_SUPPLY: u64 = 900_000_000_000_000_000;
const REMAINING: u64 = 
100_000_000_000_000_000;

fun init(witness: MY_COIN, ctx: &mut TxContext) {
		let (mut treasury, metadata) = coin::create_currency(
				witness,
				9,
				b"MY_COIN",
				b"",
				b"",
				option::none(),
				ctx,
		);
    mint( &mut treasury, INITIAL_SUPPLY, ctx.sender(), ctx);
		transfer::public_freeze_object(metadata);
		transfer::public_transfer(treasury, ctx.sender())
}

public fun mint(
		treasury_cap: &mut TreasuryCap<MY_COIN>,
		amount: u64,
		recipient: address,
		ctx: &mut TxContext,
) {
		let coin = coin::mint(treasury_cap, amount, ctx);
		transfer::public_transfer(coin, recipient)
}

//----------== Test
#[test_only]use sui::test_scenario;
#[test_only] use sui::coin::value;
#[test_only] use std::debug::print as pp;
#[test_only] use std::string::{utf8};

#[test]
fun test_init() {
let admin = @0xAd;
let bob = @0xb0;
let mut sce = test_scenario::begin(admin);
{
    let otw = MY_COIN{};
    init(otw, sce.ctx());
};

sce.next_tx(admin);
{
  let coin = sce.take_from_sender<Coin<MY_COIN>>();
  pp(&utf8(b"admin balc1"));
  pp(&value(&coin));
  assert!(value(&coin) == INITIAL_SUPPLY, 441);
  sce.return_to_sender(coin);
};

//mint 2nd time
sce.next_tx(admin);
{
  let mut treasury = sce.take_from_sender<TreasuryCap<MY_COIN>>();
      mint(
        &mut treasury,
        2000,
        admin,//sce.ctx().sender(),
        sce.ctx()
    );
  sce.return_to_sender(treasury);
};

sce.next_tx(admin);
{
  let coin = sce.take_from_sender<Coin<MY_COIN>>();
  pp(&utf8(b"admin balc2"));
  pp(&value(&coin));// it shows 2000, instead of INITIAL_SUPPLY+2000  !!??
  assert!(value(&coin) == INITIAL_SUPPLY+2000, 442);// Failed here
  sce.return_to_sender(coin);
};
sce.end();
}
  • Sui
  • SDKs and Developer Tools
0
2
Поделиться
Комментарии
.

Ответы

2
HaGiang.
Jul 9 2025, 07:51

проблема в том, что это разные Coinобъекты. В игре Sui Move каждая операция монетного двора создает новый <T>объект Coin, а не автоматически комбинирует его с существующими монетами того же типа.

Поэтому вам нужно будет создать еще одну пробную транзакцию, чтобы получить оба монетных объекта и объединить их... как показано ниже

module package_addr::my_coin;
//https://docs.sui.io/guides/developer/coin
use sui::coin::{Self, Coin, TreasuryCap, CoinMetadata};

public struct MY_COIN has drop {}
const TOTAL_SUPPLY: u64 = 1_000_000_000_000_000_000;//includes decimal zeros
const INITIAL_SUPPLY: u64 = 900_000_000_000_000_000;
const REMAINING: u64 = 
100_000_000_000_000_000;

fun init(witness: MY_COIN, ctx: &mut TxContext) {
		let (mut treasury, metadata) = coin::create_currency(
				witness,
				9,
				b"MY_COIN",
				b"",
				b"",
				option::none(),
				ctx,
		);
    mint( &mut treasury, INITIAL_SUPPLY, ctx.sender(), ctx);
		transfer::public_freeze_object(metadata);
		transfer::public_transfer(treasury, ctx.sender())
}

public fun mint(
		treasury_cap: &mut TreasuryCap<MY_COIN>,
		amount: u64,
		recipient: address,
		ctx: &mut TxContext,
) {
		let coin = coin::mint(treasury_cap, amount, ctx);
		transfer::public_transfer(coin, recipient)
}

//----------== Test
#[test_only]use sui::test_scenario;
#[test_only] use sui::coin::value;
#[test_only] use std::debug::print as pp;
#[test_only] use std::string::{utf8};

#[test]
fun test_init() {
    let admin = @0xAd;
    let mut sce = test_scenario::begin(admin);
    {
        let otw = MY_COIN{};
        init(otw, sce.ctx());
    };

    // First transaction - check initial supply
    sce.next_tx(admin);
    {
        let coin = sce.take_from_sender<Coin<MY_COIN>>();
        pp(&utf8(b"admin balc1"));
        pp(&value(&coin));
        assert!(value(&coin) == INITIAL_SUPPLY, 441);
        sce.return_to_sender(coin);
    };

    // Second transaction - mint additional coins
    sce.next_tx(admin);
    {
        let mut treasury = sce.take_from_sender<TreasuryCap<MY_COIN>>();
        mint(
            &mut treasury,
            2000,
            admin,
            sce.ctx()
        );
        sce.return_to_sender(treasury);
    };

    // Third transaction - check combined balance
    sce.next_tx(admin);
    {
        // Take both coins from sender
        let coin1 = sce.take_from_sender<Coin<MY_COIN>>(); // Initial coin
        let coin2 = sce.take_from_sender<Coin<MY_COIN>>(); // Newly minted coin
        
        // Combine them
        let mut combined_coin = coin1;
        coin::join(&mut combined_coin, coin2);
        
        pp(&utf8(b"admin balc2"));
        pp(&value(&combined_coin));
        assert!(value(&combined_coin) == INITIAL_SUPPLY + 2000, 442);
        sce.return_to_sender(combined_coin);
    };
    sce.end();
}

Ключевые моменты:

При первой транзакции мы переводим исходную монету обратно отправителю, а не возвращаем ее

Во второй транзакции мы чеканяем новые монеты, в результате чего создается отдельный объект Coin

В третьей транзакции мы забираем обе монеты у отправителя

Используйте coin: :join, чтобы объединить их

Проверьте общую стоимость

Это соответствует реальному поведению Суя, когда каждый монетный двор создает новый предмет Coin

Кошельки обычно автоматически объединяют монеты

Вам необходимо явно объединять монеты в тестах, поскольку в тестовом сценарии они не объединяются автоматически.

0
Комментарии
.
24p30p.
Jul 9 2025, 21:07

Когда вы чеканите собственную монету несколько раз по одному и тому же адресу в Суе, каждый монетный двор создает отдельный Coin<T>предмет, а не дополняет существующий. Поэтому, если вы проверите баланс, используя только одну монету, вы увидите стоимость одной монеты, обычно последней выпущенной монеты. Чтобы получить полный баланс, вам необходимо объединить все монетные объекты, принадлежащие вашему адресу. Coin<MY_COIN>В тестах это означает, что нужно собрать все данныеcoin::merge, принадлежащие отправителю, и объединить их. В противном случае тест покажет сумму только одной монеты, а не сумму всех выпущенных монет. Вот простой пример объединения двух монет после нескольких монет в блоке транзакций:

let coin1 = sce.take_from_sender<Coin<MY_COIN>>();
let coin2 = sce.take_from_sender<Coin<MY_COIN>>();
let merged_coin = coin::merge(coin1, coin2);
pp(&value(&merged_coin));

Если вы чеканили несколько монет на один и тот же адрес, обязательно объединяйте их, прежде чем считывать баланс или использовать монеты в других операциях. Подробнее о том, как работают монеты в Sui и как правильно обращаться с ними, можно здесь: https://docs.sui.io/build/coin

0
Комментарии
.

Знаете ответ?

Пожалуйста, войдите в систему и поделитесь им.