Sui.

Publicación

Comparte tu conocimiento.

Owen.
Owen1185
Jun 11, 2025
P&R expertos

¿Cómo actualizar la clave de un comerciante en ObjectTable cuando cambia en la estructura?

Hola a todos, acabo de empezar a escribir contratos inteligentes y estoy trabajando en mi primer proyecto. Me encantaría recibir ayuda con un problema en el que estoy atrapado.

Hasta ahora, he creado una Merchantestructura que se ve así: -id: un identificador único (UID) -owner: la dirección del comerciante -key: una cadena utilizada como clave única -balance: un u64 que representa su saldo

También creé una MerchantRegistryestructura para administrar a todos los comerciantes: -id: otro identificador -merchant_to_address: una ObjectTable<address, Merchant>asignación de direcciones a los comerciantes -merchant_to_key: un ObjectTable<String, Merchant>mapeo de claves para los comerciantes

Quiero poder buscar un comerciante por sudireccióno por suclave.

MerchantCuando un usuario actualiza su clave dentro de la merchant_to_keyestructura, el cambio no actualiza automáticamente la clave de la tabla. Esto significa que la llave antigua sigue apuntando al comerciante, lo que descifra las cosas.

Intenté eliminar la entrada de la tabla y volver a insertarla con la nueva clave, pero sigo encontrando errores como:

«No se pueden ignorar los valores sin la habilidad de soltar»

Estoy bastante seguro de que se trata de un error de principiante, pero no he podido encontrar una explicación o solución clara en ninguna parte. ¿Existe una forma adecuada de gestionar la actualización de la clave tanto en la estructura como en la tabla de búsqueda?

  • Sui
  • Move
5
6
Cuota
Comentarios
.

Respuestas

6
HaGiang.
Jul 23 2025, 15:44

En Sui Move, los objetos son «recursos» por defecto, lo que significa que no se pueden duplicar ni descartar accidentalmente. Esto se aplica mediante un sistema de tipos en el que las estructuras tienen la habilidad de soltar (lo que significa que pueden descartarse si se salen de su alcance) o no la tienen.

Es probable que tu estructura de comerciante no tenga la habilidad de soltar porque contiene un UID. Los UID son identificadores únicos para los objetos de Sui y están diseñados para evitar la eliminación accidental o la duplicación de esos objetos. Si un objeto contiene un UID, normalmente no se puede eliminar.

Cuando intentas eliminar una entrada de una ObjectTable, la función de eliminación devuelve el valor (el objeto Merchant en este caso). Si no asignas este valor devuelto a una variable ni lo pasas a otra función que lo consuma, Move asume que estás intentando «ignorarlo» o «descartarlo». Como tu estructura Merchant no tiene la habilidad de borrar, esta acción está prohibida y se produce el error «No se pueden ignorar los valores sin la habilidad de borrar».

El problema principal es que al cambiar un campo dentro de un objeto (por ejemplo, merchant.key = new_key;) no se actualizan automáticamente las claves de tu ObjectTable. La ObjectTable almacena el objeto en función de la clave con la que se agregó. Por lo tanto, si la clave interna cambia, la tabla solo conoce la clave anterior.

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
Comentarios
.
MoonBags.
Jul 23 2025, 15:30

En Sui Move, no puedes simplemente cambiar la clave de un objeto dentro de una tabla o eliminar/reemplazar objetos directamente sin un manejo adecuado. Cuando modificas el campo clave dentro de tu estructura Merchant, no se actualiza automáticamente la tabla merchant_to_key. Esto hace que tu clave anterior siga apuntando al objeto, mientras que la nueva clave no tiene ninguna asociación.

El error «No se pueden ignorar los valores sin la habilidad de soltar» se debe a que es probable que tu estructura de comerciante no tenga la habilidad de soltar (algo habitual en las estructuras que contienen un UID). Esto significa que no puedes simplemente eliminar el objeto de la tabla y descartarlo; debes almacenar y reutilizar de forma explícita el objeto devuelto.

3
Comentarios
.
0xduckmove.
Jul 23 2025, 15:32

Para actualizar correctamente la clave de un vendedor, debes eliminar manualmente la entrada anterior, actualizar la estructura del vendedor con la nueva clave y, a continuación, volver a insertarla en la tabla. A continuación te explicamos cómo puedes hacerlo en una transacción:

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);
}```

Este enfoque garantiza que extraiga de forma segura el objeto Merchant, modifique su clave y, a continuación, lo vuelva a insertar en ObjectTable con la nueva clave. 
Este método respeta las reglas de propiedad de Move y evita la pérdida accidental o la «fuga» de recursos, algo crucial cuando se trata de tipos que carecen de la capacidad de borrar.

3
Comentarios
.
harry phan.
Jul 23 2025, 15:45

Para actualizar la clave de un vendedor en ObjectTable, debes hacer lo siguiente de forma manual:

elimina el objeto Merchant con la old_key.

Actualiza el campo clave de la estructura Merchant que acabas de recuperar y vuelve a añadir el objeto Merchant modificado a la ObjectTable con la 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
Comentarios
.
Meaning.Sui.
Jul 23 2025, 15:52

Si intentas actualizar la clave de un comerciante en una ObjectTable<String, Merchant> y te encuentras con errores como «No se pueden ignorar los valores sin la capacidad de borrar», el problema es que en Sui Move no puedes simplemente eliminar o reemplazar objetos almacenados sin gestionar explícitamente la propiedad y los tipos que carecen de la capacidad de borrar. Cuando cambias la clave dentro de tu estructura Merchant, no se actualiza automáticamente la tabla merchant_to_key, por lo que la clave anterior sigue apuntando al objeto, mientras que la nueva clave no está vinculada en absoluto.

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

Si intentas actualizar la información de un comerciante key``ObjectTable<String, Merchant>y te encuentras con errores como «No se pueden ignorar los valores sin la posibilidad de eliminarlos», el problema es que en Sui Move no puedes simplemente eliminar o reemplazar objetos almacenados sin gestionar explícitamente la propiedad y los tipos que carecen de esa dropcapacidad. Cuando cambias la clave dentro de tu Merchantestructura, la merchant_to_keytabla no se actualiza automáticamente, por lo que la clave anterior sigue apuntando al objeto, mientras que la nueva clave no está vinculada en absoluto.

Para solucionarlo, tienes queeliminar manualmente la entrada antigua, actualizar la estructura y, a continuaciónvolver a insertarla con la nueva clave. Pero como la Merchantestructura no tiene esa dropcapacidad, no puedes simplemente eliminarla e ignorar el resultado: tienes que almacenar y reutilizar el objeto devuelto. Así es como puedes hacerlo correctamente dentro de una transacción:

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);
}

De esta forma, extraes el vendedor, lo modificas y lo vuelves a insertar con la nueva clave de forma segura, sin dejar de respetar las normas de propiedad de Move.

Recuerda que esto es necesario porque Move está diseñado para evitar que se caigan o se pierdan recursos accidentalmente. Si tu Merchantestructura no tiene esta drophabilidad (lo cual es habitual cuando contiene unaUID), siempre debes utilizarla de forma explícita.

Para tener una idea más clara de cómo ObjectTablefunciona y cómo gestionar la propiedad de forma segura, consulta la documentación oficial aquí: https://docs.sui.io/build/move/object-tables.

0
Comentarios
.

Sabes la respuesta?

Inicie sesión y compártalo.

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

609Publicaciones1280Respuestas
Sui.X.Peera.

Gana tu parte de 1000 Sui

Gana puntos de reputación y obtén recompensas por ayudar a crecer a la comunidad de Sui.

Campaña de RecompensasJulio