Mover ganchos IBC

Descripción general

El gancho Move es un middleware IBC que se utiliza para permitir que las transferencias de tokens ICS-20 inicien llamadas de contrato. Esto permite llamadas de contratos entre cadenas, que implican movimiento de tokens. Esto es útil para una variedad de casos de uso. Uno de primordial importancia son los intercambios entre cadenas, que es una primitiva extremadamente poderosa.

El mecanismo que permite esto es un memocampo en cada paquete de transferencia ICS20 e ICS721 a partir de IBC v3.4.0 . Move Hooks es un middleware IBC que analiza una transferencia ICS20 y, si el memocampo tiene una forma particular, ejecuta una llamada de contrato Move. Ahora detallamos el memoformato de las llamadas de contrato Move y las garantías de ejecución proporcionadas.

Mover formato de ejecución de contrato

Antes de explorar el formato de metadatos de IBC, es fundamental comprender el formato de datos de enlace. El movimiento MsgExecutese define aquí y otros tipos se definen aquí como el siguiente tipo:

// HookData defines a wrapper for Move execute message
// and async callback.
type HookData struct {
 // Message is a Move execute message which will be executed
 // at `OnRecvPacket` of receiver chain.
 Message movetypes.MsgExecute `json:"message"`

 // AsyncCallback is a callback message which will be executed
 // at `OnTimeoutPacket` and `OnAcknowledgementPacket` of
 // sender chain.
 AsyncCallback *AsyncCallback `json:"async_callback,omitempty"`
}

// AsyncCallback is data wrapper which is required
// when we implement async callback.
type AsyncCallback struct {
 // callback id should be issued form the executor contract
 Id            uint64 `json:"id"`
 ModuleAddress string `json:"module_address"`
 ModuleName    string `json:"module_name"`
}

type MsgExecute struct {
 // Sender is the that actor that signed the messages
 Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"`
 // ModuleAddress is the address of the module deployer
 ModuleAddress string `protobuf:"bytes,2,opt,name=module_address,json=moduleAddress,proto3" json:"module_address,omitempty"`
 // ModuleName is the name of module to execute
 ModuleName string `protobuf:"bytes,3,opt,name=module_name,json=moduleName,proto3" json:"module_name,omitempty"`
 // FunctionName is the name of a function to execute
 FunctionName string `protobuf:"bytes,4,opt,name=function_name,json=functionName,proto3" json:"function_name,omitempty"`
 // TypeArgs is the type arguments of a function to execute
 // ex) "0x1::BasicCoin::Initia", "bool", "u8", "u64"
 TypeArgs []string `protobuf:"bytes,5,rep,name=type_args,json=typeArgs,proto3" json:"type_args,omitempty"`
 // Args is the arguments of a function to execute
 // - number: little endian
 // - string: base64 bytes
 Args [][]byte `protobuf:"bytes,6,rep,name=args,proto3" json:"args,omitempty"`
}

Así que detallamos de dónde queremos obtener cada uno de estos campos:

  • Sender: No podemos confiar en el remitente de un paquete IBC, la cadena contraparte tiene plena capacidad de mentir al respecto. No podemos arriesgarnos a que este remitente sea confundido con un usuario o dirección de módulo en particular en Initia. Entonces reemplazamos el remitente con una cuenta para representar al remitente con el prefijo del canal y un prefijo del módulo Move. Esto se hace configurando el remitente en Bech32(Hash(Hash("ibc-move-hook-intermediary") + channelID/sender)), donde channelId es la identificación del canal en la cadena local.

  • ModuleAddress: Este campo debe obtenerse directamente de los metadatos del paquete ICS-20.

  • ModuleName: Este campo debe obtenerse directamente de los metadatos del paquete ICS-20.

  • FunctionName: Este campo debe obtenerse directamente de los metadatos del paquete ICS-20.

  • TypeArgs: Este campo debe obtenerse directamente de los metadatos del paquete ICS-20.

  • Args: Este campo debe obtenerse directamente de los metadatos del paquete ICS-20.

Entonces nuestro mensaje de movimiento construido que ejecutamos se verá así:

msg := MsgExecuteContract{
 // Sender is the that actor that signed the messages
 Sender: "init1-hash-of-channel-and-sender",
 // ModuleAddress is the address of the module deployer
 ModuleAddress: packet.data.memo["move"]["message"]["module_address"],
    // ModuleName is the name of module to execute
 ModuleName: packet.data.memo["move"]["message"]["module_name"],
    // FunctionName is the name of a function to execute
 FunctionName: packet.data.memo["move"]["message"]["function_name"],
 // TypeArgs is the type arguments of a function to execute
 // ex) "0x1::BasicCoin::Initia", "bool", "u8", "u64"
 TypeArgs: packet.data.memo["move"]["message"]["type_args"],
 // Args is the arguments of a function to execute
 // - number: little endian
 // - string: base64 bytes
 Args: packet.data.memo["move"]["message"]["args"]}

