Administrador de canales para NFT

Los tokens no fungibles (NFT) se están convirtiendo en una locura en estos días, ya que brindan el beneficio de la autoridad, la autenticidad de la emisión y el ciclo de vida de un activo digital. La emisión de éstos sobre Algorand proporcionan mayor velocidad, bajo costo y fácil trazabilidad para las partes involucradas con el mismo.

Solución

Les traemos a Channel Manager, como un medio para emitir y digitalizar NFT, brindar una solución de custodia al mapear a cada usuario a un número de teléfono móvil, realizar transacciones comerciales como Comprar, Vender, Revender y Enviar. Para la solución de custodia, utilizamos Hashicorp Vault, que brinda administración de secretos en Amazon Web Service.

La funcionalidad del administrador de canales se puede descentralizar creando un protocolo de gobernanza y dando control a múltiples autoridades según el caso de uso. En este ejemplo, hemos creado un administrador de canales para el sistema de emisión de boletos, donde múltiples mercados pueden conectarse al administrador de canales, digitalizarlos como una NFT y permitir el comercio utilizando estos tickets digitales en la parte superior de la cadena de bloques de Algorand.

Por ejemplo, si un organizador de eventos desea albergar el concierto de Katy Perry en Reino Unido y quiere vender las entradas en múltiples mercados como Ticketmaster, Eventbrite (entre otras plataformas locales) ¿Cómo haría? 

El organizador de eventos puede conectarse al administrador de canales y emitir los boletos como NFT y el mercado puede hacer uso del mismo para realizar transacciones comerciales según las condiciones establecidas por el organizador. De esta forma, se puede incorporar interoperabilidad y transparencia al sistema. La reventa o el envío de tickets puede ser posible con el bajo costo y la escalabilidad de Algorand, resolviendo el problema de la promoción y la autenticidad de los boletos.

Blockchain puede tener inmensas ventajas para los consumidores, sin embargo, la facilidad de uso es la cuestión clave. En éste mapeo se almacena en la bóveda de Hashicorp, aprovechando la seguridad de un módulo de seguridad basado en software. Channel Manager tiene la capacidad de proporcionar diferentes mercados con la facilidad de mapear la dirección de Algorand de su usuario con un número de móvil comprensible para humanos, y transferir a sus usuarios a blockchain con facilidad.

