Criar e Implantar Contrato Inteligente NFT

Nesta seção, você aprenderá a criar e implantar um contrato inteligente NFT que é necessário para configurar o NFT Marketplace.

Use o Blockchain App Builder para gerar um contrato inteligente para gerenciar minting, propriedade e transferência de NFTs. O capacitador principal dessa solução é a capacidade de mint e transferir NFTs usando o Oracle Blockchain Platform.

Primeiro você cria um arquivo de especificação NFT e, em seguida, implanta o contrato inteligente em uma instância do Oracle Blockchain Platform. Em seguida, você pode testar o contrato inteligente.

Instalar o Blockchain App Builder

Depois de provisionar uma instância do Oracle Blockchain Platform, conclua as seguintes etapas:

  1. Na console do Oracle Blockchain Platform, abra a guia Ferramentas do Desenvolvedor e selecione o painel Blockchain App Builder.
  2. Na seção Fazer Download, faça download do arquivo de ferramentas da interface de linha de comando (CLI) ou da extensão de Código do Visual Studio (VS) e, em seguida, configure-o localmente.

Personalizar o Arquivo de Especificação NFT de Amostra

Um exemplo de arquivo de especificação NFT está incluído no Blockchain App Builder. Você pode usá-lo e adaptá-lo às suas necessidades, como mostrado no seguinte exemplo:


 assets:
    - name: ArtCollection
      type: token
      symbol: ART      #mandatory
      standard: erc721+   
      anatomy:
        type: nonfungible
        unit: whole
      behavior:
        - indivisible      # mandatory
        - singleton        # mandatory
        - mintable:        # mandatory
            max_min_quantity: 20000
        - transferable
        - burnable
        - roles:
            minter_role_name: minter
      properties:
        - name: price
          type: number
        - name: on_sale_flag
          type: boolean
      metadata:
        - name: painting_name
          type: string
        - name: description
          type: string
        - name: image
          type: string
        - name: painter_name
          type: string
 customMethods:
    - executeQuery
    - "createAccountByConsumers(org_id: string, user_id: string, token_type: string)" # Create accounts for consumers while signing up
    - "sell(token_id: string, selling_price: number)" # Post the token for selling in the marketplace
    - "buyWithTokens(from_org_id: string, from_user_id: string, to_org_id: string, to_user_id: string, nonfungible_token_id: string, fungible_token_id: string, amount_paid: number)"  # Buy the NFT after paying the using FT Tokens 
    - "buyWithDirectPayment(from_org_id: string, from_user_id: string, to_org_id: string, to_user_id: string, nonfungible_token_id: string, amount_paid: number)"  # Buy the NFT after paying the amount using payment gateway

Você pode adicionar propriedades e métodos personalizados para estender esta especificação para chaincode NFT. Os nós de cadeia NFT gerados pelo Blockchain App Builder são baseados no padrão ERC-721.

Para gerar o chaincode (contrato inteligente) usando a extensão Visual Studio Code, conclua as seguintes etapas:

  1. Na seção Chaincodes, clique no ícone +. O painel Detalhes do Codificação é aberto.
  2. Preencha os campos obrigatórios para gerar o projeto chaincode:
    • Informe um nome para o projeto chaincode.
    • Selecione TypeScript ou Go como o idioma no qual os métodos de chaincode serão gerados.
    • Selecione o arquivo de especificação de entrada criado anteriormente.
    • Insira o local onde deseja que o projeto seja gerado.
  3. Clique em Criar.

A captura de tela a seguir da IU mostra a janela Criar Chaincode.

Veja a seguir a descrição da ilustração blockchain_chaincode_details.png
Descrição da ilustração blockchain_chaincode_details.png
Depois que o chaincode for gerado, você poderá revisar a implementação do modelo e do controlador inspecionando os arquivos no diretório src na hierarquia do projeto.
  • O arquivo de modelo contém todas as estruturas de dados geradas que representam o token, as contas etc.
  • O arquivo do controlador contém todos os métodos de ciclo de vida NFT gerados e funções de suporte com a lógica de validação necessária com base na especificação.
