Sui.

Пост

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

Owen.
Owen1185
Jun 11, 2025
Экспертные Вопросы и Ответы

Как обновить ключ продавца в ObjectTable при его изменении в структуре?

Всем привет! Я только начинаю писать смарт-контракты и работаю над своим самым первым проектом. Мне бы очень хотелось получить помощь в решении проблемы, в которой я застрял.

До сих пор я создал Merchantструктуру, которая выглядит следующим образом: -id: уникальный идентификатор (UID) -owner: адрес продавца -key: строка, используемая в качестве уникального ключа -balance: u64, представляющий их баланс

Я также создал MerchantRegistryструктуру для управления всеми продавцами: -id: еще один UID -merchant_to_address: ObjectTable<address, Merchant>сопоставление адресов продавцов -merchant_to_key: ObjectTable<String, Merchant>сопоставление ключей с продавцами

Я хочу найти продавца по егоадресуили поключу.

MerchantКогда пользователь обновляет свой ключ в merchant_to_keyструктуре, это изменение не приводит к автоматическому обновлению ключа в таблице. Это означает, что старый ключ по-прежнему указывает на продавца, что ломает ситуацию.

Я попытался удалить запись из таблицы и вставить её обратно с новым ключом, но всё время сталкивался с такими ошибками, как:

«Невозможно игнорировать значения, не умея их удалять»

Я уверен, что это ошибка новичка, но мне нигде не удалось найти четкого объяснения или решения. Есть ли правильный способ обновления ключа как в структуре, так и в таблице поиска?

  • Sui
  • Move
5
6
Поделиться
Комментарии
.

Ответы

6
HaGiang.
Jul 23 2025, 15:44

В Sui Move объекты по умолчанию являются «ресурсами», то есть их нельзя случайно дублировать или удалять. Этому способствует система типов, в которой структуры либо можно удалять (то есть их можно удалять, если они выходят за пределы области видимости), либо нет.

Скорее всего, в вашей структуре Merchant нет возможности удаления, поскольку она содержит UID. UID — это уникальные идентификаторы объектов в Sui. Они предназначены для предотвращения случайного удаления или дублирования этих объектов. Если объект содержит UID, его обычно нельзя удалить.

При попытке удалить запись из ObjectTable функция удаления возвращает значение (в данном случае это объект Merchant). Если вы не присвоите это возвращаемое значение переменной или не передадите его другой функции, которая его использует, Move предполагает, что вы пытаетесь «проигнорировать» или «удалить» это значение. Поскольку в вашей структуре Merchant нет возможности удаления, это действие запрещено, в результате чего появляется ошибка «Невозможно игнорировать значения без возможности удаления».

Основная проблема заключается в том, что изменение поля внутри объекта (например, merchant.key = new_key;) не приводит к автоматическому обновлению ключей в таблице ObjectTable. В ObjectTable объект хранится на основе ключа, с помощью которого он был добавлен. Таким образом, если внутренний ключ изменится, в таблице останется информация только о старом ключе.

module your_address::your_module_name {

    use sui::object::{Self, UID};
    use sui::tx_context::{Self, TxContext};
    use sui::string::{String, utf8};
    use sui::object_table::{Self, ObjectTable}; // Import ObjectTable

    // Define your Merchant struct
    public struct Merchant has key, store {
        id: UID,
        owner: address,
        key: String, // This is the key you want to update
        balance: u64,
    }

    // Define your MerchantRegistry struct
    public struct MerchantRegistry has key, store {
        id: UID,
        merchant_to_address: ObjectTable<address, Merchant>, // Mapping addresses to merchants
        merchant_to_key: ObjectTable<String, Merchant>,     // Mapping keys to merchants
    }

    // You'll likely have an `init` function to create the registry
    fun init(ctx: &mut TxContext) {
        let registry = MerchantRegistry {
            id: object::new(ctx),
            merchant_to_address: object_table::new(ctx),
            merchant_to_key: object_table::new(ctx),
        };
        // Transfer ownership of the registry, or make it shared if applicable
        sui::transfer::transfer(registry, tx_context::sender(ctx));
    }