func saveAddress(ctx context.Context, v vault.Vault, algo algorand.Algo, 
         addressPath string) error {
    a, err := algo.GenerateAccount()
    if err != nil {
        return fmt.Errorf("saveAddress: error generating address: 
                  %w", err)
    }

path := fmt.Sprintf("%s/%s", v.UserPath, addressPath)
data := map[string]interface{}{
        constants.AccountAddress:     a.AccountAddress,
        constants.PrivateKey:         a.PrivateKey,
        constants.SecurityPassphrase: a.SecurityPassphrase,
    }
    _, err = v.Logical().Write(path, data)
    if err != nil {
    return fmt.Errorf("saveAddress: unable to write to vault: 
                  %w", err)
   }
err = algo.Send(ctx, a, 5)
    if err != nil {
        return fmt.Errorf("saveAddress: error sending algos 
                  to: %+v: err: %w", a, err)
    }
    return nil
}
func (u *User) userAddress(addressPath string) (*algorand.Account,
                        bool, error) {
    path := fmt.Sprintf("%s/%s", u.Vault.UserPath, addressPath)
    secret, err := u.Vault.Logical().Read(path)
    if err != nil {
        return nil, false, fmt.Errorf("userAddress: could not 
                          get account of user: %d", addressPath)
    }    accountAddress, accountAddressOK := secret.Data[constants.AccountAddress]
    if !accountAddressOK {
        return nil, false, fmt.Errorf("userAddress: account address not found")
    }
    privateKey, privateKeyOK := secret.Data[constants.PrivateKey]
    if !privateKeyOK {
        return nil, false, fmt.Errorf("userAddress: private key not found")
    }
    securityPassphrase, securityPassphraseOK := 
                          secret.Data[constants.SecurityPassphrase]
    if !securityPassphraseOK {
        return nil, false, fmt.Errorf("userAddress: security 
                          passphrase not found")
    }    ua := algorand.Account{
        AccountAddress:     accountAddress.(string),
        PrivateKey:         privateKey.(string),
        SecurityPassphrase: securityPassphrase.(string),
    }
    return &ua, true, nil
}

Algorand proporciona varios soportes de SDK para generar direcciones y realizar diversas operaciones en la cadena de bloques.

type algo struct {    from         *Account
    apiAddress   string
    apiKey       string
    amountFactor uint64
    minFee       uint64
    seedAlgo     uint64
}
func (a *algo) GenerateAccount() (*Account, error) {    account := crypto.GenerateAccount()
    paraphrase, err := mnemonic.FromPrivateKey(account.PrivateKey)
    if err != nil {
        return nil, fmt.Errorf("generateAccount: error generating account: %w", err)
    }  return &Account{
        AccountAddress:     account.Address.String(),
        PrivateKey:         string(account.PrivateKey),
        SecurityPassphrase: paraphrase,
    }, nil
}

Digitalización

Las NFT se representan como un activo estándar de Algorand con el parámetro de emisión establecido como 1. Esto crea de forma única una NFT y aquí está nuestra estructura para representar.

func (a *algo) CreateNFT(ctx context.Context, ac *Account) {
    var headers []*algod.Header
    headers = append(headers, &algod.Header{Key: "X-API-Key", Value: a.apiKey})
    algodClient, err := algod.MakeClientWithHeaders(a.apiAddress, "", headers)
    if err != nil {
        return 0, fmt.Errorf("createAsset: error connecting to algo: %w", err)
    }
    txParams, err := algodClient.SuggestedParams()
    if err != nil {
        return 0, fmt.Errorf("createAsset: error getting suggested tx params: %w", err)
    }
    genID := txParams.GenesisID    genHash := txParams.GenesisHash
    firstValidRound := txParams.LastRound
    lastValidRound := firstValidRound + 1000
    // Create an asset    // Set parameters for asset creation transaction
    creator := ac.AccountAddress
    assetName := "NAME"
    unitName := "tickets"
    assetURL := "URL"
    assetMetadataHash := "thisIsSomeLength32HashCommitment"
    defaultFrozen := false
    decimals := uint32(0)
    totalIssuance := uint64(1)  //unique NFTs
    manager := a.from.AccountAddress
    reserve := a.from.AccountAddress
    freeze := ""    clawback := a.from.AccountAddress
    note := []byte(nil)
    txn, err := transaction.MakeAssetCreateTxn(creator, a.minFee, firstValidRound,                                              lastValidRound, note,
        genID, base64.StdEncoding.EncodeToString(genHash), totalIssuance, decimals,
                                             defaultFrozen, manager, reserve, freeze, clawback,
        unitName, assetName, assetURL, assetMetadataHash)
    if err != nil {
        return 0, fmt.Errorf("createAsset: failed to make asset: %w", err)
    }
    fmt.Printf("Asset created AssetName: %s\n", txn.AssetConfigTxnFields.AssetParams.AssetName)

    privateKey, err := mnemonic.ToPrivateKey(ac.SecurityPassphrase)
    if err != nil {
        return 0, fmt.Errorf("createAsset: error getting private key from mnemonic: %w", err)
    }    txid, stx, err := crypto.SignTransaction(privateKey, txn)    if err != nil {
        return 0, fmt.Errorf("createAsset: failed to sign transaction: %w", err)
    }
    logger.Infof(ctx, "Signed txid: %s", txid)
    // Broadcast the transaction to the network
    txHeaders := append([]*algod.Header{}, &algod.Header{Key: "Content-Type", Value: "application/x-binary"})
    sendResponse, err := algodClient.SendRawTransaction(stx, txHeaders...)
    if err != nil {
        return 0, fmt.Errorf("createAsset: failed to send transaction: %w", err)
    }
}

Las operaciones comerciales como comprar, vender, revender y reenviar se realizan utilizando varios mecanismos de transacción utilizando la API de Purestake para conectarse a la cadena de bloques Algorand.

func (a *algo) Send(ctx context.Context, to *Account, noOfAlgos uint64) error {
    var headers []*algod.Header
    headers = append(headers, &algod.Header{Key: "X-API-Key", Value: a.apiKey})
    algodClient, err := algod.MakeClientWithHeaders(a.apiAddress, "", headers)
    if err != nil {
        return fmt.Errorf("send: error connecting to algo: %w", err)
    }

    txParams, err := algodClient.SuggestedParams()
    if err != nil {
        return fmt.Errorf("send: error getting suggested tx params: %w", err)
    }

    fromAddr := a.from.AccountAddress
    toAddr := to.AccountAddress
    amount := noOfAlgos * a.amountFactor
    note := []byte(fmt.Sprintf("Transferring %d algos from %s", a.seedAlgo, a.from))
    genID := txParams.GenesisID
    genHash := txParams.GenesisHash
    firstValidRound := txParams.LastRound
    lastValidRound := firstValidRound + 1000

    txn, err := transaction.MakePaymentTxnWithFlatFee(fromAddr, toAddr,
                                                    a.minFee, amount, firstValidRound,
                                                    lastValidRound, note, "", genID, genHash)
    if err != nil {
        return fmt.Errorf("send: error creating transaction: %w", err)
    }

    privateKey, err := mnemonic.ToPrivateKey(a.from.SecurityPassphrase)
    if err != nil {
        return fmt.Errorf("send: error getting private key from mnemonic: %w", err)
    }

    txId, bytes, err := crypto.SignTransaction(privateKey, txn)
    if err != nil {
        return fmt.Errorf("send: failed to sign transaction: %w", err)
    }
    logger.Infof(ctx, "Signed txid: %s", txId)

    txHeaders := append([]*algod.Header{}, &algod.Header{Key: "Content-Type", Value: "application/x-binary"})
    sendResponse, err := algodClient.SendRawTransaction(bytes, txHeaders...)
    if err != nil {
        return fmt.Errorf("send: failed to send transaction: %w", err)
    }
    logger.Infof(ctx, "send: submitted transaction %s", sendResponse.TxID)

    return nil }

Demostración

Aquí está la demostración de una aplicación de eventos que se conecta al administrador de canales y resuelve los problemas relacionados con el mundo de la venta de entradas.

La aplicación proporciona varias implementaciones de casos de uso en Java y Kotlin. La funcionalidad de cambio de clave y las funciones sin custodia también se implementan como parte del código en la aplicación de Android.

Ver video: https://youtu.be/kDnyFdpeyNs

Referencias

Aplicación de Android: https://play.google.com/store/apps/details?id=com.eventersapp.marketplace

Channel Manager Code (en Golang): https://github.com/eventers/Eventers-Marketplace-Backend

Android Código (en Kotlin + Java): https://github.com/eventers/Eventers-Marketplace-Android


Este artículo ha sido escrito originalmente por Akshay Kant en   «Soluciones» del portal para desarrolladores de Algorand y traducido por AlgoLatam.

Original Article: https://developer.algorand.org/solutions/channel-manager-for-nfts/

Deja una respuesta

Tu dirección de correo electrónico no será publicada.