WEB3DEV

Cover image for ERC20EXP
Fatima Lima
Fatima Lima

Posted on

ERC20EXP

Uma nova abordagem do token ERC20 com tempo limitado.

Introdução

O padrão ERC20 tornou-se o padrão mais amplamente utilizado para a implementação de tokens na blockchain Ethereum. No entanto, ele tem uma série de limitações que surgiram à medida que o uso de tokens ERC20 cresceu. Uma das principais limitações do padrão ERC20 é que ele não oferece suporte a casos de uso complexos, como o modelo UTXO, que pode empacotar dados em cada conjunto UTXO. No modelo UTXO, cada UTXO pode ser usado para armazenar dados adicionais juntamente com o valor do token, permitindo que aplicativos mais complexos e ricos em recursos sejam criados com base no padrão de token.

Por exemplo, considere um aplicativo que exija que os usuários anexem uma mensagem ou um aviso a cada transferência de token. Com o padrão ERC20, isso não seria possível sem recorrer a um armazenamento externo ou acrescentar funcionalidades adicionais ao contrato de token. No entanto, com o modelo UTXO, a mensagem ou o aviso pode ser armazenado diretamente junto com o valor do token em cada UTXO, permitindo uma comunicação perfeita e eficiente entre os usuários.

Outro exemplo de como o modelo UTXO pode ser usado para empacotar dados adicionais está na implementação de tokens não fungíveis (NFTs). Os NFTs são um tipo de token que representa ativos exclusivos e indivisíveis, como obras de arte digitais ou itens colecionáveis. Com o padrão ERC20, cada NFT exigiria um contrato e um ID de token separados. No entanto, com o modelo UTXO, cada NFT pode ser representado como um único UTXO, com os dados exclusivos do ativo armazenados junto com o valor do token.

De modo geral, a capacidade de incluir dados adicionais em cada conjunto UTXO é uma das principais vantagens do modelo UTXO em relação ao padrão ERC20. Ao incorporar essa funcionalidade ao padrão ERC20, talvez seja possível criar aplicativos baseados em tokens mais versáteis e poderosos, que possam suportar uma ampla gama de casos de uso.

Abordagem

ERC20 com uma data de validade, usando a abordagem UTXO (Unspent Transaction Output, output de transação não utilizada) é um novo método de implementação de tokens na blockchain Ethereum. Essa abordagem utiliza o mesmo conceito dos UTXOs no Bitcoin, em que a titularidade de um token é representada como um output de uma transação não utilizada, o que pode ser útil para rastrear a movimentação de tokens e implementar casos de uso mais complexos, onde um UTXO exclusivo representa cada token com uma data de validade. Quando ocorre uma transferência, o detentor do token deve selecionar um UTXO não expirado e, para obter a melhor experiência do usuário, a função de transferência deve selecionar automaticamente ou sugerir o gasto do UTXO não expirado mais próximo para a transferência.

Isso pode ser confuso e complicado para os usuários, especialmente aqueles que não estão familiarizados com os aspectos técnicos de blockchains e criptomoedas. Além disso, como cada UTXO representa uma quantidade específica de tokens, pode ser difícil para os usuários calcular com precisão a quantidade de tokens necessária para fazer um pagamento ou transferência.

Além disso, o uso de UTXOs pode dificultar a implementação de determinados recursos, como a queima de tokens ou a criação de novos tokens. No padrão ERC20, essas ações podem ser facilmente realizadas ajustando-se o suprimento total do token. No entanto, no modelo UTXO, essas ações exigem a criação ou destruição de UTXOs específicos, o que pode ser mais complexo e demorado.

Apesar desses desafios, o modelo UTXO continua sendo uma ferramenta poderosa para a criação de aplicativos baseados em tokens mais complexos e ricos em recursos. À medida que a tecnologia blockchain continuar a evoluir e a se tornar mais fácil de usar, é possível que o modelo UTXO se torne mais amplamente adotado e acessível a um público mais amplo.

https://github.com/olegfomenko/utxo-evm/blob/develop/docs/utxo-erc20.md

Passo a passo

