Bài viết
Chia sẻ kiến thức của bạn.
Làm cách nào để cập nhật khóa của người bán trong ObjectTable khi nó thay đổi trong cấu trúc?
Xin chào mọi người, tôi mới bắt đầu viết hợp đồng thông minh và tôi đang thực hiện dự án đầu tiên của mình. Tôi rất muốn được giúp đỡ với một vấn đề mà tôi gặp khó khăn.
Cho đến nay, tôi đã tạo một Merchant
struct trông như thế này:
-id
: một định danh duy nhất (UID)
-owner
: địa chỉ của người bán
-key
: Chuỗi được sử dụng như một khóa duy nhất
-balance
: một u64 đại diện cho số dư của họ
Tôi cũng đã tạo một MerchantRegistry
cấu trúc để quản lý tất cả các thương nhân:
-id
: UID khác
-merchant_to_address
: ObjectTable<address, Merchant>
lập bản đồ địa chỉ cho thương nhân
-merchant_to_key
: một ObjectTable<String, Merchant>
bản đồ chìa khóa cho thương nhân
Tôi muốn có thể tìm kiếm một thương nhân bằngđịa chỉcủa họ hoặc bằng** khóa**của họ.
Khi người dùng cập nhật khóa của họ bên Merchant
trong cấu trúc, thay đổi sẽ không tự động cập nhật khóa trong merchant_to_key
bảng. Điều đó có nghĩa là chìa khóa cũ vẫn chỉ vào thương gia, làm hỏng mọi thứ.
Tôi đã thử xóa mục nhập khỏi bảng và chèn nó trở lại bằng khóa mới, nhưng tôi tiếp tục gặp lỗi như:
“Không thể bỏ qua các giá trị mà không có khả năng giảm”
Tôi khá chắc chắn đây là một sai lầm của người mới bắt đầu, nhưng tôi không thể tìm thấy lời giải thích hoặc giải pháp rõ ràng ở bất cứ đâu. Có cách nào thích hợp để xử lý việc cập nhật khóa trong cả cấu trúc và bảng tra cứu không?
- Sui
- Move
Câu trả lời
6Trong Sui Move, các đối tượng là “tài nguyên” theo mặc định, có nghĩa là chúng không thể bị nhân đôi hoặc loại bỏ vô tình. Điều này được thực thi thông qua một hệ thống kiểu mà các cấu trúc hoặc có khả năng thả (có nghĩa là chúng có thể bị loại bỏ nếu chúng vượt ra ngoài phạm vi) hoặc chúng không.
Struct Merchant của bạn có thể không có khả năng thả vì nó chứa UID. UID là định danh duy nhất cho các đối tượng trên Sui và chúng được thiết kế để ngăn chặn việc vô tình xóa hoặc sao chép các đối tượng đó. Nếu một đối tượng chứa một UID, nó thường không thể bị loại bỏ.
Khi bạn cố gắng xóa một mục nhập khỏi ObjectTable, hàm remove trả về giá trị (đối tượng Merchant trong trường hợp này). Nếu bạn không gán giá trị trả về này cho một biến hoặc chuyển nó cho một hàm khác tiêu thụ nó, Move giả định bạn đang cố gắng “bỏ qua” hoặc “thả” nó. Vì cấu trúc Merchant của bạn không có khả năng thả nên hành động này bị cấm, dẫn đến lỗi “Không thể bỏ qua các giá trị mà không có khả năng thả”.
Vấn đề cốt lõi là việc thay đổi một trường bên trong một đối tượng (như merchant.key = new_key;) không tự động cập nhật các khóa trong ObjectTable của bạn. ObjectTable lưu trữ đối tượng dựa trên khóa mà nó đã được thêm vào. Vì vậy, nếu khóa nội bộ thay đổi, bảng vẫn chỉ biết về khóa cũ.
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))
}
}
Trong Sui Move, bạn không thể đơn giản thay đổi khóa của một đối tượng trong một bảng hoặc trực tiếp xóa/thay thế các đối tượng mà không xử lý thích hợp. Khi bạn sửa đổi trường khóa bên trong cấu trúc Merchant, nó sẽ không tự động cập nhật bảng merchant_to_key. Điều này khiến khóa cũ của bạn vẫn trỏ đến đối tượng, trong khi khóa mới hoàn toàn không có liên kết.
Lỗi “Không thể bỏ qua các giá trị mà không có khả năng thả” phát sinh vì cấu trúc Merchant của bạn có thể không có khả năng thả (điều này phổ biến đối với các cấu trúc chứa UID). Điều này có nghĩa là bạn không thể đơn giản xóa đối tượng khỏi bảng và loại bỏ nó; bạn phải lưu trữ và sử dụng lại đối tượng trả về một cách rõ ràng.
Để cập nhật chính xác khóa của thương nhân, bạn cần xóa thủ công mục nhập cũ, cập nhật cấu trúc Merchant bằng khóa mới, sau đó chèn lại vào bảng. Đây là cách bạn có thể thực hiện điều đó trong một giao dịch:
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);
}```
Cách tiếp cận này đảm bảo bạn trích xuất một cách an toàn đối tượng Merchant, sửa đổi khóa của nó và sau đó chèn nó trở lại ObjectTable với khóa mới.
Phương pháp này tôn trọng các quy tắc sở hữu của Move và ngăn ngừa tình trạng mất mát hoặc “rò rỉ” tài nguyên, điều này rất quan trọng khi xử lý các loại không có khả năng thả.
Để cập nhật khóa thương nhân trong ObjectTable, bạn phải theo cách thủ công:
loại bỏ đối tượng Merchant bằng old_key.
Cập nhật trường khóa bên trong cấu trúc Merchant mà bạn vừa truy xuất và thêm đối tượng Merchant đã sửa đổi trở lại ObjectTable bằng 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
}
Nếu bạn đang cố gắng cập nhật khóa của thương nhân trong ObjectTable<String, Merchant> và gặp lỗi như “Không thể bỏ qua các giá trị mà không có khả năng thả”, vấn đề là trong Sui Move, bạn không thể xóa hoặc thay thế các đối tượng trong bộ nhớ mà không xử lý rõ ràng quyền sở hữu và loại thiếu khả năng thả. Khi bạn thay đổi khóa bên trong cấu trúc Merchant, điều đó không tự động cập nhật bảng merchant_to_key—vì vậy bạn sẽ thấy khóa cũ vẫn trỏ đến đối tượng, trong khi khóa mới hoàn toàn không được liên kết.
Nếu bạn đang cố gắng cập nhật thương nhân key
trong ObjectTable<String, Merchant>
a và gặp lỗi như “Không thể bỏ qua các giá trị mà không có khả năng thả”, vấn đề là trong Sui Move, bạn không thể xóa hoặc thay thế các đối tượng trong bộ nhớ mà không xử lý rõ ràng quyền sở hữu và loại thiếu drop
khả năng. Khi bạn thay đổi khóa bên trong Merchant
cấu trúc của mình, điều đó không tự động cập nhật merchant_to_key
bảng — vì vậy bạn sẽ thấy khóa cũ vẫn trỏ đến đối tượng, trong khi khóa mới hoàn toàn không được liên kết.
Để khắc phục điều này, bạn cần phải xóa thủ công mục nhập cũ**, cập nhật cấu trúc, và sau đóchèn lại nó bằng khóa mới. Nhưng vì Merchant
cấu trúc không có drop
khả năng, bạn không thể chỉ xóa nó và bỏ qua kết quả — bạn phải lưu trữ và sử dụng lại đối tượng trả về. Dưới đây là cách bạn có thể thực hiện đúng cách trong một giao dịch:
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);
}
Bằng cách này, bạn giải nén thương nhân một cách an toàn, sửa đổi và chèn lại bằng khóa mới—tất cả trong khi vẫn tuân thủ các quy tắc sở hữu của Move.
Hãy nhớ rằng, điều này là cần thiết vì Move được thiết kế để ngăn chặn vô tình làm rơi hoặc rò rỉ tài nguyên. Nếu Merchant
cấu trúc của bạn không có drop
khả năng (thường gặp khi nó chứa aUID
), bạn phải luôn xử lý nó một cách rõ ràng.
Để có được ý tưởng rõ ràng hơn về cách ObjectTable
hoạt động và cách xử lý quyền sở hữu một cách an toàn, hãy kiểm tra tài liệu chính thức tại đây: https://docs.sui.io/build/move/object-tables.
Bạn có biết câu trả lời không?
Hãy đăng nhập và chia sẻ nó.
Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.
Kiếm phần của bạn từ 1000 Sui
Tích lũy điểm danh tiếng và nhận phần thưởng khi giúp cộng đồng Sui phát triển.

- ... SUISuiLover+1211
- ... SUI0xduckmove+1207
- ... SUIThorfin+1204
- ... SUIharry phan+849
- ... SUIOwen+689
- ... SUIMeaning.Sui+675
- ... SUItheking+587
- Tại sao BCS yêu cầu thứ tự trường chính xác để khử chuỗi khi cấu trúc Move có các trường được đặt tên?53
- Nhiều lỗi xác minh nguồn” trong các ấn phẩm về mô-đun Sui Move - Giải quyết lỗi tự động43
- Giao dịch Sui thất bại: Đối tượng được dành riêng cho giao dịch khác25
- Làm thế nào để các ràng buộc về khả năng tương tác với các trường động trong các bộ sưu tập không đồng nhất?05