// SPDX-License-Identifier: MIT
// Faucet contract
// Para no tener que dar uno a uno los tokens, tenemos este contrato de "faucet"
//
// ¿Qué es un Faucet? es una forma muy común de distribuir nuevos Tokens.
//
// Consiste en un contrato (que _suele_ tener una página web asociada)
// que distribuye los tokens a quien los solicita.
// Así el que crea el token no tiene que pagar por distribuir los tokens,
// y sólo los interesados 'pagan' a los mineros de la red por el coste de la transacción.
pragma solidity >=0.7.0;
interface iERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
contract Faucet {
// Vamos a hacer un Faucet_muy_ sencillo:
// - No podemos saber de qué país es cada dirección así que:
// - Cada dirección de Ethereum debe poder participar en el Airdrop.
// - A cada dirección que lo solicite le transferimos 1000 unidades del tocken.
// - Sólo se puede participar en el airdrop una sola vez.
// - Si hay algún problema, la transacción _falla_
// Primero necesitamos la dirección del contrato del Token
address public immutable token;
address public _root;
// También necesitamos una lista de direcciones.
// Un "mapping" direccion -> a un 0 o 1 bastaría...
// .. pero ese tipo de dato no existe en este lenguaje)
// .. así que hay que construirlo a mano.
// este es un bitmap (hay formas más eficientes de hacerlo, pero esta nos sirve)
mapping (address => uint256) public claimed;
uint256 public immutable claimAmount;
event Claimed(address _by, address _to, uint256 _value);
// Cantidad solicitada por cada dirección
function ClaimedAmount(address index) public view returns (uint256) {
return claimed[index];
}
function _setClaimed(address index) private {
require(claimed[index] == 0);
claimed[index] += claimAmount; // No puede desbordar
}
// Esta funcion es la que permite reclamar los tokens.
// No hace falta ser el dueño de la dirección para solicitarlo.
// De todos modos... ¿quién puede querer enviar tokens a otro?
// Hmm.. ¿puede haber alguna implicación legal de eso?
function Claim(address index) public returns (uint256) {
// hmm... ¿dejamos que lo haga un smart contract?
require(msg.sender == tx.origin, "Only humans");
require(ClaimedAmount(index) == 0 && index != address(0));
_setClaimed(index);
// Hacemos la transferencia y revertimos operacion si da algún error
require(iERC20(token).transfer(index, claimAmount), "Airdrop: error transferencia");
emit Claimed(msg.sender, index, claimAmount);
return claimAmount;
}
// Cuando acabe el tiempo del airdrop se pueden recuperar
// a menos que haya alguna logica en el token que no lo permita...
// Se permite que Recovertokens lo llame un contrato por motivos fiscales.
// Si se llama a Recovertokens desde una direccion "normal" de Ethereum
// hacienda nos puede obligar a tributar por tener los tokens durante unos segundos.
function Recovertokens() public returns (bool) {
require(tx.origin == _root || msg.sender == _root , "tx.origin is not root");
uint256 allbalance = iERC20(token).balanceOf(address(this));
return iERC20(token).transfer(_root, allbalance);
}
// SetRoot permite cambiar el superusuario del contrato
// es decir, la dirección a la que se permite reclamar
// todos los tokens. Se puede llamar desde un contrato!
event NewRootEvent(address);
function SetRoot(address newroot) public {
require(msg.sender == _root); // sender
address oldroot = _root;
emit NewRootEvent(newroot);
_root = newroot;
}
// Necesitamos construir el contrato (instanciar)
// Constructor
constructor(address tokenaddr, uint256 claim_by_addr) {
token = tokenaddr;
claimAmount = claim_by_addr;
_root = tx.origin; // La persona (humana?) que crea el contrato.
}
}
{
"compilationTarget": {
"Faucet.sol": "Faucet"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"tokenaddr","type":"address"},{"internalType":"uint256","name":"claim_by_addr","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_by","type":"address"},{"indexed":false,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"","type":"address"}],"name":"NewRootEvent","type":"event"},{"inputs":[{"internalType":"address","name":"index","type":"address"}],"name":"Claim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"index","type":"address"}],"name":"ClaimedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"Recovertokens","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newroot","type":"address"}],"name":"SetRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"_root","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"claimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]