O ERC20 com uma data de validade, usando a abordagem UTXO (Unspent Transaction Output) é um novo método de implementação de tokens na blockchain Ethereum. Essa abordagem utiliza o mesmo conceito dos UTXOs no Bitcoin, em que a titularidade de um token é representada como um output de uma transação não utilizada, o que pode ser útil para rastrear um conjunto de tokens.

Image description

Interface ERC20UTXO

No código acima, a estrutura UTXO pode conter um campo de dados extra para que você possa colocar os dados em cada UTXO; por exemplo, você pode gastar e chamar uma função ao mesmo tempo.

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.14;

import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "./IERC20UTXO.sol";

contract ERC20UTXO is Context, IERC20UTXO {
   using ECDSA for bytes32;


   UTXO[] private _utxos;

   mapping(address => uint256) private _balances;

   uint256 private _totalSupply;

   string private _name;
   string private _symbol;


   constructor(string memory name_, string memory symbol_) {
       _name = name_;
       _symbol = symbol_;
   }

   function name() public view virtual override returns (string memory){
       return _name;
   }

   function symbol() public view virtual override returns (string memory){
       return _symbol;
   }

   function decimals() public view virtual override returns (uint8){
       return 18;
   }

   function totalSupply() public view virtual override returns (uint256) {
       return _totalSupply;
   }

   function balanceOf(address account) public view virtual override returns (uint256) {
       return _balances[account];
   }

   function utxoLength() public view returns (uint256) {
       return _utxos.length;
   }

   function utxo(uint256 id) public override view returns (UTXO memory) {
       require(id < _utxos.length, "ERC20UTXO: id out of bound");
       return _utxos[id];
   }

   function transfer(uint256 amount, TxInput memory input, TxOutput memory output) public virtual {
       require(output.amount <= amount, "ERC20UTXO: transfer amount exceeds utxo amount");
       address creator = _msgSender();
       bytes storage data = _utxos[input.id].data;
       if (output.amount < amount) {
           uint256 value = amount - output.amount;
           _spend(input, creator);
           unchecked {
               _balances[creator] -= value;  
               _balances[output.owner] += amount;
           }
           _create(output, creator, data);
           _create(TxOutput(value, creator), creator, data);
       } else {
           _spend(input,creator);
           unchecked {
               _balances[creator] -= amount;  
               _balances[output.owner] += amount;
           }
           _create(output, creator, data);
       }
   }

   function _mint(uint256 amount, TxOutput memory output, bytes memory data) internal virtual {
       require(output.amount == amount, "ERC20UTXO: invalid amounts");
       _totalSupply += amount;
       unchecked {
           _balances[output.owner] += amount;
       }
       _create(output, address(0), data);
   }


   function _create(TxOutput memory output, address creator, bytes memory data) internal virtual {
       require(output.owner != address(0),"ERC20UTXO: create utxo output to zero address");
       uint256 id = utxoLength()+1;
       UTXO memory utxo = UTXO(output.amount, output.owner, data, false);


       _beforeCreate(output.owner,utxo);

       _utxos.push(utxo);
       emit UTXOCreated(id, creator);

       _afterCreate(output.owner,utxo);
   }

   function _spend(TxInput memory inputs, address spender) internal virtual {
       require(inputs.id < _utxos.length, "ERC20UTXO: utxo id out of bound");
       UTXO memory utxo = _utxos[inputs.id];
       require(!utxo.spent, "ERC20UTXO: utxo has been spent");

       _beforeSpend(utxo.owner,utxo);

       require(
           utxo.owner == keccak256(abi.encodePacked(inputs.id))
                         .toEthSignedMessageHash()
                         .recover(inputs.signature),
                         "ERC20UTXO: invalid signature");
       _utxos[inputs.id].spent = true;
       emit UTXOSpent(inputs.id, spender);

       _afterSpend(utxo.owner,utxo);
   }

   function _beforeCreate(address creator, UTXO memory utxo) internal virtual {}

   function _afterCreate(address creator, UTXO memory utxo) internal virtual {}

   function _beforeSpend(address spender, UTXO memory utxo) internal virtual {}

   function _afterSpend(address spender, UTXO memory utxo) internal virtual {}
}
Enter fullscreen mode Exit fullscreen mode
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.14;

