Como: Criar um token na rede PIRL

Compartilhar esta publicação

Compartilhar facebook
Compartilhar linkedin
Compartilhar twitter
Compartilhar email

A moeda

Vamos criar um token digital. Os tokens no ecossistema Pirl podem representar qualquer bem negociável fungível: moedas, pontos de fidelidade, certificados de ouro, IOUs, itens do jogo etc. Como todos os tokens implementam alguns recursos básicos de maneira padrão, isso também significa que seu token será instantaneamente compatível com a carteira Pirl e qualquer outro cliente ou contrato que use os mesmos padrões.

Token mínimo e viável

O contrato de token padrão pode ser bastante complexo. Mas, em essência, um token muito básico se resume a isso:

contract MyToken {/ * Isso cria uma matriz com todos os saldos * / mapeamento (endereço => uint256) public balanceOf; / * Inicializa o contrato com tokens de fornecimento inicial para o criador do contrato * / função MyToken (uint256 initialSupply) {balanceOf [msg.sender] = initialSupply; // Dê ao criador todos os tokens iniciais} / * Enviar moedas * / transferência de função (endereço _to, uint256 _value) {require (balanceOf [msg.sender]> = _value); // Verifique se o remetente tem requisito suficiente (balanceOf [_to] + _value> = balanceOf [_to]); // Verifique se há excedentes balanceOf [msg.sender] - = _value; // Subtrai do remetente balanceOf [_to] + = _value; // Adicione o mesmo ao destinatário}}

O CÓDIGO

Mas se você quiser copiar e colar um código mais completo, use o seguinte:

solidez pragma ^ 0,4,16;

interface tokenRecipient {função receiveApproval (endereço _from, uint256 _value, endereço _token, bytes _extraData) public; }

contrato TokenERC20 {
   // Variáveis públicas do token
   nome público da string;
   símbolo público de string;
   decimais públicos uint8 = 18;
   // 18 casas decimais é o padrão fortemente sugerido, evite alterá-lo
   uint256 public totalSupply;

   // Isso cria uma matriz com todos os saldos
   mapeamento (endereço => uint256) public balanceOf;
   mapeamento (endereço => mapeamento (endereço => uint256)) subsídio público;

   // Isso gera um evento público no blockchain que notificará os clientes
   Transferência de evento (endereço indexado de, endereço indexado para, valor uint256);

   // Isso notifica os clientes sobre a quantidade queimada
   queimadura de evento (endereço indexado de, valor uint256);

   /**
    * Função construtora
    *
    * Inicializa contrato com tokens de fornecimento inicial para o criador do contrato
    */
   função TokenERC20 (
       uint256 initialSupply,
       string tokenName,
       string tokenSymbol
   ) public {
       totalSupply = initialSupply * 10 ** uint256 (casas decimais); // Atualizar fornecimento total com o valor decimal
       balanceOf [msg.sender] = totalSupply; // Dê ao criador todos os tokens iniciais
       nome = tokenName; // Defina o nome para fins de exibição
       symbol = tokenSymbol; // Defina o símbolo para fins de exibição
   }

   /**
    * Transferência interna, somente pode ser chamada por este contrato
    */
   função _transferência (endereço _de, endereço_para, uint_value) interno {
       // Impede a transferência para o endereço 0x0. Use burn ()
       require (_to! = 0x0);
       // Verifique se o remetente tem o suficiente
       require (balanceOf [_from]> = _value);
       // Verifique se há estouros
       require (balanceOf [_to] + _value> balanceOf [_to]);
       // Salve isso para uma afirmação no futuro
       uint previousBalances = balanceOf [_de] + balanceOf [_to];
       // Subtrair do remetente
       balanceOf [_from] - = _value;
       // Adicione o mesmo ao destinatário
       balanceOf [_to] + = _value;
       Transferência (_de, _para, _valor);
       // Asserts são usados para usar a análise estática para encontrar erros no seu código. Eles nunca devem falhar
       assert (balanceOf [_de] + balanceOf [_to] == previousBalances);
   }

   /**
    * Tokens de transferência
    *
    * Envie tokens `_value` para` _to` da sua conta
    *
    * @param _to O endereço do destinatário
    * @param _valorize o valor a enviar
    */
   transferência de função (endereço _to, uint256 _value) public {
       _transferência (msg.sender, _to, _value);
   }

   /**
    * Transferir tokens de outro endereço
    *
    * Envie tokens `_value` para` _to` em nome de `_from`
    *
    * @param _from O endereço do remetente
    * @param _to O endereço do destinatário
    * @param _valorize o valor a enviar
    */
   a função transferFrom (endereço _de, endereço _to, uint256 _value) retorna retornos públicos (êxito de bool)
       require (_valor <= permissão [_de] [msg.sender]); // Verifique o subsídio
       subsídio [_from] [msg.sender] - = _value;
       _transferência (_from, _to, _value);
       return true;
   }

   /**
    * Definir subsídio para outro endereço
    *
    * Permite que o `_spender` gaste não mais que tokens do` _value` em seu nome
    *
    * @param _spender O endereço autorizado para gastar
    * @param _valorize o valor máximo que eles podem gastar
    */
   função aprovar (endereço _spender, uint256 _value) public
       retornos (sucesso booleano) {
       subsídio [msg.sender] [_ gastador] = _valor;
       return true;
   }

   /**
    * Defina a permissão para outro endereço e notifique
    *
    * Permite que o `_spender` gaste não mais que tokens do` _value` em seu nome e, em seguida, execute ping no contrato
    *
    * @param _spender O endereço autorizado para gastar
    * @param _valorize o valor máximo que eles podem gastar
    * @param _extraData algumas informações extras para enviar ao contrato aprovado
    */
   função approveAndCall (endereço _spender, uint256 _value, bytes _extraData)
       público
       retornos (sucesso booleano) {
       gastador tokenRecipient = tokenRecipient (_spender);
       if (aprovar (_spender, _value)) {
           spender.receiveApproval (msg.sender, _value, this, _extraData);
           return true;
       }
   }

   /**
    * Destrua fichas
    *
    * Remova os tokens `_value` do sistema irreversivelmente
    *
    * @param _value a quantidade de dinheiro para queimar
    */
   função burn (uint256 _value) retornos públicos (sucesso bool) {
       require (balanceOf [msg.sender]> = _value); // Verifique se o remetente tem o suficiente
       balanceOf [msg.sender] - = _value; // Subtrair do remetente
       totalSupply - = _value; // Atualiza totalSupply
       Gravar (msg.sender, _value);
       return true;
   }

   /**
    * Destrua tokens de outra conta
    *
    * Remova os tokens `_value` do sistema irreversivelmente em nome de` _from`.
    *
    * @param _do endereço do remetente
    * @param _value a quantidade de dinheiro para queimar
    */
   função burnFrom (endereço _from, uint256 _value) retornos públicos (sucesso do bool) {
       require (balanceOf [_from]> = _value); // Verifique se o saldo desejado é suficiente
       require (_valor <= permissão [_de] [msg.sender]); // Verifique o subsídio
       balanceOf [_from] - = _value; // Subtrair do saldo desejado
       subsídio [_from] [msg.sender] - = _value; // Subtrair do subsídio do remetente
       totalSupply - = _value; // Atualizar totalSupply
       Gravar (_de, _valor);
       return true;
   }
}

Compreendendo o código

Então, vamos começar com o básico. Abra o aplicativo Wallet, vá para a guia Contratos e, em seguida, Implante novo contrato. No campo de texto Código-fonte do contrato de solidez, digite o código abaixo:

contrato MyToken {
       / * Isso cria uma matriz com todos os saldos * /
       mapeamento (endereço => uint256) public balanceOf;
   }

Um mapeamento significa uma matriz associativa, na qual você associa endereços a saldos. Os endereços estão no formato básico Pirl hexadecimal, enquanto os saldos são números inteiros, variando de 0 a 115 quattuorvigintilhões. Se você não sabe quanto custa um quatuorigmilhão, são muitos mais vigilhões do que você planeja usar seus tokens. A palavra-chave pública, significa que essa variável estará acessível a qualquer pessoa na blockchain, o que significa que todos os saldos são públicos (como precisam ser, para que os clientes os mostrem).

Se você publicasse seu contrato imediatamente, ele funcionaria, mas não seria muito útil: seria um contrato que poderia consultar o saldo da sua moeda em qualquer endereço - mas, como você nunca criou uma única moeda, todos eles return 0. Então, vamos criar alguns tokens na inicialização. Adicione esse código antes do último colchete, logo abaixo da linha de mapeamento.

função MyToken () {
       balanceOf [msg.sender] = 21000000;
   }

Observe que a função MyToken tem o mesmo nome que o contrato MyToken. Isso é muito importante e, se você renomear um, também será necessário renomear o outro: esta é uma função especial de inicialização que é executada apenas uma vez e apenas uma vez quando o contrato é carregado pela primeira vez na rede. Esta função definirá o saldo do msg.sender, o usuário que implantou o contrato, com um saldo de 21 milhões.

A escolha de 21 milhões foi bastante arbitrária, e você pode alterá-la para o que quiser no código, mas há uma maneira melhor: em vez disso, forneça-a como um parâmetro para a função, assim:

função MyToken (uint256 initialSupply) public {
       balanceOf [msg.sender] = InitialSupply;
   }

Dê uma olhada na coluna da direita ao lado do contrato e você verá uma lista suspensa, escrita, escolha um contrato. Selecione o contrato “MyToken” e verá que agora ele mostra uma seção chamada Parâmetros do construtor. Esses são parâmetros alteráveis para seu token, para que você possa reutilizar o mesmo código e alterar essas variáveis apenas no futuro.

No momento, você tem um contrato funcional que criou saldos de tokens, mas como não há função para movê-lo, tudo o que faz é permanecer na mesma conta. Então, vamos implementar isso agora. Escreva o seguinte código antes do último colchete.

/ * Enviar moedas * /
   transferência de função (endereço _to, uint256 _value) {
       / * Adicionar e subtrair novos saldos * /
       balanceOf [msg.sender] - = _value;
       balanceOf [_to] + = _value;
   }

Essa é uma função muito direta: possui um destinatário e um valor como parâmetro e, sempre que alguém o chama, subtrai o valor _ do seu saldo e o adiciona ao saldo _. Imediatamente há um problema óbvio: o que acontece se a pessoa quiser enviar mais do que possui? Como não queremos lidar com dívidas neste contrato específico, faremos uma verificação rápida e, se o remetente não tiver fundos suficientes, a execução do contrato simplesmente será interrompida. É também para verificar se há estouros, para evitar ter um número tão grande que se torne zero novamente.

Para interromper a execução de um contrato no meio da execução, você pode retornar ou jogar. O primeiro custará menos gasolina, mas pode causar mais dor de cabeça, pois as alterações feitas no contrato até o momento serão mantidas. Por outro lado, 'throw' cancelará toda a execução do contrato, reverterá quaisquer alterações que a transação possa ter feito e o remetente perderá todo o Pirl que ele enviou para abastecer. Mas como a Carteira virtual pode detectar que um contrato será lançado, ela sempre mostra um alerta, impedindo que qualquer Pirl seja gasto.

transferência de função (endereço _to, uint256 _value) {
       / * Verifique se o remetente tem saldo e se há estouros * /
       require (balanceOf [msg.sender]> = _value && balanceOf [_to] + _value> = balanceOf [_to]);

       / * Adicionar e subtrair novos saldos * /
       balanceOf [msg.sender] - = _value;
       balanceOf [_to] + = _value;
   }

 

Agora, tudo o que falta é ter algumas informações básicas sobre o contrato. Num futuro próximo, isso pode ser tratado por um registro de token, mas por enquanto os adicionaremos diretamente ao contrato:

nome público da string;
símbolo público de string;
decimais públicos uint8;

E agora atualizamos a função construtora para permitir que todas essas variáveis sejam configuradas no início:

/ * Inicializa contrato com tokens de fornecimento inicial para o criador do contrato * /
   função MyToken (uint256 initialSupply, string tokenName, string tokenSymbol, uint8 decimalUnits) {
       balanceOf [msg.sender] = InitialSupply; // Dê ao criador todos os tokens iniciais
       nome = tokenName; // Defina o nome para fins de exibição
       symbol = tokenSymbol; // Defina o símbolo para fins de exibição
       decimais = unidades decimais; // Quantidade de casas decimais para fins de exibição
   }

Por fim, agora precisamos de um somPirling chamado Events. Essas são funções vazias especiais que você chama para ajudar clientes como a Carteira Pirl a acompanhar as atividades que estão acontecendo no contrato. Os eventos devem começar com uma letra maiúscula. Adicione esta linha no início do contrato para declarar o evento:

Transferência de evento (endereço indexado de, endereço indexado para, valor uint256);
 

E então você só precisa adicionar essas duas linhas dentro da função "transferir":

      / * Notifique alguém ouvindo que essa transferência ocorreu * /
       Transferência (msg.sender, _to, _value);

 

E agora seu token está pronto!

Notou os comentários?

O que são esses comentários @notice e @param, você pode perguntar? Isso é Natspec um padrão emergente para uma especificação de linguagem natural, que permite que as carteiras mostrem ao usuário uma descrição em linguagem natural do que o contrato está prestes a fazer. Embora atualmente não seja suportado por muitas carteiras, isso mudará no futuro, por isso é bom estar preparado.

Como implantar

Se você ainda não estiver lá, abra a Carteira Pirl, vá para a guia contratos e clique em "implantar novo contrato".

Agora pegue a fonte do token de cima e cole-a no “Campo Fonte de Solididade”. Se o código for compilado sem nenhum erro, você verá uma lista suspensa "escolher um contrato" à direita. Obtenha e selecione o contrato “MyToken”. Na coluna da direita, você verá todos os parâmetros necessários para personalizar seu próprio token. Você pode ajustá-los como quiser.

Role até o final da página e você verá uma estimativa do custo de computação desse contrato e poderá selecionar uma taxa sobre quanto Pirl está disposto a pagar por ele. Qualquer excesso de Pirl que você não gaste será devolvido a você, para que você possa deixar as configurações padrão, se desejar. Pressione “deploy”, digite a senha da sua conta e aguarde alguns segundos para que sua transação seja retirada.

Você será redirecionado para a página inicial, onde poderá ver sua transação aguardando confirmações. Clique na conta e, após não mais de um minuto, você verá que sua conta mostrará que você possui 100% dos compartilhamentos que você acabou de criar. Para enviar alguns para alguns amigos: selecione "enviar" e depois escolha a moeda que deseja enviar (Pirl ou seu compartilhamento recém-criado), cole o endereço do seu amigo no campo "para" e pressione "enviar".

Se você enviar para um amigo, ele ainda não verá nada na carteira. Isso ocorre porque a carteira rastreia apenas os tokens que conhece e você precisa adicioná-los manualmente. Agora vá para a guia "Contratos" e você verá um link para o seu contrato recém-criado. Clique nele para ir para sua página. Como esta é uma página de contrato muito simples, não há muito o que fazer aqui, basta clicar em "copiar endereço" e colar o endereço do contrato em um editor de texto, em breve você precisará dele.

Para adicionar um token para assistir, vá para a página de contratos e clique em "Watch Token". Um pop-up aparecerá e você só precisará colar o endereço do contrato. O nome do token, o símbolo e o número decimal devem ser preenchidos automaticamente, mas se não for, você pode colocar o que quiser (isso afetará apenas a maneira como ele é exibido na sua carteira). Depois de fazer isso, você verá automaticamente qualquer saldo que tiver desse token e poderá enviá-lo a qualquer outra pessoa.

E agora você tem seu próprio token de criptografia! Os tokens por si só podem ser úteis como troca de valor nas comunidades locais, maneiras de acompanhar as horas trabalhadas ou outros programas de fidelidade. Mas podemos fazer com que uma moeda tenha um valor intrínseco, tornando-a útil?

Melhore seu token

Você pode implantar todo o seu token de criptografia sem nunca tocar em uma linha de código, mas a verdadeira mágica acontece quando você o personaliza. As seções a seguir serão sugestões sobre funções que você pode adicionar ao seu token para ajustá-lo às suas necessidades.

Funções mais básicas

Você notará que existem mais funções em seu contrato de token básico, como aprovar, sendFrom e outras. Essas funções existem para o seu token interagir com outros contratos: se você quiser, digamos, vender tokens para uma central descentralizada, apenas enviá-los para um endereço não será suficiente, pois a central não terá conhecimento dos novos tokens ou de quem enviou eles, porque os contratos não podem se inscrever em Eventos apenas para funcionar. Portanto, para os contratos, você deve primeiro aprovar uma quantidade de tokens que eles podem mover da sua conta e depois enviá-los para que eles saibam que devem fazer suas coisas - ou executar as duas ações em uma, com approveAndCall.

Como muitas dessas funções precisam reimplementar a transferência de tokens, faz sentido alterá-las para uma função interna, que só pode ser chamada pelo próprio contrato:

/ * Transferência interna, só pode ser chamada por este contrato * /
   função _transferência (endereço _de, endereço_para, uint_value) interno {
       require (_to! = 0x0); // Impede a transferência para o endereço 0x0. Use burn ()
       require (balanceOf [_from]> = _value); // Verifique se o remetente tem o suficiente
       require (balanceOf [_to] + _value> balanceOf [_to]); // Verifique se há estouros
       require (! frozenAccount [_from]); // Verifique se o remetente está congelado
       require (! frozenAccount [_to]); // Verifique se o destinatário está congelado
       balanceOf [_from] - = _value; // Subtrair do remetente
       balanceOf [_to] + = _value; // Adicione o mesmo ao destinatário
       Transferência (_de, _para, _valor);
   }

Agora, todas as suas funções que resultam na transferência de moedas podem fazer suas próprias verificações e depois chamar a transferência com os parâmetros corretos. Observe que esta função moverá moedas de uma conta para outra, sem a permissão de ninguém para fazer isso: é por isso que é uma função interna, chamada somente pelo contrato: se você adicionar qualquer função que a chame, verifique se ele verifica corretamente se o o chamador deve ter permissão para movê-los.

Administrador centralizado

Todos os dapps são totalmente descentralizados por padrão, mas isso não significa que eles não possam ter algum tipo de gerente central, se você quiser. Talvez você queira cunhar mais moedas, talvez queira proibir algumas pessoas de usar sua moeda. Você pode adicionar qualquer um desses recursos, mas o problema é que você só pode adicioná-los no início, para que todos os detentores de token sempre saibam exatamente as regras do jogo antes de decidirem possuí-lo.

Para que isso aconteça, você precisa de um controlador central de moeda. Isso pode ser uma conta simples, mas também pode ser um contrato e, portanto, a decisão de criar mais fichas dependerá do contrato: se é uma organização democrática que pode votar, ou talvez seja apenas uma maneira de limitar o poder do proprietário do token.

Para fazer isso, aprenderemos uma propriedade muito útil dos contratos: herança. A herança permite que um contrato adquira propriedades de um contrato pai, sem precisar redefinir todas elas. Isso torna o código mais limpo e fácil de reutilizar. Adicione esse código à primeira linha do seu código, antes de contratar o MyToken {.

propriedade do contrato {
       endereço do proprietário público;

       função propriedade () {
           owner = msg.sender;
       }

       somente modificador Proprietário {
           requerer (msg.sender == proprietário);
           _;
       }

       função transferOwnership (endereço newOwner) onlyOwner {
           owner = newOwner;
       }
   }

Isso cria um contrato muito básico que não faz nada, exceto definir algumas funções genéricas sobre um contrato que pode ser "de propriedade". Agora, o próximo passo é adicionar o texto que pertence ao seu contrato:

 o contrato MyToken pertence {
       / * o restante do contrato, como de costume * /

Isso significa que todas as funções do MyToken agora podem acessar o proprietário da variável e o modificador onlyOwner. O contrato também recebe uma função para transferir a propriedade. Como pode ser interessante definir o proprietário do contrato na inicialização, você também pode adicioná-lo à função construtora:

função MyToken (
       uint256 initialSupply,
       string tokenName,
       uint8 decimalUnits,
       string tokenSymbol,
       endereço centralMinter
       ) {
       if (centralMinter! = 0) owner = centralMinter;
   }

Central Mint

Suponha que você queira alterar a quantidade de moedas em circulação. É o caso quando seus tokens realmente representam um ativo fora da blockchain (como certificados de ouro ou moedas do governo) e você deseja que o inventário virtual reflita o real. Esse também pode ser o caso quando os detentores de moeda esperam algum controle do preço do token e desejam emitir ou remover tokens da circulação.

Primeiro, precisamos adicionar uma variável para armazenar o totalSupply e atribuí-lo à nossa função de construtor.

contrato MyToken {
       uint256 public totalSupply;

       função MyToken (…) {
           totalSupply = initialSupply;
           …
       }
       …
   }

Agora vamos finalmente adicionar uma nova função que permitirá ao proprietário criar novos tokens:

função mintToken (destino do endereço, uint256 mintedAmount) onlyOwner {
       balanceOf [target] + = mintedAmount;
       totalSupply + = mintedAmount;
       Transferência (0, proprietário, mintedAmount);
       Transferência (proprietário, destino, mintedAmount);
   }

Observe o modificador onlyOwner no final do nome da função. Isso significa que essa função será reescrita na compilação para herdar o código do modificador onlyOwner que definimos anteriormente. O código dessa função será inserido onde houver um sublinhado na função modificadora, o que significa que essa função específica só pode ser chamada pela conta definida como proprietária. Basta adicionar isso a um contrato com um modificador proprietário e você poderá criar mais moedas.

Congelamento de ativos

Dependendo do seu caso de uso, talvez você precise ter alguns obstáculos regulatórios sobre quem pode ou não usar seus tokens. Para que isso aconteça, você pode adicionar um parâmetro que permita ao proprietário do contrato congelar ou descongelar ativos.

Adicione essa variável e funcione em qualquer lugar dentro do contrato. Você pode colocá-los em qualquer lugar, mas, para boas práticas, recomendamos que você coloque os mapeamentos nos outros mapeamentos e eventos nos outros eventos.

mapeamento (endereço => bool) public frozenAccount;
   evento FrozenFunds (endereço de destino, bool congelado);

   função freezeAccount (destino do endereço, congelamento de bool) onlyOwner {
       frozenAccount [target] = congelar;
       FrozenFunds (alvo, congelar);
   }

Com esse código, todas as contas são descongeladas por padrão, mas o proprietário pode definir qualquer uma delas para um estado de congelamento, chamando Freeze Account. Infelizmente, o congelamento não tem efeito prático porque não adicionamos nada à função de transferência. Estamos mudando isso agora:

transferência de função (endereço _to, uint256 _value) {
       require (! frozenAccount [msg.sender]);

Agora, qualquer conta congelada ainda terá seus fundos intactos, mas não poderá movê-los. Todas as contas são descongeladas por padrão até que você as congele, mas você pode reverter esse comportamento facilmente para uma lista de desbloqueio em que precisa aprovar manualmente todas as contas. Apenas renomeie frozenAccount para aprovadoAccount e altere a última linha para:

require (protectedAccount [msg.sender]);

Compra e venda automática

Até agora, você confiou no utilitário e na confiança para valorizar seu token. Mas, se você quiser, pode fazer com que o valor do token seja apoiado pelo Pirl (ou outros tokens), criando um fundo que os venda e os compre automaticamente pelo valor de mercado.

Primeiro, vamos definir o preço para compra e venda:

uint256 sellPrice público;
   uint256 buyPrice público;

   função setPrices (uint256 newSellPrice, uint256 newBuyPrice) onlyOwner {
       sellPrice = newSellPrice;
       buyPrice = newBuyPrice;
   }

Isso é aceitável por um preço que não muda com muita frequência, pois cada nova alteração de preço exige que você execute uma transação e gaste um pouco do Pirl. Se você deseja ter um preço flutuante constante, recomendamos investigar feeds de dados padrão

O próximo passo é fazer as funções de compra e venda:

função buy () retornos pagáveis (valor uint) {
       amount = msg.value / buyPrice; // calcula a quantidade
       require (balanceOf [this]> = amount); // verifica se tem o suficiente para vender
       balanceOf de [msg.sender] + = quantia; // adiciona o valor ao saldo do comprador
       balanceOf [this] - = amount; // subtrai o valor do saldo do vendedor
       Transferência (isto, msg.sender, quantia); // executa um evento refletindo a alteração
       valor de retorno; // termina a função e retorna
   }

   a função sell (quantia uint) retorna (receita uint) {
       require (balanceOf [msg.sender]> = amount); // verifica se o remetente tem o suficiente para vender
       balanceOf [this] + = valor; // adiciona o valor ao saldo do proprietário
       balanceOf [msg.sender] - = valor; // subtrai o valor do saldo do vendedor
       receita = valor * sellPrice;
       msg.sender.transfer (receita); // envia o Pirl ao vendedor: é importante fazer isso por último para evitar ataques de recursão
       Transferir (msg.sender, esse valor); // executa um evento refletindo na mudança
       retorno de receita; // termina a função e retorna
   }

Observe que isso não criará novos tokens, mas alterará o saldo do contrato. O contrato pode conter seus próprios tokens e a Pirl e o proprietário do contrato, enquanto pode definir preços ou, em alguns casos, criar novos tokens (se aplicável), não podendo tocar nos tokens do banco ou Pirl. A única maneira pela qual esse contrato pode movimentar fundos é vendendo e comprando.

Nota Os "preços" de compra e venda não estão definidos no Pirl, mas em wei a moeda mínima do sistema (equivalente ao centavo em euros e euros ou Satoshi no Bitcoin). Um Pirl é 1000000000000000000 wei. Portanto, ao definir preços para seu token no Pirl, adicione 18 zeros no final.

Ao criar o contrato, envie Pirl suficiente para que ele possa comprar de volta todos os tokens do mercado; caso contrário, seu contrato será insolvente e seus usuários não poderão vender seus tokens.

Os exemplos anteriores, é claro, descrevem um contrato com um único comprador e vendedor central, um contrato muito mais interessante permitiria um mercado onde qualquer pessoa pudesse licitar preços diferentes, ou talvez carregasse os preços diretamente de uma fonte externa.

Preenchimento automático

Toda vez que você faz uma transação na Pirl, precisa pagar uma taxa ao minerador do bloco que calculará o resultado do seu contrato inteligente. Embora isso possa mudar no futuro, no momento as taxas só podem ser pagas no Pirl e, portanto, todos os usuários de seus tokens precisam. Tokens em contas com saldo menor que a taxa ficam bloqueados até que o proprietário possa pagar a taxa necessária. Mas, em alguns casos de uso, você pode não querer que seus usuários pensem sobre Pirl, blockchain ou como obtê-lo, portanto, uma abordagem possível faria sua moeda reabastecer automaticamente o saldo do usuário assim que detectar que o saldo está perigosamente baixo.

Para fazer isso, primeiro você precisa criar uma variável que mantenha o valor limite e uma função para alterá-lo.

uint minBalanceForAccounts;

   função setMinBalance (uint minimumBalanceInFinney) onlyOwner {
        minBalanceForAccounts = minimumBalanceInFinney * 1 finney;
   }

 

Em seguida, adicione esta linha à função de transferência para que o remetente seja reembolsado:

  / * Enviar moedas * /
   transferência de função (endereço _to, uint256 _value) {
       …
       if (msg.sender.balance <minBalanceForAccounts)
           sell ((minBalanceForAccounts - msg.sender.balance) / sellPrice);
   }

 

Você também pode alterá-lo para que a taxa seja paga ao remetente pelo remetente:

  / * Enviar moedas * /
   transferência de função (endereço _to, uint256 _value) {
       …
       if (_to.balance            _to.send (sell ((minBalanceForAccounts - _to.balance) / sellPrice));
   }

Isso garantirá que nenhuma conta que receba o token tenha menos do que o Pirl necessário para pagar as taxas.

Prova de trabalho

Existem algumas maneiras de vincular seu suprimento de moedas a uma fórmula matemática. Uma das maneiras mais simples seria torná-la uma “mineração mesclada” com a Pirl, o que significa que qualquer pessoa que encontrar um bloco na Pirl também receberá uma recompensa da sua moeda, já que alguém chama a função de recompensa nesse bloco. Você pode fazer isso usando a palavra-chave especial coinbase que se refere ao mineiro que encontra o bloco.

função giveBlockReward () {
       balanceOf [block.coinbase] + = 1;
   }

Também é possível adicionar uma fórmula matemática, para que qualquer pessoa que possa fazer matemática possa ganhar uma recompensa. Neste próximo exemplo, você precisa calcular que a raiz cúbica do desafio atual recebe um ponto e o direito de definir o próximo desafio:

uint currentChallenge = 1; // Você consegue descobrir a raiz cúbica desse número?

   função bonusMathGeniuses (uint answerToCurrentReward, uint nextChallenge) {
       require (answerToCurrentReward ** 3 == currentChallenge); // Se a resposta estiver errada, não continue
       balanceOf de [msg.sender] + = 1; // Recompensa ao jogador
       currentChallenge = nextChallenge; // Defina o próximo desafio
   }

É claro que, embora seja difícil calcular raízes cúbicas para alguém, elas são muito fáceis com uma calculadora, portanto esse jogo pode ser facilmente quebrado por um computador. Além disso, como o último vencedor pode escolher o próximo desafio, eles podem escolher algo que eles conhecem e, portanto, não seria um jogo muito justo para outros jogadores. Existem tarefas fáceis para humanos, mas difíceis para computadores, mas geralmente são muito difíceis de codificar em scripts simples como esses. Em vez disso, um sistema mais justo deve ser muito difícil para um computador, mas não muito difícil de verificar. Um ótimo candidato seria criar um desafio de hash em que o desafiante precise gerar hashes a partir de vários números até encontrar um que seja menor que uma determinada dificuldade.

Esse processo foi proposto pela primeira vez por Adam Back em 1997 como Hashcash e depois foi implementado no Bitcoin por Satoshi Nakamoto como Prova de trabalho em 2008.

Se você gosta do Hashing como uma forma de emissão aleatória de moedas, ainda pode criar sua própria moeda baseada no Pirl que possui uma prova de emissão do trabalho:

bytes32 public currentChallenge; // A moeda começa com um desafio
   uint public timeOfLastProof; // Variável para acompanhar quando as recompensas foram concedidas
   uint dificuldade pública = 10 ** 32; // A dificuldade começa razoavelmente baixa

   função proofOfWork (uint nonce) {
       bytes8 n = bytes8 (sha3 (nonce, currentChallenge)); // Gere um hash aleatório com base na entrada
       require (n> = bytes8 (dificuldade)); // Verifique se está sob a dificuldade

       uint timeSinceLastProof = (agora - timeOfLastProof); // Calcular o tempo desde a última recompensa
       require (timeSinceLastProof> = 5 segundos); // Não é possível receber recompensas muito rapidamente
       balanceOf [msg.sender] + = timeSinceLastProof / 60 segundos; // A recompensa para o vencedor cresce a cada minuto

       dificuldade = dificuldade * 10 minutos / timeDesdeLastProof + 1; // Ajusta a dificuldade

       timeOfLastProof = agora; // Redefina o contador
       currentChallenge = sha3 (nonce, currentChallenge, block.blockhash (block.number - 1)); // Salve um hash que será usado como a próxima prova
   }

Altere também a função Construtor (aquela que tem o mesmo nome do contrato, que é chamado no primeiro upload) para adicionar esta linha, para que o ajuste da dificuldade não fique louco:

timeOfLastProof = agora;

Quando o contrato estiver on-line, selecione a função "Prova de trabalho", adicione seu número favorito no campo nonce e tente executá-lo. Se a janela de confirmação emitir um aviso vermelho dizendo "Os dados não podem ser executados", volte e escolha outro número até encontrar um que permita que a transação avance: esse processo é aleatório. Se você encontrar um, receberá 1 token por cada minuto que passou desde a última recompensa, e a dificuldade do desafio será ajustada para cima ou para baixo para atingir uma média de 10 minutos por recompensa.

Esse processo de tentar encontrar o número que lhe dará uma recompensa é o que é chamado de mineração: se a dificuldade aumentar, pode ser muito difícil encontrar um número da sorte, mas sempre será fácil verificar se você encontrou um.

Moeda melhorada

CÓDIGO COMPLETO DA MOEDA
Se você adicionar todas as opções avançadas, é assim que o código final deve ser:

solidez pragma ^ 0,4,16;

propriedade do contrato {
   endereço do proprietário público;

   função propriedade () pública {
       owner = msg.sender;
   }

   somente modificador Proprietário {
       requerer (msg.sender == proprietário);
       _;
   }

   função transferOwnership (address newOwner) onlyOwner public {
       owner = newOwner;
   }
}


solidez pragma ^ 0,4,16;

propriedade do contrato {
   endereço do proprietário público;

   função propriedade () pública {
       owner = msg.sender;
   }

   somente modificador Proprietário {
       requerer (msg.sender == proprietário);
       _;
   }

   função transferOwnership (address newOwner) onlyOwner public {
       owner = newOwner;
   }
}

interface tokenRecipient {função receiveApproval (endereço _from, uint256 _value, endereço _token, bytes _extraData) public; }

contrato TokenERC20 {
   // Variáveis públicas do token
   nome público da string;
   símbolo público de string;
   decimais públicos uint8 = 18;
   // 18 casas decimais é o padrão fortemente sugerido, evite alterá-lo
   uint256 public totalSupply;

   // Isso cria uma matriz com todos os saldos
   mapeamento (endereço => uint256) public balanceOf;
   mapeamento (endereço => mapeamento (endereço => uint256)) subsídio público;

   // Isso gera um evento público no blockchain que notificará os clientes
   Transferência de evento (endereço indexado de, endereço indexado para, valor uint256);

   // Isso notifica os clientes sobre a quantidade queimada
   queimadura de evento (endereço indexado de, valor uint256);

   /**
    * Função Constrctor
    *
    * Inicializa contrato com tokens de fornecimento inicial para o criador do contrato
    */
   função TokenERC20 (
       uint256 initialSupply,
       string tokenName,
       string tokenSymbol
   ) public {
       totalSupply = initialSupply * 10 ** uint256 (casas decimais); // Atualizar fornecimento total com o valor decimal
       balanceOf [msg.sender] = totalSupply; // Dê ao criador todos os tokens iniciais
       nome = tokenName; // Defina o nome para fins de exibição
       symbol = tokenSymbol; // Defina o símbolo para fins de exibição
   }

   /**
    * Transferência interna, somente pode ser chamada por este contrato
    */
   função _transferência (endereço _de, endereço_para, uint_value) interno {
       // Impede a transferência para o endereço 0x0. Use burn ()
       require (_to! = 0x0);
       // Verifique se o remetente tem o suficiente
       require (balanceOf [_from]> = _value);
       // Verifique se há estouros
       require (balanceOf [_to] + _value> balanceOf [_to]);
       // Salve isso para uma afirmação no futuro
       uint previousBalances = balanceOf [_de] + balanceOf [_to];
       // Subtrair do remetente
       balanceOf [_from] - = _value;
       // Adicione o mesmo ao destinatário
       balanceOf [_to] + = _value;
       Transferência (_de, _para, _valor);
       // Asserts são usados para usar a análise estática para encontrar erros no seu código. Eles nunca devem falhar
       assert (balanceOf [_de] + balanceOf [_to] == previousBalances);
   }

   /**
    * Tokens de transferência
    *
    * Envie tokens `_value` para` _to` da sua conta
    *
    * @param _to O endereço do destinatário
    * @param _valorize o valor a enviar
    */
   transferência de função (endereço _to, uint256 _value) public {
       _transferência (msg.sender, _to, _value);
   }

   /**
    * Transferir tokens de outro endereço
    *
    * Envie tokens `_value` para` _to` em nome de `_from`
    *
    * @param _from O endereço do remetente
    * @param _to O endereço do destinatário
    * @param _valorize o valor a enviar
    */
   a função transferFrom (endereço _de, endereço _to, uint256 _value) retorna retornos públicos (êxito de bool)
       require (_valor <= permissão [_de] [msg.sender]); // Verifique o subsídio
       subsídio [_from] [msg.sender] - = _value;
       _transferência (_from, _to, _value);
       return true;
   }

   /**
    * Definir subsídio para outro endereço
    *
    * Permite que o `_spender` gaste mais do que tokens do` _value` em seu nome
    *
    * @param _spender O endereço autorizado para gastar
    * @param _valorize o valor máximo que eles podem gastar
    */
   função aprovar (endereço _spender, uint256 _value) public
       retornos (sucesso booleano) {
       subsídio [msg.sender] [_ gastador] = _valor;
       return true;
   }

   /**
    * Defina a permissão para outro endereço e notifique
    *
    * Permite que o `_spender` gaste não mais que tokens do` _value` em seu nome e, em seguida, execute ping no contrato sobre ele
    *
    * @param _spender O endereço autorizado para gastar
    * @param _valorize o valor máximo que eles podem gastar
    * @param _extraData algumas informações extras para enviar ao contrato aprovado
    */
   função approveAndCall (endereço _spender, uint256 _value, bytes _extraData)
       público
       retornos (sucesso booleano) {
       gastador tokenRecipient = tokenRecipient (_spender);
       if (aprovar (_spender, _value)) {
           spender.receiveApproval (msg.sender, _value, this, _extraData);
           return true;
       }
   }

   /**
    * Destrua fichas
    *
    * Remova os tokens `_value` do sistema irreversivelmente
    *
    * @param _value a quantidade de dinheiro para queimar
    */
   função burn (uint256 _value) retornos públicos (sucesso bool) {
       require (balanceOf [msg.sender]> = _value); // Verifique se o remetente tem o suficiente
       balanceOf [msg.sender] - = _value; // Subtrair do remetente
       totalSupply - = _value; // Atualiza totalSupply
       Gravar (msg.sender, _value);
       return true;
   }

   /**
    * Destrua tokens de outra conta
    *
    * Remova os tokens `_value` do sistema irreversivelmente em nome de` _from`.
    *
    * @param _do endereço do remetente
    * @param _value a quantidade de dinheiro para queimar
    */
   função burnFrom (endereço _from, uint256 _value) retornos públicos (sucesso do bool) {
       require (balanceOf [_from]> = _value); // Verifique se o saldo desejado é suficiente
       require (_valor <= permissão [_de] [msg.sender]); // Verifique o subsídio
       balanceOf [_from] - = _value; // Subtrair do saldo desejado
       subsídio [_from] [msg.sender] - = _value; // Subtrair do subsídio do remetente
       totalSupply - = _value; // Atualizar totalSupply
       Gravar (_de, _valor);
       return true;
   }
}

/******************************************/
/ * TOKEN AVANÇADO COMEÇA AQUI * /
/******************************************/

pertence ao contrato MyAdvancedToken, TokenERC20 {

   uint256 sellPrice público;
   uint256 buyPrice público;

   mapeamento (endereço => bool) public frozenAccount;

   / * Isso gera um evento público no blockchain que notificará os clientes * /
   evento FrozenFunds (endereço de destino, bool congelado);

   / * Inicializa contrato com tokens de fornecimento inicial para o criador do contrato * /
   função MyAdvancedToken (
       uint256 initialSupply,
       string tokenName,
       string tokenSymbol
   ) TokenERC20 (initialSupply, tokenName, tokenSymbol) public {}

   / * Transferência interna, somente pode ser chamada por este contrato * /
   função _transferência (endereço _de, endereço_para, uint_value) interno {
       require (_to! = 0x0); // Impede a transferência para o endereço 0x0. Use burn ()
       require (balanceOf [_from]> = _value); // Verifique se o remetente tem o suficiente
       require (balanceOf [_to] + _value> balanceOf [_to]); // Verifique se há estouros
       require (! frozenAccount [_from]); // Verifique se o remetente está congelado
       require (! frozenAccount [_to]); // Verifique se o destinatário está congelado
       balanceOf [_from] - = _value; // Subtrair do remetente
       balanceOf [_to] + = _value; // Adicione o mesmo ao destinatário
       Transferência (_de, _para, _valor);
   }

   /// @notice Crie tokens `mintedAmount` e envie-o para` target`
   /// @param target Endereço para receber os tokens
   /// @param mintedMonte a quantidade de tokens que receberá
   função mintToken (destino do endereço, uint256 mintedAmount) onlyOwner public {
       balanceOf [target] + = mintedAmount;
       totalSupply + = mintedAmount;
       Transfer (0, este, mintedAmount);
       Transfer (this, target, mintedAmount);
   }

   /// @notice `congelar? Prevenir | Permitir que o `` target` envie e receba tokens
   /// @param target Endereço a ser congelado
   /// @param freeze para congelar ou não
   função freezeAccount (destino de endereço, congelamento de bool) apenasOperner public {
       frozenAccount [target] = congelar;
       FrozenFunds (alvo, congelar);
   }

   /// @notice Permite que os usuários comprem tokens para o `newBuyPrice` Pirl e venda tokens para o` newSellPrice`
   /// @param newSellPrice Preço que os usuários podem vender ao contrato
   /// @param newBuyPrice Preço que os usuários podem comprar do contrato
   função setPrices (uint256 newSellPrice, uint256 newBuyPrice) onlyOwner public {
       sellPrice = newSellPrice;
       buyPrice = newBuyPrice;
   }

   /// @notice Compre tokens por contrato enviando Pirl
   função buy () público a pagar {
       uint amount = msg.value / buyPrice; // calcula a quantidade
       _transferência (this, msg.sender, amount); // faz as transferências
   }

   /// @notice Venda tokens de `valor` a contrato
   /// @param quantidade de tokens a serem vendidos
   função vender (valor uint256) público {
       require (this.balance> = amount * sellPrice); // verifica se o contrato tem Pirl suficiente para comprar
       _transfer (msg.sender, este valor); // faz as transferências
       msg.sender.transfer (quantidade * sellPrice); // envia Pirl para o vendedor. É importante fazer isso por último para evitar ataques de recursão
   }
}

Implantando

Role para baixo e você verá um custo estimado para implantação. Se desejar, você pode alterar o controle deslizante para definir uma taxa menor, mas se o preço estiver muito abaixo da taxa média de mercado, sua transação poderá demorar mais para ser recuperada. Clique em Implementar e digite sua senha. Após alguns segundos, você será redirecionado para o painel e, em Últimas transações, verá uma linha dizendo "criando contrato". Aguarde alguns segundos para alguém escolher sua transação e, em seguida, você verá um retângulo azul lento representando quantos outros nós viram sua transação e os confirmaram. Quanto mais confirmações você tiver, maior a garantia de que seu código foi implantado.

Clique no link na página Admin e você será levado ao painel do banco central mais simples do mundo, onde poderá fazer o que quiser com sua moeda recém-criada.

No lado esquerdo, em Ler do contrato, você tem todas as opções e funções que podem ser usadas para ler informações do contrato gratuitamente. Se o seu token tiver um proprietário, ele exibirá seu endereço aqui. Copie esse endereço e cole-o no Saldo de e ele mostrará o saldo de qualquer conta (o saldo também é mostrado automaticamente em qualquer página da conta que tenha tokens).

No lado direito, em Write to Contract, você verá todas as funções que você pode usar para alterar ou alterar o blockchain de qualquer forma. Isso custará gás. Se você criou um contrato que permite cunhar novas moedas, você deve ter uma função chamada “Mint Token”. Selecione.

Selecione o endereço em que essas novas moedas serão criadas e, em seguida, o valor (se você tiver casas decimais definidas como 2, adicione 2 zeros após o valor, para criar a quantidade correta). Em Executar, selecione a conta que foi definida como proprietária, deixe o valor Pirl em zero e pressione executar.

Após algumas confirmações, o saldo do destinatário será atualizado para refletir o novo valor. Mas a carteira do destinatário pode não mostrá-la automaticamente: para conhecer os tokens personalizados, a carteira deve adicioná-los manualmente a uma lista de observação. Copie seu endereço de token (na página de administrador, pressione endereço de cópia) e envie-o ao seu destinatário. Se ainda não o fizeram, eles devem ir para a guia Contratos, pressione Observar Token e adicione o endereço lá. Nome, símbolos e valores decimais exibidos podem ser personalizados pelo usuário final, especialmente se eles tiverem outros tokens com nome semelhante (ou o mesmo). O ícone principal não é alterável e os usuários devem prestar atenção a eles ao enviar e receber tokens para garantir que estejam lidando com a oferta real e não com algum token de cópia.

Usando sua moeda

Depois de implantar seus tokens, eles serão adicionados à sua lista de tokens monitorados e o saldo total será mostrado em sua conta. Para enviar tokens, basta ir para a guia Enviar e selecionar uma conta que contenha tokens. Os tokens que a conta possui serão listados logo abaixo de Pirl. Selecione-os e digite a quantidade de tokens que deseja enviar.

Se você deseja adicionar o token de outra pessoa, basta acessar a guia Contracts e clicar em Watch token. Por exemplo, para adicionar o token Pirl Vortex à sua lista de observação, basta adicionar o endereço 0x0489A975393A1cD0330740040141D702C35180cb

O que agora?

Você acabou de aprender como usar o Pirl para emitir um token, que pode representar o que você quiser. Mas o que você pode fazer com os tokens? Você pode usar, por exemplo, os tokens para representar uma participação em uma empresa ou pode usar um comitê central para votar quando emitir novas moedas para controlar a inflação. Você também pode usá-los para arrecadar dinheiro por uma causa, através de uma venda coletiva. O que você construirá a seguir?

Material de referência: ethereum.org


Assine a nossa newsletter

Receba atualizações e aprenda com os melhores

Mais para explorar

pt_PTPortuguês
en_USEnglish fr_FRFrançais nl_NLNederlands tr_TRTürkçe es_ESEspañol ru_RUРусский ko_KR한국어 zh_CN简体中文 hi_INहिन्दी pt_PTPortuguês