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.
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 {}
}
// 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");
}
}
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 _period
define 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
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).
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.
Top comments (0)