O arquivo do controlador também tem modelos de função para métodos personalizados, onde você pode adicionar sua lógica de negócios com base nas funções de SDK geradas.

/**
*      
* BDB sql rich queries can be executed in OBP CS/EE.
* This method can be invoked only when connected to remote OBP CS/EE network.
*    
*/
@Validator(yup.string())
    public async executeQuery(query: string) {
        const result = await this.query(query);
        return result;
    }
@Validator(yup.string(), yup.string(), yup.string())
          public async createAccountByConsumers(org_id: string, user_id: string, token_type: string) {       
          //await this.Ctx.ERC721Auth.checkAuthorization('ERC721ACCOUNT.createAccount', 'TOKEN');
          return await this.Ctx.ERC721Account.createAccount(org_id, user_id, token_type);   
    }
@Validator(yup.string(), yup.number())
public async sell (token_id: string, selling_price: number) {       
          try {  
            const token = await this.Ctx.ERC721Token.get(token_id);
            const t = new ArtCollection(token)
            t.price =  selling_price;
            t.on_sale_flag = true;
            //console.log(token);           
            await this.Ctx.ERC721Token.update(t);
            return `Token ID : '${token_id}' has been posted for selling in the marketplace'`;           
          } catch(error) {
                throw new Error(error.message);
        }
}
@Validator(yup.string(), yup.string(), yup.string(), yup.string(), yup.string(), yup.string(), yup.number())
public async buyWithTokens(from_org_id: string, from_user_id: string, to_org_id: string, to_user_id: string, nonfungible_token_id: string, fungible_token_id: string, amount_paid: number) {
        try {  
            const token = await this.Ctx.ERC721Token.get(nonfungible_token_id);
            const t = new ArtCollection(token);
            const oChainUtil = new OChainUtils(this.Ctx.Stub);
            var msg = `Token ID : '${nonfungible_token_id}' had not been transferred'`;
            if (t.on_sale_flag==true) {
                if(t.price == amount_paid) {                   
                     // @ts-ignore
                    await oChainUtil.invokeChaincode("LoyaltyToken7", "transferTokens", [fungible_token_id, from_org_id, from_user_id, amount_paid], "marketplace");
                    const from_account_id = await this.Ctx.ERC721Account.generateAccountId(from_org_id, from_user_id);                   
                    const to_account_id = await this.Ctx.ERC721Account.generateAccountId(to_org_id, to_user_id);         
                    await this.Ctx.ERC721Token.transferFrom(from_account_id, to_account_id, t);
     
                    msg = `Token ID : '${nonfungible_token_id}' has been successfully transferred to UserID : '${to_user_id}'`;           
              }           
            }
            else {
                msg = `Token ID : '${nonfungible_token_id}' has not been transferred to UserID : '${to_user_id}' as the amount was not fully paid'`;
            }
            return msg;
       } catch(error)
          {
            throw new Error(error.message);
         }
}
@Validator(yup.string(), yup.string(), yup.string(), yup.string(), yup.string(), yup.number())
    public async buyWithDirectPayment(from_org_id: string, from_user_id: string, to_org_id: string, to_user_id: string, nonfungible_token_id: string, amount_paid: number) {
         try {  
             const token = await this.Ctx.ERC721Token.get(nonfungible_token_id);
             const t = new ArtCollection(token);
             var msg = `Token ID : '${nonfungible_token_id}' had not been transferred'`;           
           if (t.on_sale_flag==true) {
                 if(t.price == amount_paid) {                   
                 const from_account_id = await this.Ctx.ERC721Account.generateAccountId(from_org_id, from_user_id);         
                 const to_account_id = await this.Ctx.ERC721Account.generateAccountId(to_org_id, to_user_id);                   
                 await this.Ctx.ERC721Token.transferFrom(from_account_id, to_account_id, t);
                   
                 msg = `Token ID : '${nonfungible_token_id}' has been successfully transferred to UserID : '${to_user_id}'`;
                 }
             }
             else {
                 msg = `Token ID : '${nonfungible_token_id}' has not been transferred to UserID : '${to_user_id}' as the amount was not fully paid'`;
             }
             return msg;
         } catch(error) {         
             throw new Error(error.message);
         }
      }
}

