Урок 2

Criando o seu Primeiro Token com FA1.2 Standard

Nesta lição, vamos passar pelo processo de criação de um token fungível usando o padrão FA1.2 no Tezos. Usaremos o IDE online SmartPy para escrever e implementar o nosso contrato inteligente. Tenha em mente que o padrão FA1.2 é usado principalmente para tokens fungíveis, o que significa tokens que têm propriedades idênticas e podem ser trocados um por um.

Guia passo a passo

  1. Aceder ao SmartPy IDE

  2. Primeiro, abra o IDE online SmartPy em https://smartpy.io/ide/. Esta é a plataforma que usaremos para escrever, testar e implementar os nossos contratos inteligentes.

  3. Iniciar o modelo FA1.2

  4. Clique em “Modelos por Tipo” na barra lateral esquerda e seleccione “FA1.2”. Um novo separador será aberto com o modelo de contrato FA1.2. Este é um contrato pronto a usar que segue o padrão FA1.2.

  5. Compreender o modelo FA1.2

  6. Este modelo tem a funcionalidade básica para um token fungível, que inclui a transferência de tokens, a aprovação de transferências, a verificação de saldos e a visualização da oferta total de tokens. O contrato também inclui funcionalidades adicionais para cunhar e queimar tokens, bem como para gestão de governança.

  7. Estude este modelo e certifique-se de que compreende as suas funcionalidades. Está tudo bem se não perceber tudo neste momento, mas tente ter uma noção geral das operações que este contrato pode realizar.
    Por exemplo, pode copiar o código do modelo no IDE SmartPy ou aqui abaixo:

Python
# Ativos Fungíveis - FA12
# Inspirado em https://gitlab.com/tzip/tzip/blob/master/A/FA1.2.md

importar de forma inteligente como sp


# Os metadados abaixo são apenas um exemplo, servem de base,
# o conteúdo é usado para construir os metadados JSON que os utilizadores
# pode copiar e fazer upload para o IPFS.
TZIP16_Metadata_Base = {
    "name": "SmartPy FA1.2 Token Template",
    "description": "Example Template for an FA1.2 Contract from SmartPy",
    "authors": ["SmartPy Dev Team"],
    "homepage": "https://smartpy.io",
    "interfaces": ["TZIP-007-2021-04-17", "TZIP-016-2021-04-17"],
}