    // Function to add a new merchant (for context)
    public entry fun add_merchant(
        registry: &mut MerchantRegistry,
        merchant_owner: address,
        merchant_key: vector<u8>, // Using vector<u8> for input, convert to String
        ctx: &mut TxContext
    ) {
        let merchant = Merchant {
            id: object::new(ctx),
            owner: merchant_owner,
            key: utf8(merchant_key),
            balance: 0,
        };

        // Add to both tables
        object_table::add(&mut registry.merchant_to_address, merchant_owner, object::share_object(merchant));     
        // You'll need to decide on a primary storage and potentially store references or IDs in other tables.
        // For simplicity, let's assume `merchant_to_key` is the primary and `merchant_to_address` stores the object ID.
        // If `Merchant` is a shared object, you can store it in multiple tables.
        // Let's adjust the `Merchant` struct for clarity in this example.

        // For simplicity and to avoid the "same object in multiple places" issue,
        // it's often better to have one "primary" table that owns the object,
        // and other tables store only the object's ID or a reference.
        // If you truly need the Merchant object directly in both tables, the Merchant itself needs to be a SHARED object.
        // Let's revise the example to make Merchant a shared object.

        // REVISED MERCHANT STRUCT (if you want it in both tables directly):
        // public struct Merchant has key { // No 'store' ability if it's shared directly
        //     id: UID,
        //     owner: address,
        //     key: String,
        //     balance: u64,
        // }
        // public entry fun add_merchant(...) {
        //     let merchant = Merchant { ... };
        //     object::share_object(merchant); // Make it shared
        //     object_table::add(&mut registry.merchant_to_address, merchant_owner, merchant);
        //     object_table::add(&mut registry.merchant_to_key, utf8(merchant_key), merchant);
        // }
        // This makes `Merchant` a shared object, which has different implications for ownership and mutability.

        //***************Let's stick to the original assumption that Merchant is not shared,
        //***************and therefore only one table directly owns the `Merchant` object.
        //***************If you want to look up by address AND key, and only one table can own the object,
        //***************then one table (e.g., merchant_to_address) stores the `Merchant` object,
        //***************and the other (merchant_to_key) stores the `ID` of the Merchant.

        // For this scenario, let's assume `merchant_to_address` owns the `Merchant` objects,
        // and `merchant_to_key` will map a String key to the `ID` of the Merchant.

        // New Merchant struct:
        // public struct Merchant has key, store {
        //     id: UID,
        //     owner: address,
        //     key: String, // Stored inside the object
        //     balance: u64,
        // }

        // Updated MerchantRegistry:
        // public struct MerchantRegistry has key, store {
        //     id: UID,
        //     merchant_to_address: ObjectTable<address, Merchant>, // Owners the Merchant object
        //     merchant_to_key: ObjectTable<String, object::ID>,    // Maps key to Merchant's ID
        // }

        //****************Let's assume for the sake of the user's original question*****************
        //****************that `ObjectTable<String, Merchant>` is intended to hold****************
        //****************the actual Merchant object, which implies it's the primary****************
        //****************owner or the `Merchant` is shared.
        //****************If `Merchant` is not shared, only ONE `ObjectTable` can hold it.
        //****************The error "Cannot ignore values without drop ability" confirms
        //****************that the `Merchant` object itself is being handled.

        // Assuming merchant_to_key is the primary table where the Merchant object resides:
        object_table::add(&mut registry.merchant_to_key, utf8(merchant_key), merchant);
        // If you still need lookup by address, you'd store the ID in merchant_to_address:
        // object_table::add(&mut registry.merchant_to_address, merchant_owner, object::id(&merchant));
        // This creates a circular dependency in a single transaction if you try to get ID *after* adding.
        // Best practice is one table owning the object, others holding IDs.

        // For the sake of directly answering the user's original question about `merchant_to_key`:
    }


    /// Entry function to update a merchant's key in the ObjectTable.
    public entry fun update_merchant_key(
        registry: &mut MerchantRegistry,
        old_key: vector<u8>, // Input as vector<u8> for String conversion
        new_key: vector<u8>, // Input as vector<u8> for String conversion
        ctx: &mut TxContext // TxContext is often needed for object::new or other operations
    ) {
        // Convert input vectors to Sui Strings
        let old_key_str = utf8(old_key);
        let new_key_str = utf8(new_key);

        // 1. Remove the merchant using the old key.
        // This returns the 'Merchant' object. Since Merchant does not have 'drop',
        // we *must* bind it to a variable.
        let mut merchant = object_table::remove(&mut registry.merchant_to_key, old_key_str);

        // 2. Update the 'key' field inside the retrieved Merchant struct.
        merchant.key = new_key_str;

        // 3. Re-insert the updated Merchant object into the table with the new key.
        // This will create a new entry in the ObjectTable.
        object_table::add(&mut registry.merchant_to_key, new_key_str, merchant);

        // If you also had a `merchant_to_address` table mapping to `object::ID`,
        // and the `owner` of the merchant might also change, you'd need to update that as well.
        // For example:
        // let old_address = merchant.owner; // Assuming merchant.owner is still the original owner
        // if (old_address != new_owner_address) { // If owner changes
        //     let merchant_id = object_table::remove(&mut registry.merchant_to_address, old_address);
        //     // merchant.owner = new_owner_address; // Update owner in the struct if needed
        //     object_table::add(&mut registry.merchant_to_address, new_owner_address, merchant_id);
        // }
    }

    // You might also want a function to get a merchant by key for testing
    public fun get_merchant_by_key(
        registry: &MerchantRegistry,
        key: vector<u8>
    ): &Merchant {
        object_table::borrow(&registry.merchant_to_key, utf8(key))
    }
}
4
Комментарии
.
MoonBags.
Jul 23 2025, 15:30

В Sui Move нельзя просто изменить ключ объекта в таблице или напрямую удалить/заменить объекты без надлежащей обработки. Когда вы изменяете ключевое поле в структуре Merchant, таблица merchant_to_key автоматически не обновляется. Таким образом, ваш старый ключ по-прежнему указывает на объект, а новый ключ вообще не имеет связи.