Implantar o Contrato Inteligente

Depois de criar um projeto de chaincode, você pode implantá-lo localmente.

No painel Detalhes de Codificação, selecione Implantar para abrir o assistente de implantação. O Blockchain App Builder inclui uma rede blockchain local, que é executada em contêineres Docker, que você pode usar para fins de teste.

Você também pode implantar o chaincode em uma instância do Oracle Blockchain Platform selecionando o perfil de conexão na lista para o ambiente de destino. Você também deve concluir e salvar os Parâmetros de Entrada, porque o chaincode NFT gerado requer os parâmetros orgId e userId para inicialização. Os parâmetros orgId e userId são usados para especificar quais usuários têm privilégios de administrador de token.

Testar o Contrato Inteligente

Quando o chaincode é implantado, o Oracle Blockchain Platform expõe automaticamente as APIs REST para inicialização de token, gerenciamento de conta e atribuição, e métodos de ciclo de vida NFT (criar, transferir, queimar).

Você pode testar selecionando Executar no painel Detalhes do Codificação no Construtor de Aplicativos Blockchain ou usando um cliente de API REST como Postman.

A captura de tela a seguir da IU mostra a guia Executar da janela Criar Chaincode.

Veja a seguir a descrição da ilustração blockchain_chaincode_execute.png
Descrição da ilustração blockchain_chaincode_execute.png

Para chamar métodos de contrato inteligente do Oracle Blockchain Platform usando a API REST, use o método POST e especifique o URL, que consiste em duas partes concatenadas juntas. A primeira parte é o ponto final do proxy REST no Oracle Blockchain Platform, que você pode obter na guia Nós da console do Oracle Blockchain Platform.

A segunda parte é o URI específico para chamar a transação usando a API Transaction. Consulte a documentação para enviar uma solicitação POST. As duas partes formam um URL completo semelhante ao aqui.

https://oabcs1-iad.blockchain.ocp.oraclecloud.com:7443/restproxy/api/v2/channels/{channelName}/transactions

Substitua {channelName} pelo nome do canal especificado ao implantar seu chaincode, como marketplace. Ao construir a solicitação de API, conclua as seguintes etapas:

  • Defina a autorização para usar Basic Auth com o userid and password especificado que está mapeado para a atribuição REST_Client. Você também pode usar tokens OAuth2. Consulte Usar Autenticação Baseada em Token de Acesso OAuth 2.0 no guia da API REST para obter mais informações.
  • Defina o cabeçalho Content-Type como application/json.
  • No corpo da solicitação, inclua os parâmetros necessários para a chamada da transação, incluindo o nome do chaincode e o método create<TokenName>Token junto com os argumentos necessários.

Com base no chaincode gerado pelo modelo yaml, o texto a seguir mostra um corpo de solicitação de amostra e a resposta associada.

Solicitação

{
    "chaincode": "{{NFTChaincode}}",
    "args": [
        "createArtCollectionToken",
        "{\"token_id\":\"{{NFTTokenID}}\",\"token_uri\":\"https://ipfs.io/ipfs/QmV68aiT7xw2WX8pmDbeTWpGP2or35NUFan9RagymsLpgV?filename=ArtCollection_NFT1.json\",\"metadata\":{\"painting_name\":\"Oracle - Red Bull Partnership\",\"image\":\"https://ipfs.io/ipfs/QmVap6Gkh3Cp9DiLLWvkvJHpuXpFmYB2GzU1caM57gNcAa?filename=Oracle_RedBull_NFT1.jpeg\",\"painter\":\"Alex\"},\"price\":200,\"on_sale_flag\":false}"
    ],
    "timeout": 0,
    "sync": true
}

Resposta