Estructura del paquete ICS20

Entonces, dados los detalles anteriores, propagamos la estructura de datos del paquete ICS20 implícita. ICS20 es JSON nativo, por lo que usamos JSON para el formato de nota.

{
  //... other ibc fields that we don't care about
  "data": {
    "denom": "denom on counterparty chain (e.g. uatom)", // will be transformed to the local denom (ibc/...)
    "amount": "1000",
    "sender": "addr on counterparty chain", // will be transformed
    "receiver": "ModuleAddr::ModuleName::FunctionName",
    "memo": {
      "move": {
          // execute message on receive packet
          "message": {
            "module_address": "0x1",
            "module_name": "dex",
            "function_name": "swap",
            "type_args": ["0x1::native_uinit::Coin", "0x1::native_uusdc::Coin"],
            "args": ["base64 encoded bytes array"]
          },
          // optional field to get async callback (ack and timeout)
          "async_callback": {
            "id": 1,
            "module_address": "0x1",
            "module_name": "dex"
          }
        }
      }
    }
  }
}

Un paquete ICS20 tiene el formato correcto para los ganchos de movimiento si se cumple lo siguiente:

  • memono esta en blanco

  • memoes JSON válido

  • memotiene al menos una clave, con valor"move"

  • memo["move"]["message"]tiene exactamente cinco entradas, "module_address", "module_name", "function_name"y"type_args""args"

  • receiver== "" || receiver== "dirección_del_módulo::nombre_del_módulo::nombre_de_la_función"

Consideramos que un paquete ICS20 está dirigido a movehooks si se cumple todo lo siguiente:

  • memono esta en blanco

  • memoes JSON válido

  • memotiene al menos una clave, con nombre"move"

Si un paquete ICS20 no está dirigido a movehooks, movehooks no hace nada. Si un paquete ICS20 se dirige hacia movehooks y tiene un formato incorrecto, movehooks devuelve un error.

Flujo de ejecución

Ganchos previos al movimiento:

  • Asegúrese de que el paquete IBC entrante sea criptográficamente válido

  • Asegúrese de que el paquete IBC entrante no haya agotado el tiempo de espera.

En ganchos de movimiento, ejecución previa del paquete:

  • Asegúrese de que el paquete esté formateado correctamente (como se define anteriormente)

  • Edite el receptor para que sea la cuenta del módulo IBC codificada

En los ganchos de movimiento, publique la ejecución del paquete:

  • Construir mensaje de movimiento como se definió antes

  • Ejecutar mensaje de movimiento

  • si el mensaje de movimiento tiene error, devuelve ErrAck

  • de lo contrario, continúe a través del middleware

Devolución de llamada asíncrona

Es posible que un contrato que envía una transferencia IBC necesite escuchar el ACK de ese paquete. Para permitir que los contratos escuchen el acuse de recibo de paquetes específicos, proporcionamos devoluciones de llamada de acuse de recibo. El contrato que desea recibir una devolución de llamada debe implementar dos funciones.

  • ibc_ack

  • ibc_timeout

Copiar

public entry fun ibc_ack(
  account: &signer,
  callback_id: u64,
  success:     bool,
)

public entry fun ibc_timeout(
  account: &signer,
  callback_id: u64,
)

Además, cuando un contrato realiza una solicitud de transferencia de IBC, debe proporcionar datos de devolución de llamada asíncronos a través del campo de nota.

  • memo['move']['async_callback']['id']: la identificación de devolución de llamada asíncrona se asigna desde el contrato. luego se pasará como argumento de ibc_acky ibc_timeout.

  • memo['move']['async_callback']['module_address']: La dirección del módulo que define la función de devolución de llamada.

  • memo['move']['async_callback']['module_name']: El nombre del módulo que define la función de devolución de llamada.

Conclusión

El gancho Move representa un avance en la interoperabilidad, ya que permite funcionalidades fluidas entre cadenas y mejora la utilidad de las transferencias de tokens. Al aprovechar el gancho Move, los desarrolladores pueden iniciar interacciones contractuales complejas en diferentes cadenas, fomentando nuevos casos de uso y aplicaciones. Este desarrollo no solo simplifica las barreras técnicas previamente asociadas con las comunicaciones entre cadenas, sino que también allana el camino para ecosistemas blockchain más integrados y eficientes. A medida que esta tecnología madure, sin duda desbloqueará un mayor potencial para aplicaciones descentralizadas, contribuyendo a una adopción más amplia y a la eficacia operativa de las tecnologías blockchain.

Last updated