Ошибка «Невозможно игнорировать значения без возможности удаления» возникает из-за того, что в вашей структуре Merchant, скорее всего, нет возможности удаления (что характерно для структур, содержащих UID). Это означает, что вы не можете просто удалить объект из таблицы и выбросить его; вы должны явно сохранить и повторно использовать возвращенный объект.

3
Комментарии
.
0xduckmove.
Jul 23 2025, 15:32

Чтобы правильно обновить ключ продавца, вам необходимо вручную удалить старую запись, обновить структуру Merchant новым ключом, а затем снова вставить ее в таблицу. Вот как это можно сделать в транзакции:

public entry fun update_merchant_key(
    registry: &mut MerchantRegistry,
    old_key: String,
    new_key: String
) {
    // 1. Remove the merchant using the old key and store the returned object.
    let merchant = object_table::remove(&mut registry.merchant_to_key, old_key);

    // 2. Update the key field inside the struct.
    merchant.key = new_key;

    // 3. Re-insert the merchant into the table with the updated (new) key.
    object_table::add(&mut registry.merchant_to_key, new_key, merchant);
}```

Такой подход позволяет безопасно извлечь объект Merchant, изменить его ключ, а затем вставить его обратно в ObjectTable с новым ключом. 
Этот метод учитывает правила владения Move и предотвращает случайную потерю или «утечку» ресурсов, что очень важно при работе с типами, которые не могут быть удалены.

3
Комментарии
.
harry phan.
Jul 23 2025, 15:45

Чтобы обновить ключ продавца в ObjectTable, вы должны вручную:

удалить объект Merchant с помощью old_key.

Обновите ключевое поле в только что полученной структуре Merchant и добавьте измененный объект Merchant обратно в ObjectTable, используя new_key.

public entry fun update_merchant_key(
    registry: &mut MerchantRegistry,
    old_key: String,
    new_key: String
) {
    let mut merchant = object_table::remove(&mut registry.merchant_to_key, old_key); // 1. Remove and capture
    merchant.key = new_key; // 2. Update the struct
    object_table::add(&mut registry.merchant_to_key, new_key, merchant); // 3. Re-insert
}
2
Комментарии
.
Meaning.Sui.
Jul 23 2025, 15:52

Если вы пытаетесь обновить ключ продавца в таблице объектов<String, Merchant> и сталкиваетесь с ошибками типа «Невозможно игнорировать значения, не удаляя их», проблема в том, что в Sui Move нельзя просто удалять или заменять объекты в хранилище без явного указания владельца и типов, которые не могут быть удалены. Когда вы меняете ключ в структуре Merchant, таблица merchant_to_key автоматически не обновляется, поэтому старый ключ по-прежнему указывает на объект, а новый ключ вообще не связан.

2
Комментарии
.
24p30p.
24p30p1865
Jul 9 2025, 05:43

Если вы пытаетесь обновить данные продавца key``ObjectTable<String, Merchant>и сталкиваетесь с ошибками типа «Невозможно игнорировать значения без возможности их удаления», проблема в том, что в Sui Move нельзя просто удалять или заменять объекты в хранилище, не указав, что право собственности и типы объектов не имеют такой dropвозможности. Когда вы меняете ключ в Merchantструктуре, merchant_to_keyтаблица автоматически не обновляется, поэтому старый ключ по-прежнему указывает на объект, а новый ключ вообще не связан.

Чтобы исправить это, вам нужновручную удалить старую запись, обновить структуру, а затемснова вставить ее с помощью нового ключа. Но поскольку в Merchantструктуре нет таких dropвозможностей, ее нельзя просто удалить и проигнорировать результат — возвращаемый объект нужно сохранить и повторно использовать. Вот как правильно это сделать в транзакции:

public entry fun update_merchant_key(
    registry: &mut MerchantRegistry,
    old_key: String,
    new_key: String
) {
    // Remove the merchant using the old key and store the returned object
    let merchant = object_table::remove(&mut registry.merchant_to_key, old_key);

    // Update the key field inside the struct
    merchant.key = new_key;

    // Re-insert into the table with the updated key
    object_table::add(&mut registry.merchant_to_key, new_key, merchant);
}

Таким образом, вы безопасно извлечете имя продавца, измените его и вставите обратно с помощью нового ключа, соблюдая правила владения Move.

Помните, что это необходимо, потому что Move разработан таким образом, чтобы предотвратить случайную потерю или утечку ресурсов. Если в вашей Merchantструктуре нет такой dropвозможности (что обычно бывает, когда она содержит строку aUID), вы всегда должны обрабатывать ее явно.

Чтобы получить более четкое представление о том, как это ObjectTableработает и как безопасно распоряжаться правами собственности, ознакомьтесь с официальной документацией здесь: https://docs.sui.io/build/move/object-tables.

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

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

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

Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.

609Посты1280Ответы
Sui.X.Peera.

Заработай свою долю из 1000 Sui

Зарабатывай очки репутации и получай награды за помощь в развитии сообщества Sui.

Кампания вознагражденийИюль