{
    "returnCode": "Success",
    "error": "",
    "result": {
        "txid": "c999922f04c3011bf25ca43624e4bb23e8900634f8e23a1648170a90274a9733",
        "payload": {
            "metadata": {
                "painter": "Alex",
                "painting_name": "Oracle - Red Bull Partnership",
                "image": "https://ipfs.io/ipfs/QmVap6Gkh3Cp9DiLLWvkvJHpuXpFmYB2GzU1caM57gNcAa?filename=Oracle_RedBull_NFT1.jpeg"
            },
            "assetType": "otoken",
            "created_by": "oaccount~eadf1b0ae857164f8681d1742b6328089a7d33ebec76d8248cb909da7a84f42a",
            "creation_date": "2022-04-28T12:08:38.000Z",
            "owner": "oaccount~eadf1b0ae857164f8681d1742b6328089a7d33ebec76d8248cb909da7a84f42a",
            "uri": "https://ipfs.io/ipfs/QmV68aiT7xw2WX8pmDbeTWpGP2or35NUFan9RagymsLpgV?filename=ArtCollection_NFT1.json",
            "is_burned": false,
            "token_id": "NFT17",
            "token_name": "artcollection",
            "symbol": "ART",
            "token_standard": "erc721+",
            "token_type": "nonfungible",
            "token_unit": "whole",
            "behaviors": [
                "indivisible",
                "singleton",
                "mintable",
                "transferable",
                "burnable",
                "roles"
            ],
            "roles": {
                "minter_role_name": "minter"
            },
            "mintable": {
                "max_mint_quantity": 20000
            },
            "token_uri": "https://ipfs.io/ipfs/QmV68aiT7xw2WX8pmDbeTWpGP2or35NUFan9RagymsLpgV?filename=ArtCollection_NFT1.json",
            "price": 200,
            "on_sale_flag": false
        },

Consulte a seção Explorar Mais para obter mais informações sobre os métodos disponíveis para gerenciar códigos de cadeia NFT, incluindo os métodos Golang ou TypeScript e Platform REST API.

Você pode chamar as APIs diretamente do webhook do Oracle Content Management e do aplicativo web criado no Visual Builder Cloud Service (VBCS), ou remapear e encapsulá-las com o gateway de API para uso por um aplicativo web do marketplace hospedado externamente. Observe que quando você cria (mint) um NFT, um dos parâmetros que deve ser fornecido é token_uri, que pode ser um IPFS URI apontando para um arquivo JSON que representa o objeto digital.

https://ipfs.io/ipfs/QmV68aiT7xw2WX8pmDbeTWpGP2or35NUFan9RagymsLpgV?filename=ArtCollection_NFT1.json

As etapas a seguir descrevem um exemplo de geração do URI do token usando o IPFS Desktop:

  1. Faça upload de um arquivo de imagem em IPFS e salve o URI da imagem.
  2. Crie e faça upload do arquivo JSON que inclua o URI da imagem junto com os campos de metadados relevantes.
  3. Compartilhe e copie o link para o arquivo JSON sob o ... Mais lista.

{ 
    "painting_name": "Oracle - Red Bull Partnership",
    "description": "Cloud Partnership",
    "image": "https://ipfs.io/ipfs/QmVap6Gkh3Cp9DiLLWvkvJHpuXpFmYB2GzU1caM57gNcAa?filename=Oracle_RedBull.jpeg",
    "painter_name": "Alex"
}

Use o URI que aponta para o arquivo JSON para o parâmetro token_uri.

Você também pode incluir metadados relevantes nos atributos passados para o método create<Token-Name>Token, que manterão os metadados no razão do Oracle Blockchain Platform para fácil recuperação.

Para NFTs criados usando o OCM (Oracle Content Management), use o webhook do OCM para chamar o Oracle Functions usando um Gateway de API e colocar a chamada de API REST no Oracle Functions conforme mostrado no diagrama de arquitetura. Você também pode chamar APIs REST do Blockchain diretamente do aplicativo web marketplace ou usando um mapeamento de gateway de API para suportar conteúdo gerado pelo usuário. A funcionalidade de negociação do Marketplace pode chamar os métodos personalizados para tratar o pagamento e, em seguida, acionar uma transferência da propriedade do NFT, como os métodos de amostra buyWithTokens e buyWithDirectPayment.