帖子
分享您的知识。
结构发生变化时如何更新商家在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
答案
6在 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(®istry.merchant_to_key, utf8(key))
}
}
在 Sui Move 中,你不能简单地在表格中更改对象的密钥,也不能在没有适当处理的情况下直接删除/替换对象. 当您修改卖家结构中的密钥字段时,它不会自动更新 merchant_to_key 表. 这会使你的旧密钥仍然指向对象,而新密钥根本没有关联.
出现 “没有掉落能力就无法忽略值” 的错误是因为您的卖家结构可能没有掉落能力(这对于包含 UID 的结构来说很常见). 这意味着您不能简单地从表中删除该对象并将其丢弃;必须明确存储和重用返回的对象.
要正确更新卖家的密钥,您需要手动删除旧条目,使用新密钥更新 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 的所有权规则,防止资源意外丢失或 “泄漏”,这在处理缺乏掉落能力的类型时至关重要.
要在 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
}
如果你尝试在 ObjectTable 中更新商家的密钥@@<String, Merchant> 并遇到诸如 “没有掉落能力就无法忽略值” 之类的错误,那么问题在于在 Sui Move 中,如果不明确处理所有权和缺乏掉落能力的类型,就不能删除或替换存储空间中的对象. 当您更改卖家结构中的密钥时,它不会自动更新 merchant_to_key 表,因此您最终会看到旧密钥仍然指向对象,而新密钥根本没有关联.
如果你尝试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.
你知道答案吗?
请登录并分享。
Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.

- ... SUISuiLover+1211
- ... SUI0xduckmove+1207
- ... SUIThorfin+1204
- ... SUIharry phan+849
- ... SUIOwen+689
- ... SUIMeaning.Sui+675
- ... SUItheking+587