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 中,对象默认是 “资源”,这意味着它们不能被意外复制或丢弃. 这是通过类型系统强制执行的,在该类型系统中,结构要么具有删除能力(这意味着如果它们超出范围可以被丢弃),要么没有.

您的卖家结构可能没有掉落能力,因为它包含 UID. UID 是 Sui 上对象的唯一标识符,它们旨在防止意外删除或复制这些对象. 如果对象包含 UID,则通常无法将其删除.

当你尝试从 ObjectTable 中删除一个条目时,移除函数会返回该值(在本例中为卖家对象). 如果您没有将此返回值分配给变量或将其传递给使用该变量的另一个函数,Move 会假定您试图 “忽略” 或 “删除” 它. 由于您的卖家结构没有掉落能力,因此禁止此操作,这会导致错误 “没有掉落能力就无法忽略值”.

核心问题是,更改对象内的字段(比如 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_to_key 表. 这会使你的旧密钥仍然指向对象,而新密钥根本没有关联.

出现 “没有掉落能力就无法忽略值” 的错误是因为您的卖家结构可能没有掉落能力(这对于包含 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 中更新卖家的密钥,您必须手动:

使用 old_key 移除 Merchant 对象.

更新您刚刚检索到的卖家结构中的密钥字段,并使用 new_key 将修改后的卖家对象重新添加到 ObjectTable 中.

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

如果你尝试在 ObjectTable 中更新商家的密钥@@<String, Merchant> 并遇到诸如 “没有掉落能力就无法忽略值” 之类的错误,那么问题在于在 Sui Move 中,如果不明确处理所有权和缺乏掉落能力的类型,就不能删除或替换存储空间中的对象. 当您更改卖家结构中的密钥时,它不会自动更新 merchant_to_key 表,因此您最终会看到旧密钥仍然指向对象,而新密钥根本没有关联.

2
评论
.
24p30p.
24p30p1865
Jul 9 2025, 05:43

如果你尝试key在 a 中更新商家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能力(当它包含 a 时很常见UID),你必须始终明确地处理它.

要更清楚地了解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 社区成长而获得奖励。

奖励活动七月