Sui.

Bài viết

Chia sẻ kiến thức của bạn.

Owen.
Owen1185
Jun 11, 2025
Hỏi đáp Chuyên Gia

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 Merchantstruct 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 MerchantRegistrycấ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 Merchanttrong cấu trúc, thay đổi sẽ không tự động cập nhật khóa trong merchant_to_keybả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
5
6
Chia sẻ
Bình luận
.

Câu trả lời

6
HaGiang.
Jul 23 2025, 15:44

Trong 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(&registry.merchant_to_key, utf8(key))
    }
}
4
Bình luận
.
MoonBags.
Jul 23 2025, 15:30

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.

3
Bình luận
.
0xduckmove.
Jul 23 2025, 15:32

Để 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ả.

3
Bình luận
.
harry phan.
Jul 23 2025, 15:45

Để 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
}
2
Bình luận
.
Meaning.Sui.
Jul 23 2025, 15:52

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.

2
Bình luận
.
24p30p.
24p30p1865
Jul 9 2025, 05:43

Nếu bạn đang cố gắng cập nhật thương nhân keytrong 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 dropkhả năng. Khi bạn thay đổi khóa bên trong Merchantcấu trúc của mình, điều đó không tự động cập nhật merchant_to_keybả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ì Merchantcấu trúc không có dropkhả 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 Merchantcấu trúc của bạn không có dropkhả 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 ObjectTablehoạ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.

0
Bình luận
.

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.

609Bài viết1280Câu trả lời
Sui.X.Peera.

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.

Chiến dịch phần thưởngTháng Bảy