@sp .módulo
def m ():
 classe AdminInterface (SP.Contract):
 @sp .private ("with_storage= apenas leitura) "
 def é_administrador_ (próprio, remetente):
 sp.cast (sp.remetente, sp.endereço)
 """Não é normal, pode ser redefinido através de herança. """
            retorno Verdadeiro

 classe CommonInterface (AdminInterface):
 def __init__(self):
 Interface de administrador.__init__(auto)
            self.data.balances = sp.cast (
                sp.big_map (),
 sp.big_map [
 sp.endereço,
 sp.record (aprovações=sp.map [sp.address, sp.nat], saldo = sp.nat),
                ],
 )
 self.data.total_supply = 0
 self.data.token_metadata = sp.cast (
                sp.big_map (),
 sp.big_map [
 sp.nat,
 sp.record (token_id=sp.nat, token_info=sp.map [sp.string, sp.bytes]),
 ],
 )
 self.data.metadata = sp.cast (
                sp.big_map (),
 sp.big_map [sp.string, sp.bytes],
 )
 self.data.balances = sp.cast (
                sp.big_map (),
 sp.big_map [
 sp.endereço,
 sp.record (aprovações=sp.map [sp.address, sp.nat], saldo = sp.nat),
                ],
 )
 self.data.total_supply = 0
 self.data.token_metadata = sp.cast (
                sp.big_map (),
 sp.big_map [
 sp.nat,
 sp.record (token_id=sp.nat, token_info=sp.map [sp.string, sp.bytes]),
 ],
 )
 self.data.metadata = sp.cast (
                sp.big_map (),
 sp.big_map [sp.string, sp.bytes],
 )

 @sp .private ("with_storage= apenas leitura) "
 def é_pausada_ (self):
 """Não é normal, pode ser redefinido através de herança. """
            devolver Falso

 classe Fa1_2 (CommonInterface):
 def __init__(self, metadados, livro-razão, token_metadata):
 """
 especificação de token_metadata: https://gitlab.com/tzip/tzip/-/blob/master/proposals/tzip-12/tzip-12.md#token-metadata
            Os metadados específicos do Token são armazenados/apresentados como um valor de Michelson do tipo (bytes da string do mapa).
            Algumas das chaves estão reservadas e predefinidas:

 - "": Deve corresponder a um URI TZIP-016 que aponta para uma representação JSON dos metadados do token.
            - " nome": Deve ser uma string UTF-8 dando um nome de " exibição " ao token.
            - " símbolo": Deve ser uma string UTF-8 para o identificador curto do token (ex. XTZ, EUR,...).
 - " decimais": Deve ser um número inteiro (convertido numa string UTF-8 em decimal)
 que define a posição do ponto decimal nos saldos de token para fins de exibição.

            especificação contract_metadata: https://gitlab.com/tzip/tzip/-/blob/master/proposals/tzip-16/tzip-16.md
 """
 Interface comum.__init__(auto)
            self.data.metadata = metadados
 self.data.token_metadata = sp.big_map (
                {0: sp.record(token_id=0, token_info=token_metadata)}
            )

 para o proprietário em ledger.items ():
                self.data.balances [owner.key] = proprietário.value
 self.data.total_supply += proprietário.value.balance

 # TODO: Ativar quando esta funcionalidade for implementada.
            # self.init_metadata (metadados, " " metadados)

 @sp .ponto de entrada
 transferência def (self, param):
 sp.cast (
 param,
 sp.record (de_=sp.address, para_=sp.endereço, value=sp.nat) .layout (
                    ("de_ a partir de", ("a_ quanto a", " valor"))
 ),
 )
 balance_from = self.data.balances.get (
                param.de_, padrão = sp.record (saldo = 0, aprovações= {})
 )
 balance_to = self.data.balances.get (
                param.to_, padrão = sp.record (saldo = 0, aprovações= {})
 )
 saldo de.balance = sp.as_nat (
                balance_from.balance - param.value, erro = FA1.2_Saldo insuficiente " "
 )
 saldo para.saldo += param.valor
 se não auto.is_administrator_ (sp.sender):
                afirme não a si mesmo. is_paused_ (), "FA1.2_Pausado "
                se param.de_ ! = sp.remetente:
 saldo de.aprovações [sp.sender] = sp.as_nat (
                        balance_from.aprovações [sp.sender] - param.value,
                        erro = " FA1.2_Não permitido, "
 )
 self.data.balances [param.from_] = saldo_de
 self.data.balances [param.to_] = saldo_para

 @sp .ponto de entrada
 aprovar def (self, param):
 sp.cast (
 param,
 sp.record (spender=sp.address, value=sp.nat) .layout (
                    ("gastador", " valor")
 ),
 )
 afirme não a si mesmo. is_paused_ (), "FA1.2_Pausado "
            spender_balance = self.data.balances.get (
                sp.remetente, predefinição=sp.record (saldo = 0, aprovações= {})
 )
 Já aprovado = spender_balance.aprovados.get (param.spender, padrão = 0)
 afirmar (
 Já aprovado == 0 ou param.value == 0
 ), " FA1.2 _Alteração de subsídio não seguro "
            spender_balance.aprovações [param.spender] = parâmetro. valor
 self.data.balances [sp.sender] = saldo_gastador_saldo

 @sp .ponto de entrada
 def GetBalance (self, param):
 (endereço, chamada de retorno) = param
 resultado = self.data.balances.get (
                endereço, default=sp.record (saldo = 0, aprovações= {})
 ) .saldo
 sp.transfer (resultado, sp.tez (0), retorno de chamada)

 @sp .ponto de entrada
 def getAllovenance (self, param):
 (args, callback) = param
 resultado = self.data.balances.get (
                args.proprietário, padrão = sp.record (saldo = 0, aprovações= {})
 ) .aprovações.get (args.spender, padrão = 0)
 sp.transfer (resultado, sp.tez (0), retorno de chamada)

 @sp .ponto de entrada
 def getTotalSupply (self, param):
 sp.cast (param, sp.pair [sp.unit, sp.contrato [sp.nat]])
            sp.transfer (self.data.total_supply, sp.tez (0), sp.and (param))

 @sp .offchain_view ()
 def token_metadata (self, token_id):
 """Devolver o URI dos metadados do token fornecido. (token_id deve ser 0). """
            sp.cast (token_id, sp.nat)
 devolver self.data.token_metadata [token_id]

    ##########
 # Misturas #
 ##################

    class Admin(sp.Contract):
        def __init__(self, administrator):
            self.data.administrator = administrator

        @sp.private(with_storage="read-only")
        def is_administrator_(self, sender):
            return sender == self.data.administrator

        @sp.entrypoint
        def setAdministrator(self, params):
            sp.cast(params, sp.address)
            assert self.is_administrator_(sp.sender), "Fa1.2_NotAdmin"
            self.data.administrator = params

        @sp.entrypoint()
        def getAdministrator(self, param):
            sp.cast(param, sp.pair[sp.unit, sp.contract[sp.address]])
            sp.transfer(self.data.administrator, sp.tez(0), sp.snd(param))

        @sp.onchain_view()
        def get_administrator(self):
            return self.data.administrator

    class Pause(AdminInterface):
        def __init__(self):
            AdminInterface.__init__(self)
            self.data.paused = False

        @sp.private(with_storage="read-only")
        def is_paused_(self):
            return self.data.paused

        @sp.entrypoint
        def setPause(self, param):
            sp.cast(param, sp.bool)
            assert self.is_administrator_(sp.sender), "Fa1.2_NotAdmin"
            self.data.paused = param

    class Mint(CommonInterface):
        def __init__(self):
            CommonInterface.__init__(self)

        @sp.entrypoint
        def mint(self, param):
            sp.cast(param, sp.record(address=sp.address, value=sp.nat))
            assert self.is_administrator_(sp.sender), "Fa1.2_NotAdmin"
            receiver_balance = self.data.balances.get(
                param.address, default=sp.record(balance=0, approvals={})
            )
            receiver_balance.balance += param.value
            self.data.balances[param.address] = receiver_balance
            self.data.total_supply += param.value

    class Burn(CommonInterface):
        def __init__(self):
            CommonInterface.__init__(self)

        @sp.entrypoint
        def burn(self, param):
            sp.cast(param, sp.record(address=sp.address, value=sp.nat))
            assert self.is_administrator_(sp.sender), "Fa1.2_NotAdmin"
            receiver_balance = self.data.balances.get(
                param.address, default=sp.record(balance=0, approvals={})
            )
            receiver_balance.balance = sp.as_nat(
                receiver_balance.balance - param.value,
                error="FA1.2_InsufficientBalance",
            )
            self.data.balances[param.address] = receiver_balance
            self.data.total_supply = sp.as_nat(self.data.total_supply - param.value)

    class ChangeMetadata(CommonInterface):
        def __init__(self):
            CommonInterface.__init__(self)

        @sp.entrypoint
        def update_metadata(self, key, value):
            """An entrypoint to allow the contract metadata to be updated."""
            assert self.is_administrator_(sp.sender), "Fa1.2_NotAdmin"
            self.data.metadata[key] = value

    ### Testes #
 ##########

 classe FA1_2TestFull (Admin, Pausa, Fa1_2, Mint, Burn, ChangeMetadata):
 def __init__(auto, administrador, metadados, livro-razão, token_metadata):
 Alterar metadados.__init__(auto)
            Queimar.__init__(eu)
            Hortelã.__init__(auto)
            Fa1_2.__init__(eu, metadados, livro-razão, token_metadata)
 Pausa.__init__(auto)
            Administrador.__init__(eu, administrador)

 classe Viewer_NAT (SP.Contract):
 def __init__(self):
 self.data.last = sp.cast (Nenhum, sp.option [sp.nat])

        @sp .ponto de entrada
 def alvo (self, parâmetros):
 self.data.last = Sp.Alguns (parâmetros)

 turma Viewer_address (SP.Contract):
 def __init__(self):
 self.data.last = sp.cast (Nenhum, sp.option [sp.address])

        @sp .ponto de entrada
 alvo def (self, parâmetros):
 self.data.last = SP.Alguns (parâmetros)


se " os modelos " não estiverem em __name__:

 @sp .add_test (nome = FA12) " "
 teste def ():
 sc = sp.test_scenario (m)
        sc.h1 (modelo " FA1.2 - Ativos fungíveis) "

 # sp.test_account gera ED25519 pares de chaves deterministicamente:
 admin = sp.test_account (Administrador) " "
        alice = sp.test_account (Alice) " "
        bob = sp.test_account (Robert) " "

        # Vamos exibir as contas:
 sc.h1 (Contas) " "
 sc.show ([admin, alice, bob])

 sc.h1 (Contrato) " "
 token_metadados = {
            "decimals": sp.utils.bytes_of_string("18"),  # Mandatory by the spec
            "name": sp.utils.bytes_of_string("My Great Token"),  # Recommended
            "symbol": sp.utils.bytes_of_string("MGT"),  # Recommended
            # Extra fields
            "icon": sp.utils.bytes_of_string(
                "https://smartpy.io/static/img/logo-only.svg"
            ),
        }
        contract_metadados = sp.utils.metadata_of_url (
            "ipfs: //qmaiaUJ1FFngYTU8RLBJC3een9CSkwaf8EGmbNdmHzpNfd "
 )

 c1 = m.FA1_2TestCompleto (
 administrator=admin.address,
 metadata=contract_metadata,
 token_metadata=token_metadata,
 livro-razão = {},
 )
 sc += c1

 sc.h1 (Vista " fora da cadeia - token_metadata) "
 sc.verify_equal (
 SP.View (c1, " " token_metadata) (0),
 sp.record (
 token_id=0,
 token_info=sp.map (
                    {
                        "decimals": sp.utils.bytes_of_string("18"),
                        "name": sp.utils.bytes_of_string("My Great Token"),
                        "symbol": sp.utils.bytes_of_string("MGT"),
                        "icon": sp.utils.bytes_of_string(
                            "https://smartpy.io/static/img/logo-only.svg"
                        ),
                    }
                ),
 ),
 )

 sc.h1 ("Tentativa de actualizar metadados) "
 sc.verificar (
 c1.data.metadados [] " "
            == sp.utils.bytes_of_string (
                "ipfs: //qmaiaUJ1FFngYTU8RLBJC3EEN9CSkwaf8EGmbNdmHzpNfd "
 )
 )
 c1.update_metadata (chave =, valor = sp.bytes (0x00)) .run (remetente = " " administrador) " "
 sc.verify (c1.data.metadata [] " " == sp.bytes (0x00)) " "

 sc.h1 (Pontos de entrada) " "
 sc.h2 (O " administrador cunha algumas moedas) "
 c1.mint (endereço=alice.address, value=12) .run (remetente=admin)
        c1.mint (endereço=alice.address, value=3) .run (remetente = admin)
        c1.mint (endereço=alice.address, value=3) .run (remetente = admin)
        sc.h2 ("Alice transfere para Bob) "
 c1.transfer (de_=alice.address, para_=bob.address, valor=4) .run (remetente = alice)
        sc.verify (c1.data.balances [alice.address] .balance == 14)
 sc.h2 ("Bob tenta transferir-se de Alice mas não tem a aprovação dela) "
 c1.transfer (de_=alice.address, para_=bob.address, valor = 4) .run (
            remetente = bob, válido = falso
 )
 sc.h2 ("Alice aprova transferências de Bob e Bob) "
 c1.approve (spender=bob.address, value=5) .run (remetente = alice)
        c1.transfer (de_=alice.address, para_=bob.address, valor=4) .run (remetente = bob)
        sc.h2 ("Bob tenta transferir em excesso de Alice) "
 c1.transfer (de_=alice.address, para_=bob.address, valor = 4) .run (
            remetente = bob, válido = falso
 )
 sc.h2 (O " administrador queima o token Bob) "
 c1.burn (endereço=bob.address, valor=1) .run (remetente = administrador)
        sc.verify (c1.data.balances [alice.address] .balance == 10)
 sc.h2 ("Alice tenta queimar o token Bob) "
 c1.burn (endereço=bob.address, value=1) .run (remetente = alice, Válido = falso)
 sc.h2 (O " administrador pausa o contrato e Alice não pode mais transferir) "
 c1.setPause (Verdadeiro) .run (remetente = admin)
        c1.transfer (de_=alice.address, para_=bob.address, valor = 4) .run (
            remetente = Alice, Válido = Falso
 )
 sc.verify (c1.data.balances [alice.address] .balance == 10)
 sc.h2 (O " administrador transfere enquanto está em pausa) "
 c1.transfer (de_=alice.address, para_=bob.address, valor=1) .run (remetente = administrador)
        sc.h2 (O " administrador anula o contrato e as transferências são permitidas) "
 c1.setPause (Falso) .run (remetente = admin)
        sc.verify (c1.data.balances [alice.address] .balance == 9)
 c1.transfer (de_=alice.address, para_=bob.address, valor = 1) .run (remetente = alice)

        sc.verify (c1.data.total_supply == 17)
 sc.verify (c1.data.balances [alice.address] .balance == 8)
 sc.verify (c1.data.balances [bob.address] .balance == 9)

 sc.h1 (Visualizações) " "
 sc.h2 (Equilíbrio) " "
 view_balance = m.Viewer_NAT ()
 sc += visão_equilíbrio
 alvo = sp.contract (SP.tnat, view_balance.address, "alvo") .open_some ()
 c1.GetBalance ((alice.address, alvo))
 sc.verify_equal (ver_balance.data.last, sp.alguns (8))

 sc.h2 (Administrador) " "
 visualizar_administrador = m.Viewer_address ()
 sc += visualizar_administrador
 alvo = sp.contrato (
            Endereço SP.T, view_administrator.address, "alvo "
 ) .open_some ()
 c1.GetAdministrator ((sp.unit, alvo))
 sc.verify_equal (ver_administrator.data.last, sp.some (admin.address))

        sc.h2 (Fornecimento Total) " "
 View_totalSupply = m.Viewer_NAT ()
 sc += Visualizar_TotalSupply
 alvo = sp.contract (SP.tnat, view_totalsupply.address, "alvo") .open_some ()
 c1.GetTotalSupply ((sp.unidade, destino))
 sc.verify_equal (View_totalsupply.data.last, sp.alguns (17))

 sc.h2 (Subsídio) " "
 view_allowance = m.Viewer_NAT ()
 sc += view_allowance
 alvo = sp.contract (sp.tnat, view_allowance.address, "alvo") .open_some ()
 c1.getAllance ((sp.record (owner=alice.address, spender=bob.address), alvo))
 sc.verify_equal (ver_allowance.data.last, sp.alguns (1))
  1. Execute o Contrato. Vai ver algo do género

O contrato FA1.2 original tem funcionalidades básicas como transferir tokens, aprovar transferências, verificar saldos e visualizar o fornecimento total de tokens. Agora vamos melhorar esta funcionalidade.

  • Admin: Introduziremos um contrato que permite a um administrador executar ações específicas, como pausar o contrato, e impede que outras contas usem essas funções.
  • Pausa: Esta funcionalidade permite-nos pausar e desfazer o contrato. Quando o contrato é pausado, ninguém pode usá-lo excepto o administrador.
  • Mint: Esta funcionalidade de contrato permite ao administrador criar novos tokens.
  • Queimar: Esta funcionalidade de contrato permite ao administrador destruir tokens.
  • ChangeMetadata: Esta funcionalidade permite ao administrador atualizar os metadados do contrato.
    Cada uma destas funcionalidades é definida em classes separadas.

Parabéns! Criou o seu primeiro token fungível no Tezos usando o padrão FA1.2!

Na próxima lição, aprenderemos a interagir com o contrato de token que acabamos de criar. Isso incluirá a transferência de tokens, a aprovação de transferências de token e a verificação do saldo do token e da oferta total. Fiquem atentos!

Відмова від відповідальності
* Криптоінвестиції пов'язані зі значними ризиками. Дійте обережно. Курс не є інвестиційною консультацією.
* Курс створений автором, який приєднався до Gate Learn. Будь-яка думка, висловлена автором, не є позицією Gate Learn.
Каталог
Урок 2

Criando o seu Primeiro Token com FA1.2 Standard

Nesta lição, vamos passar pelo processo de criação de um token fungível usando o padrão FA1.2 no Tezos. Usaremos o IDE online SmartPy para escrever e implementar o nosso contrato inteligente. Tenha em mente que o padrão FA1.2 é usado principalmente para tokens fungíveis, o que significa tokens que têm propriedades idênticas e podem ser trocados um por um.

Guia passo a passo

  1. Aceder ao SmartPy IDE

  2. Primeiro, abra o IDE online SmartPy em https://smartpy.io/ide/. Esta é a plataforma que usaremos para escrever, testar e implementar os nossos contratos inteligentes.

  3. Iniciar o modelo FA1.2

  4. Clique em “Modelos por Tipo” na barra lateral esquerda e seleccione “FA1.2”. Um novo separador será aberto com o modelo de contrato FA1.2. Este é um contrato pronto a usar que segue o padrão FA1.2.

  5. Compreender o modelo FA1.2

  6. Este modelo tem a funcionalidade básica para um token fungível, que inclui a transferência de tokens, a aprovação de transferências, a verificação de saldos e a visualização da oferta total de tokens. O contrato também inclui funcionalidades adicionais para cunhar e queimar tokens, bem como para gestão de governança.

  7. Estude este modelo e certifique-se de que compreende as suas funcionalidades. Está tudo bem se não perceber tudo neste momento, mas tente ter uma noção geral das operações que este contrato pode realizar.
    Por exemplo, pode copiar o código do modelo no IDE SmartPy ou aqui abaixo:

Python
# Ativos Fungíveis - FA12
# Inspirado em https://gitlab.com/tzip/tzip/blob/master/A/FA1.2.md

importar de forma inteligente como sp


# Os metadados abaixo são apenas um exemplo, servem de base,
# o conteúdo é usado para construir os metadados JSON que os utilizadores
# pode copiar e fazer upload para o IPFS.
TZIP16_Metadata_Base = {
    "name": "SmartPy FA1.2 Token Template",
    "description": "Example Template for an FA1.2 Contract from SmartPy",
    "authors": ["SmartPy Dev Team"],
    "homepage": "https://smartpy.io",
    "interfaces": ["TZIP-007-2021-04-17", "TZIP-016-2021-04-17"],
}


@sp .módulo
def m ():
 classe AdminInterface (SP.Contract):
 @sp .private ("with_storage= apenas leitura) "
 def é_administrador_ (próprio, remetente):
 sp.cast (sp.remetente, sp.endereço)
 """Não é normal, pode ser redefinido através de herança. """
            retorno Verdadeiro

 classe CommonInterface (AdminInterface):
 def __init__(self):
 Interface de administrador.__init__(auto)
            self.data.balances = sp.cast (
                sp.big_map (),
 sp.big_map [
 sp.endereço,
 sp.record (aprovações=sp.map [sp.address, sp.nat], saldo = sp.nat),
                ],
 )
 self.data.total_supply = 0
 self.data.token_metadata = sp.cast (
                sp.big_map (),
 sp.big_map [
 sp.nat,
 sp.record (token_id=sp.nat, token_info=sp.map [sp.string, sp.bytes]),
 ],
 )
 self.data.metadata = sp.cast (
                sp.big_map (),
 sp.big_map [sp.string, sp.bytes],
 )
 self.data.balances = sp.cast (
                sp.big_map (),
 sp.big_map [
 sp.endereço,
 sp.record (aprovações=sp.map [sp.address, sp.nat], saldo = sp.nat),
                ],
 )
 self.data.total_supply = 0
 self.data.token_metadata = sp.cast (
                sp.big_map (),
 sp.big_map [
 sp.nat,
 sp.record (token_id=sp.nat, token_info=sp.map [sp.string, sp.bytes]),
 ],
 )
 self.data.metadata = sp.cast (
                sp.big_map (),
 sp.big_map [sp.string, sp.bytes],
 )

 @sp .private ("with_storage= apenas leitura) "
 def é_pausada_ (self):
 """Não é normal, pode ser redefinido através de herança. """
            devolver Falso

 classe Fa1_2 (CommonInterface):
 def __init__(self, metadados, livro-razão, token_metadata):
 """
 especificação de token_metadata: https://gitlab.com/tzip/tzip/-/blob/master/proposals/tzip-12/tzip-12.md#token-metadata
            Os metadados específicos do Token são armazenados/apresentados como um valor de Michelson do tipo (bytes da string do mapa).
            Algumas das chaves estão reservadas e predefinidas:

 - "": Deve corresponder a um URI TZIP-016 que aponta para uma representação JSON dos metadados do token.
            - " nome": Deve ser uma string UTF-8 dando um nome de " exibição " ao token.
            - " símbolo": Deve ser uma string UTF-8 para o identificador curto do token (ex. XTZ, EUR,...).
 - " decimais": Deve ser um número inteiro (convertido numa string UTF-8 em decimal)
 que define a posição do ponto decimal nos saldos de token para fins de exibição.

            especificação contract_metadata: https://gitlab.com/tzip/tzip/-/blob/master/proposals/tzip-16/tzip-16.md
 """
 Interface comum.__init__(auto)
            self.data.metadata = metadados
 self.data.token_metadata = sp.big_map (
                {0: sp.record(token_id=0, token_info=token_metadata)}
            )

 para o proprietário em ledger.items ():
                self.data.balances [owner.key] = proprietário.value
 self.data.total_supply += proprietário.value.balance

 # TODO: Ativar quando esta funcionalidade for implementada.
            # self.init_metadata (metadados, " " metadados)

 @sp .ponto de entrada
 transferência def (self, param):
 sp.cast (
 param,
 sp.record (de_=sp.address, para_=sp.endereço, value=sp.nat) .layout (
                    ("de_ a partir de", ("a_ quanto a", " valor"))
 ),
 )
 balance_from = self.data.balances.get (
                param.de_, padrão = sp.record (saldo = 0, aprovações= {})
 )
 balance_to = self.data.balances.get (
                param.to_, padrão = sp.record (saldo = 0, aprovações= {})
 )
 saldo de.balance = sp.as_nat (
                balance_from.balance - param.value, erro = FA1.2_Saldo insuficiente " "
 )
 saldo para.saldo += param.valor
 se não auto.is_administrator_ (sp.sender):
                afirme não a si mesmo. is_paused_ (), "FA1.2_Pausado "
                se param.de_ ! = sp.remetente:
 saldo de.aprovações [sp.sender] = sp.as_nat (
                        balance_from.aprovações [sp.sender] - param.value,
                        erro = " FA1.2_Não permitido, "
 )
 self.data.balances [param.from_] = saldo_de
 self.data.balances [param.to_] = saldo_para

 @sp .ponto de entrada
 aprovar def (self, param):
 sp.cast (
 param,
 sp.record (spender=sp.address, value=sp.nat) .layout (
                    ("gastador", " valor")
 ),
 )
 afirme não a si mesmo. is_paused_ (), "FA1.2_Pausado "
            spender_balance = self.data.balances.get (
                sp.remetente, predefinição=sp.record (saldo = 0, aprovações= {})
 )
 Já aprovado = spender_balance.aprovados.get (param.spender, padrão = 0)
 afirmar (
 Já aprovado == 0 ou param.value == 0
 ), " FA1.2 _Alteração de subsídio não seguro "
            spender_balance.aprovações [param.spender] = parâmetro. valor
 self.data.balances [sp.sender] = saldo_gastador_saldo

 @sp .ponto de entrada
 def GetBalance (self, param):
 (endereço, chamada de retorno) = param
 resultado = self.data.balances.get (
                endereço, default=sp.record (saldo = 0, aprovações= {})
 ) .saldo
 sp.transfer (resultado, sp.tez (0), retorno de chamada)

 @sp .ponto de entrada
 def getAllovenance (self, param):
 (args, callback) = param
 resultado = self.data.balances.get (
                args.proprietário, padrão = sp.record (saldo = 0, aprovações= {})
 ) .aprovações.get (args.spender, padrão = 0)
 sp.transfer (resultado, sp.tez (0), retorno de chamada)

 @sp .ponto de entrada
 def getTotalSupply (self, param):
 sp.cast (param, sp.pair [sp.unit, sp.contrato [sp.nat]])
            sp.transfer (self.data.total_supply, sp.tez (0), sp.and (param))

 @sp .offchain_view ()
 def token_metadata (self, token_id):
 """Devolver o URI dos metadados do token fornecido. (token_id deve ser 0). """
            sp.cast (token_id, sp.nat)
 devolver self.data.token_metadata [token_id]

    ##########
 # Misturas #
 ##################

    class Admin(sp.Contract):
        def __init__(self, administrator):
            self.data.administrator = administrator

        @sp.private(with_storage="read-only")
        def is_administrator_(self, sender):
            return sender == self.data.administrator

        @sp.entrypoint
        def setAdministrator(self, params):
            sp.cast(params, sp.address)
            assert self.is_administrator_(sp.sender), "Fa1.2_NotAdmin"
            self.data.administrator = params

        @sp.entrypoint()
        def getAdministrator(self, param):
            sp.cast(param, sp.pair[sp.unit, sp.contract[sp.address]])
            sp.transfer(self.data.administrator, sp.tez(0), sp.snd(param))

        @sp.onchain_view()
        def get_administrator(self):
            return self.data.administrator

    class Pause(AdminInterface):
        def __init__(self):
            AdminInterface.__init__(self)
            self.data.paused = False

        @sp.private(with_storage="read-only")
        def is_paused_(self):
            return self.data.paused

        @sp.entrypoint
        def setPause(self, param):
            sp.cast(param, sp.bool)
            assert self.is_administrator_(sp.sender), "Fa1.2_NotAdmin"
            self.data.paused = param

    class Mint(CommonInterface):
        def __init__(self):
            CommonInterface.__init__(self)

        @sp.entrypoint
        def mint(self, param):
            sp.cast(param, sp.record(address=sp.address, value=sp.nat))
            assert self.is_administrator_(sp.sender), "Fa1.2_NotAdmin"
            receiver_balance = self.data.balances.get(
                param.address, default=sp.record(balance=0, approvals={})
            )
            receiver_balance.balance += param.value
            self.data.balances[param.address] = receiver_balance
            self.data.total_supply += param.value

    class Burn(CommonInterface):
        def __init__(self):
            CommonInterface.__init__(self)

        @sp.entrypoint
        def burn(self, param):
            sp.cast(param, sp.record(address=sp.address, value=sp.nat))
            assert self.is_administrator_(sp.sender), "Fa1.2_NotAdmin"
            receiver_balance = self.data.balances.get(
                param.address, default=sp.record(balance=0, approvals={})
            )
            receiver_balance.balance = sp.as_nat(
                receiver_balance.balance - param.value,
                error="FA1.2_InsufficientBalance",
            )
            self.data.balances[param.address] = receiver_balance
            self.data.total_supply = sp.as_nat(self.data.total_supply - param.value)

    class ChangeMetadata(CommonInterface):
        def __init__(self):
            CommonInterface.__init__(self)

        @sp.entrypoint
        def update_metadata(self, key, value):
            """An entrypoint to allow the contract metadata to be updated."""
            assert self.is_administrator_(sp.sender), "Fa1.2_NotAdmin"
            self.data.metadata[key] = value

    ### Testes #
 ##########

 classe FA1_2TestFull (Admin, Pausa, Fa1_2, Mint, Burn, ChangeMetadata):
 def __init__(auto, administrador, metadados, livro-razão, token_metadata):
 Alterar metadados.__init__(auto)
            Queimar.__init__(eu)
            Hortelã.__init__(auto)
            Fa1_2.__init__(eu, metadados, livro-razão, token_metadata)
 Pausa.__init__(auto)
            Administrador.__init__(eu, administrador)

 classe Viewer_NAT (SP.Contract):
 def __init__(self):
 self.data.last = sp.cast (Nenhum, sp.option [sp.nat])

        @sp .ponto de entrada
 def alvo (self, parâmetros):
 self.data.last = Sp.Alguns (parâmetros)

 turma Viewer_address (SP.Contract):
 def __init__(self):
 self.data.last = sp.cast (Nenhum, sp.option [sp.address])

        @sp .ponto de entrada
 alvo def (self, parâmetros):
 self.data.last = SP.Alguns (parâmetros)


se " os modelos " não estiverem em __name__:

 @sp .add_test (nome = FA12) " "
 teste def ():
 sc = sp.test_scenario (m)
        sc.h1 (modelo " FA1.2 - Ativos fungíveis) "

 # sp.test_account gera ED25519 pares de chaves deterministicamente:
 admin = sp.test_account (Administrador) " "
        alice = sp.test_account (Alice) " "
        bob = sp.test_account (Robert) " "

        # Vamos exibir as contas:
 sc.h1 (Contas) " "
 sc.show ([admin, alice, bob])

 sc.h1 (Contrato) " "
 token_metadados = {
            "decimals": sp.utils.bytes_of_string("18"),  # Mandatory by the spec
            "name": sp.utils.bytes_of_string("My Great Token"),  # Recommended
            "symbol": sp.utils.bytes_of_string("MGT"),  # Recommended
            # Extra fields
            "icon": sp.utils.bytes_of_string(
                "https://smartpy.io/static/img/logo-only.svg"
            ),
        }
        contract_metadados = sp.utils.metadata_of_url (
            "ipfs: //qmaiaUJ1FFngYTU8RLBJC3een9CSkwaf8EGmbNdmHzpNfd "
 )

 c1 = m.FA1_2TestCompleto (
 administrator=admin.address,
 metadata=contract_metadata,
 token_metadata=token_metadata,
 livro-razão = {},
 )
 sc += c1

 sc.h1 (Vista " fora da cadeia - token_metadata) "
 sc.verify_equal (
 SP.View (c1, " " token_metadata) (0),
 sp.record (
 token_id=0,
 token_info=sp.map (
                    {
                        "decimals": sp.utils.bytes_of_string("18"),
                        "name": sp.utils.bytes_of_string("My Great Token"),
                        "symbol": sp.utils.bytes_of_string("MGT"),
                        "icon": sp.utils.bytes_of_string(
                            "https://smartpy.io/static/img/logo-only.svg"
                        ),
                    }
                ),
 ),
 )

 sc.h1 ("Tentativa de actualizar metadados) "
 sc.verificar (
 c1.data.metadados [] " "
            == sp.utils.bytes_of_string (
                "ipfs: //qmaiaUJ1FFngYTU8RLBJC3EEN9CSkwaf8EGmbNdmHzpNfd "
 )
 )
 c1.update_metadata (chave =, valor = sp.bytes (0x00)) .run (remetente = " " administrador) " "
 sc.verify (c1.data.metadata [] " " == sp.bytes (0x00)) " "

 sc.h1 (Pontos de entrada) " "
 sc.h2 (O " administrador cunha algumas moedas) "
 c1.mint (endereço=alice.address, value=12) .run (remetente=admin)
        c1.mint (endereço=alice.address, value=3) .run (remetente = admin)
        c1.mint (endereço=alice.address, value=3) .run (remetente = admin)
        sc.h2 ("Alice transfere para Bob) "
 c1.transfer (de_=alice.address, para_=bob.address, valor=4) .run (remetente = alice)
        sc.verify (c1.data.balances [alice.address] .balance == 14)
 sc.h2 ("Bob tenta transferir-se de Alice mas não tem a aprovação dela) "
 c1.transfer (de_=alice.address, para_=bob.address, valor = 4) .run (
            remetente = bob, válido = falso
 )
 sc.h2 ("Alice aprova transferências de Bob e Bob) "
 c1.approve (spender=bob.address, value=5) .run (remetente = alice)
        c1.transfer (de_=alice.address, para_=bob.address, valor=4) .run (remetente = bob)
        sc.h2 ("Bob tenta transferir em excesso de Alice) "
 c1.transfer (de_=alice.address, para_=bob.address, valor = 4) .run (
            remetente = bob, válido = falso
 )
 sc.h2 (O " administrador queima o token Bob) "
 c1.burn (endereço=bob.address, valor=1) .run (remetente = administrador)
        sc.verify (c1.data.balances [alice.address] .balance == 10)
 sc.h2 ("Alice tenta queimar o token Bob) "
 c1.burn (endereço=bob.address, value=1) .run (remetente = alice, Válido = falso)
 sc.h2 (O " administrador pausa o contrato e Alice não pode mais transferir) "
 c1.setPause (Verdadeiro) .run (remetente = admin)
        c1.transfer (de_=alice.address, para_=bob.address, valor = 4) .run (
            remetente = Alice, Válido = Falso
 )
 sc.verify (c1.data.balances [alice.address] .balance == 10)
 sc.h2 (O " administrador transfere enquanto está em pausa) "
 c1.transfer (de_=alice.address, para_=bob.address, valor=1) .run (remetente = administrador)
        sc.h2 (O " administrador anula o contrato e as transferências são permitidas) "
 c1.setPause (Falso) .run (remetente = admin)
        sc.verify (c1.data.balances [alice.address] .balance == 9)
 c1.transfer (de_=alice.address, para_=bob.address, valor = 1) .run (remetente = alice)

        sc.verify (c1.data.total_supply == 17)
 sc.verify (c1.data.balances [alice.address] .balance == 8)
 sc.verify (c1.data.balances [bob.address] .balance == 9)

 sc.h1 (Visualizações) " "
 sc.h2 (Equilíbrio) " "
 view_balance = m.Viewer_NAT ()
 sc += visão_equilíbrio
 alvo = sp.contract (SP.tnat, view_balance.address, "alvo") .open_some ()
 c1.GetBalance ((alice.address, alvo))
 sc.verify_equal (ver_balance.data.last, sp.alguns (8))

 sc.h2 (Administrador) " "
 visualizar_administrador = m.Viewer_address ()
 sc += visualizar_administrador
 alvo = sp.contrato (
            Endereço SP.T, view_administrator.address, "alvo "
 ) .open_some ()
 c1.GetAdministrator ((sp.unit, alvo))
 sc.verify_equal (ver_administrator.data.last, sp.some (admin.address))

        sc.h2 (Fornecimento Total) " "
 View_totalSupply = m.Viewer_NAT ()
 sc += Visualizar_TotalSupply
 alvo = sp.contract (SP.tnat, view_totalsupply.address, "alvo") .open_some ()
 c1.GetTotalSupply ((sp.unidade, destino))
 sc.verify_equal (View_totalsupply.data.last, sp.alguns (17))

 sc.h2 (Subsídio) " "
 view_allowance = m.Viewer_NAT ()
 sc += view_allowance
 alvo = sp.contract (sp.tnat, view_allowance.address, "alvo") .open_some ()
 c1.getAllance ((sp.record (owner=alice.address, spender=bob.address), alvo))
 sc.verify_equal (ver_allowance.data.last, sp.alguns (1))
  1. Execute o Contrato. Vai ver algo do género

O contrato FA1.2 original tem funcionalidades básicas como transferir tokens, aprovar transferências, verificar saldos e visualizar o fornecimento total de tokens. Agora vamos melhorar esta funcionalidade.

  • Admin: Introduziremos um contrato que permite a um administrador executar ações específicas, como pausar o contrato, e impede que outras contas usem essas funções.
  • Pausa: Esta funcionalidade permite-nos pausar e desfazer o contrato. Quando o contrato é pausado, ninguém pode usá-lo excepto o administrador.
  • Mint: Esta funcionalidade de contrato permite ao administrador criar novos tokens.
  • Queimar: Esta funcionalidade de contrato permite ao administrador destruir tokens.
  • ChangeMetadata: Esta funcionalidade permite ao administrador atualizar os metadados do contrato.
    Cada uma destas funcionalidades é definida em classes separadas.

Parabéns! Criou o seu primeiro token fungível no Tezos usando o padrão FA1.2!

Na próxima lição, aprenderemos a interagir com o contrato de token que acabamos de criar. Isso incluirá a transferência de tokens, a aprovação de transferências de token e a verificação do saldo do token e da oferta total. Fiquem atentos!

Відмова від відповідальності
* Криптоінвестиції пов'язані зі значними ризиками. Дійте обережно. Курс не є інвестиційною консультацією.
* Курс створений автором, який приєднався до Gate Learn. Будь-яка думка, висловлена автором, не є позицією Gate Learn.