import "@openzeppelin/contracts/access/Ownable.sol";
import "./ERC20UTXO.sol";

abstract contract ERC20UTXOExpirable is ERC20UTXO, Ownable {

   uint64 private immutable _period;

   constructor(uint64 period_) {
       _period = period_ ;
   }

   function mint(uint256 amount, TxOutput memory outputs) public onlyOwner {
       _mint(amount, outputs, abi.encode(block.timestamp + _period));
   }

   function _beforeSpend(address spender, UTXO memory utxo) internal override {
       uint256 expireDate = abi.decode(utxo.data, (uint256));
       require(block.timestamp < expireDate,"UTXO has been expired");
   }
}
Enter fullscreen mode Exit fullscreen mode

O contrato ERC20UTXOExpirable herda dos contratos ERC20UTXO e Ownable fornecidos pelo OpenZeppelin. O ERC20UTXO implementa o padrão básico de token ERC20 para UTXOs, enquanto o Ownable fornece um modificador para verificar se uma transação é executada pelo proprietário do contrato.

O contrato introduz uma nova variável de estado chamada _period que é uma variável imutável definida durante a criação do contrato. A variável _perioddefine a duração em segundos para a qual o UTXO será válido.

A função mint permite que o proprietário do contrato crie novos UTXOs com uma data de validade. Ele leva um parâmetro amount, que especifica o número de tokens a ser criado e um parâmetro TxOutput, que especifica o output da transação. O output é codificado com a data de validade, adicionando _period ao timestamp (registro de data e hora) do bloco atual. A função _mint do contrato ERC20UTXO é então chamada com um output codificado.

A função hook _beforeSpend é cancelada para verificar se o UTXO expirou ou não. Ela decodifica os dados do UTXO para recuperar a data de validade, compara-a com o timestamp do bloco atual e lança um erro se a data de validade estiver expirada.

Em geral, o contrato ERC20UTXOExpirable adiciona um recurso valioso ao contrato ERC20UTXO, permitindo que UTXOs expirem depois de um certo tempo, fornecendo uma segurança adicional aos usuários do token.

Teste

Image description

Desculpe-me se meu código é ruim

O primeiro teste, “Spent UTXO”, transfere 10,000 tokens do primeiro para o segundo usuário.

O segundo teste, "Spend expire utxo", cunha 10.000 tokens para a conta do primeiro usuário, cria um UTXO com esse valor e com a hora atual e tenta transferir 10.000 tokens do UTXO para a conta do segundo usuário. Entretanto, antes de fazer a transferência, o teste aumenta o tempo de validade do UTXO, decodifica o tempo de validade dos dados do UTXO e usa o tempo de validade decodificado para verificar se o UTXO expirou ou não. Se o UTXO tiver expirado, a transferência deverá ser revertida com a mensagem "UTXO has been expired" (UTXO expirou).

Image description

Resultado do teste

https://github.com/MASDXI/ERC20-Expirable

Isenção de responsabilidade: este código é um protótipo e não se destina ao uso em ambientes de produção. Use por sua própria conta e risco.

Conclusão

Prós:

  • Oferece uma abordagem mais eficiente e flexível para lidar com transferências de tokens, pois cada token é representado por um UTXO separado com sua data de validade.
  • Permite o fácil rastreamento de titularidade de tokens e datas de validade, melhorando a transparência e a segurança para os detentores de tokens.
  • Permite a criação de aplicativos mais complexos baseados em tokens, como sistemas de recompensa sensíveis ao tempo ou plataformas de empréstimo com termos de empréstimo baseados na expiração.

Contras:

  • O UTXO não exige apenas operações lógicas pesadas que afetam diretamente o desempenho e aumenta o gas utilizado durante a execução.
  • (Espera-se que esse tipo diferente de EVM fornecido pela camada 2 possa reduzir o custo de execução).
  • Exige mais esforço e compreensão devido à complexidade, pois é possível que introduza novos bugs ou vulnerabilidades.

Esse recurso útil me inspirou a escrever um prompt para o ChatGPT e gerar este artigo excluindo o código de contrato inteligente.

Esse artigo foi escrito por 0x91d9 e traduzido por Fátima Lima. O original pode ser lido aqui.

Latest comments (0)